Browse Source

Added pitch detection via Aubio

It isn't real-time yet, but my simple calibration tests due seem to be
promising.
pitch-detection
Macoy Madson 9 months ago
parent
commit
a424ecb153
  1. 3
      .gitmodules
  2. 13
      Build_Debug.sh
  3. 1
      Dependencies/aubio
  4. 125
      src/Aubio.cake
  5. 74
      test/src/VocalGame.cake

3
.gitmodules

@ -19,3 +19,6 @@
[submodule "Dependencies/SDL"]
path = Dependencies/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "Dependencies/aubio"]
path = Dependencies/aubio
url = https://github.com/aubio/aubio

13
Build_Debug.sh

@ -10,19 +10,20 @@ echo "\n\nOgre\n\n"
./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake test/src/OgreApp.cake || exit $?
echo "\n\nSDL Ogre\n\n"
./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake test/src/SDLOgreApp.cake || exit $?
echo "\n\nAuto Test (Math only)\n\n"
./Dependencies/cakelisp/bin/cakelisp --execute test/src/Config_Linux.cake src/AutoTest.cake src/Math.cake || exit $?
# echo "\n\nAuto Test\n\n"
# ./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake src/AutoTest.cake src/SDL.cake src/Tracy.cake src/Math.cake || exit $?
# echo "\n\nAuto Test (Math only)\n\n"
# ./Dependencies/cakelisp/bin/cakelisp --execute test/src/Config_Linux.cake src/AutoTest.cake src/Math.cake || exit $?
echo "\n\nAuto Test\n\n"
./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake src/AutoTest.cake src/SDL.cake src/Math.cake src/Aubio.cake || exit $?
# ./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake src/AutoTest.cake src/SDL.cake src/Tracy.cake src/Math.cake src/Aubio.cake || exit $?
echo "\n\nVocal Game (hot reload)\n\n"
./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake test/src/MakeHotReload.cake test/src/VocalGame.cake || exit $?
./Dependencies/cakelisp/bin/cakelisp --verbose-processes test/src/Config_Linux.cake test/src/MakeHotReload.cake test/src/VocalGame.cake || exit $?
echo "\n\nLoader\n\n"
./Dependencies/cakelisp/bin/cakelisp test/src/Config_Linux.cake test/src/Loader.cake || exit $?
echo "\n\nVocal Game (no reload)\n\n"
./Dependencies/cakelisp/bin/cakelisp \
./Dependencies/cakelisp/bin/cakelisp --execute \
test/src/Config_Linux.cake test/src/NoHotReload.cake test/src/VocalGame.cake || exit $?
# TESTING

1
Dependencies/aubio

@ -0,0 +1 @@
Subproject commit d3325ba1d97167dc17307cd116000c46c7aba1ec

125
src/Aubio.cake

@ -0,0 +1,125 @@
;; Aubio is a library with various audio and music analysis tools
(c-import "stdio.h"
"aubio.h"
"mathutils.h"
&with-decls "<stdint.h>")
(defun audio-detect-pitch (buffer (* (const uint8_t)) buffer-size int
sample-rate int &return float)
(unless (aubio_is_power_of_two buffer-size)
(var nearest-power-of-two int (/ (aubio_next_power_of_two buffer-size) 2))
(set buffer-size nearest-power-of-two))
(var hop-size uint_t buffer-size) ;; TODO: What is this?
(var pitch-detection (* aubio_pitch_t)
(new_aubio_pitch "yinfft" buffer-size hop-size sample-rate))
(var buffer-float (* fvec_t) (new_fvec buffer-size))
(scope
(var i int 0)
(while (< i buffer-size)
(var converted-sample smpl_t (/ (- (type-cast (at i buffer) float) 127) 256))
(fvec_set_sample buffer-float converted-sample i)
(incr i)))
(var detected-pitch-buffer (* fvec_t) (new_fvec buffer-size))
(aubio_pitch_do pitch-detection buffer-float detected-pitch-buffer)
;; (printf "Buffer:\n")
;; (fvec_print buffer-float)
;; (printf "Pitch buffer:\n")
;; (fvec_print detected-pitch-buffer)
(var detected-pitch float (fvec_get_sample detected-pitch-buffer 0))
(del_fvec buffer-float)
(del_fvec detected-pitch-buffer)
(del_aubio_pitch pitch-detection)
(return detected-pitch))
;;
;; Test
;;
(defun test--aubio (&return int)
(printf "Hello, aubio!\n")
(var buffer-size uint_t 4096)
(var hop-size uint_t 4096) ;; TODO: What is this?
(var sample-rate uint_t 44100)
(var pitch-detection (* aubio_pitch_t)
(new_aubio_pitch "yinfft" buffer-size hop-size sample-rate))
(var buffer (* fvec_t) (new_fvec buffer-size))
(var sound-source (* aubio_source_t)
(new_aubio_source "Tone_440.wav" sample-rate hop-size))
(var frames-read uint_t 0)
(aubio_source_do sound-source buffer (addr frames-read))
(printf "Read %d frames\n" frames-read)
(var detected-pitch (* fvec_t) (new_fvec buffer-size))
(aubio_pitch_do pitch-detection buffer detected-pitch)
(printf "Buffer:\n")
(fvec_print buffer)
(printf "Pitch:\n")
(fvec_print detected-pitch)
(del_fvec buffer)
(del_fvec detected-pitch)
(del_aubio_pitch pitch-detection)
(return 0))
;;
;; Building
;;
(set-cakelisp-option cakelisp-src-dir "Dependencies/cakelisp/src")
(add-cakelisp-search-directory "Dependencies/cakelisp/runtime")
(import &comptime-only "Macros.cake" "BuildTools.cake")
(defun-comptime build-aubio (manager (& ModuleManager) module (* Module) &return bool)
(comptime-cond
('Windows
(comptime-error "Aubio build needs to be ported to Windows."))
('Unix
;; Already built?
;; We could enhance this by checking for modifications, but that's pretty rare
(when (fileExists "Dependencies/aubio/build/src/libaubio.so")
(return true))
(Log "Aubio: Building via Make\n")
(run-process-sequential-or
("make" :in-directory "Dependencies/aubio")
(Log "failed at Aubio make. This tool requires Makefile support.\n")
(return false))
;; One final to check to ensure everything's good to go
(unless (fileExists "Dependencies/aubio/build/src/libaubio.so")
(Log
"error: Aubio build sequence completed, but files are not where expected. Is there an issue
with the configuration?\nFiles are expected in:\n\tDependencies/aubio/build\n")
(return false)))
(true
(comptime-error "need to define platform, e.g. (comptime-define-symbol 'Unix)")))
(Log "Aubio: Successfully built\n")
(return true))
(add-compile-time-hook-module pre-build build-aubio)
(add-c-search-directory-module "Dependencies/aubio/src")
(add-library-search-directory "Dependencies/aubio/build/src")
(add-library-dependency "aubio")
;; TODO: Relative path is going to break for sure
(add-library-runtime-search-directory "../Dependencies/aubio/build/src")
(set-cakelisp-option executable-output "test/aubioTest")

74
test/src/VocalGame.cake

@ -1,6 +1,8 @@
(set-cakelisp-option executable-output "test/VocalGame")
(import &comptime-only "Options.cake" "Macros.cake" "GamelibMacros.cake")
(import "Ogre.cake" "OgreInitialize.cake" "SDL.cake" "Math.cake" "Tracy.cake")
(import "Ogre.cake" "OgreInitialize.cake" "SDL.cake" "Math.cake" "Tracy.cake" "Aubio.cake")
;; TODO: Should this happen automatically, because import automatically adds current working dir?
;; Should it add working dir?
@ -383,6 +385,8 @@
(TracyCZoneEnd startup-zone)
(var pitch-hz float 0.f)
;; Main loop
(while (not exit-reason)
(FrameMarkNamed "Frame")
@ -484,36 +488,43 @@
(incr i)))
(free (field audio-conversion-settings buf)))
;; Normalize audio
;; Note: peak in both low and high, never greater than range / 2
(var highest-peak Uint8 0)
(var i int 0)
(while (< i audio-input-buffer-size)
(var current-value int (- (type-cast (at i audio-input-buffer) int) 127))
(when (< highest-peak (abs current-value))
(set highest-peak (abs current-value)))
(incr i))
(when highest-peak
;; Get within 90% of the max, to avoid clipping
(var desired-high-peak (const int) (* (/ 256 2) 0.9f))
(var scaling-factor float (/ desired-high-peak (type-cast highest-peak float)))
(printf "Scaling recording %.4f because desired = %d and highest was %d\n"
scaling-factor desired-high-peak highest-peak)
(var scaling-limit float 6.f)
;; If there's only silence, you don't want to go crazy with boosting it
(when (> scaling-factor scaling-limit)
(printf "scaling limited to %f\n" scaling-limit)
(set scaling-factor scaling-limit))
(when scaling-factor
(set i 0)
(while (< i audio-input-buffer-size)
(var current-value int (- (type-cast (at i audio-input-buffer) int) 127))
(set (at i audio-input-buffer) (type-cast
;; Map back to 0-255
(+ 127.f
(* current-value scaling-factor))
Uint8))
(incr i)))))
(scope ;; Normalize audio
;; Note: peak in both low and high, never greater than range / 2
(var highest-peak Uint8 0)
(var i int 0)
(while (< i audio-input-buffer-size)
(var current-value int (- (type-cast (at i audio-input-buffer) int) 127))
(when (< highest-peak (abs current-value))
(set highest-peak (abs current-value)))
(incr i))
(when highest-peak
;; Get within 90% of the max, to avoid clipping
(var desired-high-peak (const int) (* (/ 256 2) 0.9f))
(var scaling-factor float (/ desired-high-peak (type-cast highest-peak float)))
(printf "Scaling recording %.4f because desired = %d and highest was %d\n"
scaling-factor desired-high-peak highest-peak)
(var scaling-limit float 6.f)
;; If there's only silence, you don't want to go crazy with boosting it
(when (> scaling-factor scaling-limit)
(printf "scaling limited to %f\n" scaling-limit)
(set scaling-factor scaling-limit))
(when scaling-factor
(set i 0)
(while (< i audio-input-buffer-size)
(var current-value int (- (type-cast (at i audio-input-buffer) int) 127))
(set (at i audio-input-buffer) (type-cast
;; Map back to 0-255
(+ 127.f
(* current-value scaling-factor))
Uint8))
(incr i)))))
(scope-timed "Detect pitch"
(set pitch-hz
(audio-detect-pitch audio-input-buffer audio-input-buffer-size (field g-output-device-spec freq)))
(printf "Pitch (hz): %f\n" pitch-hz)))
(when (!= 0 pitch-hz)
(printf "Pitch (hz): %f\n" pitch-hz))
(var current-counter-ticks Uint64 (SDL_GetPerformanceCounter))
(var frame-diff-ticks Uint64 (- current-counter-ticks last-frame-perf-count))
@ -697,7 +708,6 @@ install ImageMagick. See https://www.imagemagick.org/script/download.php\n")
;; Building
;;
(set-cakelisp-option executable-output "test/VocalGame")
;; TODO: Somehow inherit this from SDL.cake?
(module-use-sdl-build-options)

Loading…
Cancel
Save