Browse Source

Renamed globals and reformatted

pitch-detection
Macoy Madson 9 months ago
parent
commit
dd986a0e84
  1. 1
      src/Math.cake
  2. 239
      test/src/VocalGame.cake

1
src/Math.cake

@ -34,7 +34,6 @@
;; TODO: Linear interpolation
;; TODO: abs(), min(), max(), clamp()
;; TODO: PI constant
;;
;; Vectors

239
test/src/VocalGame.cake

@ -12,15 +12,15 @@
"<math.h>" "<stdio.h>" "<string.h>")
;; These are read and written to from different threads (currently, without locking)
(var audio-is-initialized bool false)
(var audio-is-recording bool false)
(var audio-input-buffer (* Uint8) null)
(var audio-input-buffer-size int 0)
(var audio-input-write-head int 0)
(var audio-input-read-head int 0)
(var g-audio-is-g-initialized bool false)
(var g-audio-is-recording bool false)
(var g-audio-input-buffer (* Uint8) null)
(var g-audio-input-buffer-size int 0)
(var g-audio-input-write-head int 0)
(var g-audio-input-read-head int 0)
;; (var enable-audio bool false)
(var enable-audio bool true)
;; (var g-enable-audio bool false)
(var g-enable-audio bool true)
;; Outputs to a format that can be plotted with gnuplot:
;; gnuplot> plot 'out.dat' with lines
@ -39,18 +39,18 @@
(fclose dest-file))
(defun-local audio-input-buffer-initialize ()
(set audio-input-buffer-size 44100)
(set audio-input-buffer (type-cast (calloc audio-input-buffer-size (sizeof Uint8))
(* Uint8)))
(set g-audio-input-buffer-size 44100)
(set g-audio-input-buffer (type-cast (calloc g-audio-input-buffer-size (sizeof Uint8))
(* Uint8)))
(var i int 0)
(while (< i audio-input-buffer-size)
(while (< i g-audio-input-buffer-size)
;; TODO: Use silence value from SDL audio spec
(set (at i audio-input-buffer) 127)
(set (at i g-audio-input-buffer) 127)
(incr i)))
(defun-local audio-input-buffer-destroy ()
(free audio-input-buffer)
(set audio-input-buffer null))
(free g-audio-input-buffer)
(set g-audio-input-buffer null))
(defun-local audio-output-callback (userdata (* void) stream (* Uint8) stream-length int)
(ZoneScopedN "Audio output")
@ -58,7 +58,6 @@
(var-static up bool false)
(set up (not up))
(var i int 0)
(var pi (const float) 3.14159f)
(var num-channels int 2)
(var samples-per-channel int (/ stream-length num-channels))
(while (< i stream-length)
@ -74,15 +73,15 @@
;; ;; Map to -1 to +1
;; (sin
;; ;; Map from position in buffer to 2pi range
;; (/ (* i 2 pi) (type-cast samples-per-channel float))))))
;; (/ (* i 2 g-pi) (type-cast samples-per-channel float))))))
;; Loop playback
(if audio-is-recording
(if g-audio-is-recording
(set mono-sample 127) ;; Silence
(block ;; Else, play the recording
(var recording-index int (+ (/ i num-channels) audio-input-read-head))
(set recording-index (mod recording-index audio-input-buffer-size))
(set mono-sample (at recording-index audio-input-buffer))))
(var recording-index int (+ (/ i num-channels) g-audio-input-read-head))
(set recording-index (mod recording-index g-audio-input-buffer-size))
(set mono-sample (at recording-index g-audio-input-buffer))))
;; Channels are interleaved, e.g. LRLR, not LLRR
(var channel int 0)
@ -91,15 +90,15 @@
;; (printf "[%d][%d] %d\n" (+ i channel) channel (at i stream))
(incr channel))
(set i (+ i num-channels)))
(set audio-input-read-head
(mod (+ audio-input-read-head samples-per-channel)
audio-input-buffer-size)))
(set g-audio-input-read-head
(mod (+ g-audio-input-read-head samples-per-channel)
g-audio-input-buffer-size)))
;; Note: If input is sampled at a different rate, playback will be at a lower pitch. Use SDL's
;; audio conversion functions to handle that properly
(defun-local audio-input-callback (userdata (* void) stream (* Uint8) stream-length int)
(ZoneScopedN "Audio input")
(unless audio-is-recording
(unless g-audio-is-recording
(return))
;; (printf "received audio %d %d %d\n"
;; (at 0 stream)
@ -107,10 +106,10 @@
;; (at (- stream-length 1) stream))
(var i int 0)
(while (< i stream-length)
(set (at audio-input-write-head audio-input-buffer) (at i stream))
(if (= audio-input-write-head (- audio-input-buffer-size 1))
(set audio-input-write-head 0)
(incr audio-input-write-head))
(set (at g-audio-input-write-head g-audio-input-buffer) (at i stream))
(if (= g-audio-input-write-head (- g-audio-input-buffer-size 1))
(set g-audio-input-write-head 0)
(incr g-audio-input-write-head))
(incr i)))
;; Allocates both names and array
@ -153,10 +152,11 @@
(ZoneScopedN "Audio intialization")
;; This will take a while. Make sure we aren't marked as ready while preparing
(set audio-is-initialized false)
(set g-audio-is-g-initialized false)
(var audio-driver (* (const char)) (SDL_GetCurrentAudioDriver))
(scope-timed "Audio drivers"
(scope-timed
"Audio drivers"
(printf "Available drivers:\n")
(var num-drivers int (SDL_GetNumAudioDrivers))
(var i int 0)
@ -188,7 +188,8 @@
(var selected-output-device (* (const char)) nullptr)
(var output-device-id SDL_AudioDeviceID 0)
(var obtained-output-spec SDL_AudioSpec (array 0))
(scope-timed "Audio output device"
(scope-timed
"Audio output device"
(var desired-spec SDL_AudioSpec (array 0))
(set (field desired-spec freq) 44100)
(set (field desired-spec format) AUDIO_U8)
@ -220,7 +221,8 @@
(var selected-input-device (* (const char)) nullptr)
(var obtained-input-spec SDL_AudioSpec (array 0))
(var input-device-id SDL_AudioDeviceID 0)
(scope-timed "Audio input device"
(scope-timed
"Audio input device"
(var desired-spec SDL_AudioSpec (array 0))
(set (field desired-spec freq) 44100)
(set (field desired-spec format) AUDIO_U8)
@ -278,7 +280,7 @@
(when output-device-out (set (deref output-device-out) output-device-id))
(when input-device-out (set (deref input-device-out) input-device-id))
(set audio-is-initialized true)
(set g-audio-is-g-initialized true)
(return (and output-device-id input-device-id)))
@ -288,19 +290,19 @@
(when (>= input-device 2) (SDL_CloseAudioDevice input-device)))
(var g-window (* SDL_Window) null)
(var g-output-device SDL_AudioDeviceID 0)
(var g-input-device SDL_AudioDeviceID 0)
(var g-output-device-spec SDL_AudioSpec)
(var g-input-device-spec SDL_AudioSpec)
(var monkey-mesh mesh-handle)
(var monkey-node scene-node)
(var monkey-anim-handle OgreAnimationHandle)
(var g-audio-output-device SDL_AudioDeviceID 0)
(var g-audio-input-device SDL_AudioDeviceID 0)
(var g-audio-output-device-spec SDL_AudioSpec)
(var g-audio-input-device-spec SDL_AudioSpec)
(var g-monkey-mesh mesh-handle)
(var g-monkey-node scene-node)
(var g-monkey-anim-handle OgreAnimationHandle)
(var g-light-node scene-node)
(var reload-sentinel int 2)
(var g-reload-sentinel int 2)
(var initialized bool false)
(var g-initialized bool false)
(forward-declare (namespace Ogre
(class Root)
@ -311,8 +313,8 @@
(defun-local audio-initialize-thread (user-data (* void) &return int)
(TracyCSetThreadName "Audio Initialization")
(unless (initialize-audio (addr g-output-device) (addr g-input-device)
(addr g-output-device-spec) (addr g-input-device-spec))
(unless (initialize-audio (addr g-audio-output-device) (addr g-audio-input-device)
(addr g-audio-output-device-spec) (addr g-audio-input-device-spec))
;; TODO: Communicate with main thread and close
(printf "warning: audio failed to initialize\n")
(return 1))
@ -320,20 +322,30 @@
(defun-local audio-shutdown-thread (user-data (* void) &return int)
(ZoneScopedN "SDL audio shutdown")
(sdl-audio-close g-output-device
g-input-device)
(sdl-audio-close g-audio-output-device
g-audio-input-device)
(return 0))
(defun-local audio-set-recording-state (new-value bool)
(scope ;; Toggle recording state
(SDL_LockAudioDevice g-audio-output-device)
(SDL_LockAudioDevice g-audio-input-device)
(set g-audio-is-recording new-value)
(SDL_UnlockAudioDevice g-audio-input-device)
(SDL_UnlockAudioDevice g-audio-output-device)))
(defun-local app-main (&return int)
(TracyCZoneN startup-zone "Startup" 1)
(unless initialized ;; Note, continues after audio thread starts up
(scope-timed "SDL initialization"
(unless g-initialized ;; Note, continues after audio thread starts up
(scope-timed
"SDL initialization"
;; (defun main (&return int)
(unless (sdl-initialize-for-3d (addr g-window))
(TracyCZoneEnd startup-zone)
(return 1))))
;; Audio needs to be re-initialized due to reload removing callbacks
(when enable-audio
;; Audio needs to be re-g-initialized due to reload removing callbacks
(when g-enable-audio
(audio-input-buffer-initialize)
;; Audio initialization is extremely slow (~2-3 seconds on my machine). Let's do it on a
;; separate thread to keep start up responsive
@ -343,25 +355,29 @@
;; detach it to not leave it dangling (see SDL_thread.h)
(SDL_DetachThread audio-init-thread-handle)
(ignore ;; Single threaded version
(unless (initialize-audio (addr g-output-device) (addr g-input-device)
(addr g-output-device-spec) (addr g-input-device-spec))
(unless (initialize-audio (addr g-audio-output-device) (addr g-audio-input-device)
(addr g-audio-output-device-spec) (addr g-audio-input-device-spec))
(sdl-shutdown g-window)
(sdl-audio-close g-output-device g-input-device)
(sdl-audio-close g-audio-output-device g-audio-input-device)
(TracyCZoneEnd startup-zone)
(return 1))))
(unless initialized
(scope-timed "Ogre initialization"
(unless g-initialized
(scope-timed
"Ogre initialization"
;; Ogre uses exceptions for error handling, so we can't gracefully close without getting all that
;; stuff set up (which I don't really want to do; it belongs in Gamelib)
(unless (ogre-initialize-sdl)
(TracyCZoneEnd startup-zone)
(return 1)))
(scope-timed "Scene setup/load meshes"
(set monkey-mesh (ogre-load-mesh "Monkey_Mesh.mesh"))
(set monkey-node (ogre-node-from-item monkey-mesh))
(scope-timed
"Scene setup/load meshes"
(set g-monkey-mesh (ogre-load-mesh "Monkey_Mesh.mesh"))
(set g-monkey-node (ogre-node-from-item g-monkey-mesh))
;; TODO: How to change this animation's name?
(set monkey-anim-handle
(ogre-attach-animation monkey-mesh "Monkey_Mesh.skeleton" "Monkey_AngleChin"))
(set g-monkey-anim-handle
(ogre-attach-animation g-monkey-mesh "Monkey_Mesh.skeleton" "Monkey_AngleChin"))
(set g-light-node (ogre-create-light))
@ -370,7 +386,7 @@
(ogreCreatePbsSpheres g-ogre-root (ogre-get-scene-manager))
(printf "Creating PBS spheres done\n")))
(set initialized true))
(set g-initialized true))
(var exit-reason (* (const char)) null)
@ -411,9 +427,9 @@
;; Our audio callbacks are going away!
;; The SDL_QueueAudio() API would make this easier to deal with, but then I need to manage
;; an audio thread on my own
(sdl-audio-close g-output-device
g-input-device)
(return reload-sentinel))
(sdl-audio-close g-audio-output-device
g-audio-input-device)
(return g-reload-sentinel))
(var delta-position vec3 (array 0.f 0.f 0.f))
(when (at SDL_SCANCODE_RIGHT currentKeyStates)
@ -425,11 +441,11 @@
(when (at SDL_SCANCODE_DOWN currentKeyStates)
(set (vec-y delta-position) (- (vec-y delta-position) move-speed)))
(var prev-recording-value bool audio-is-recording)
(set audio-is-recording (at SDL_SCANCODE_SPACE currentKeyStates))
(when (and audio-is-initialized
(!= prev-recording-value audio-is-recording)
(not audio-is-recording))
(var prev-recording-value bool g-audio-is-recording)
(audio-set-recording-state (at SDL_SCANCODE_SPACE currentKeyStates))
(when (and g-audio-is-g-initialized
(!= prev-recording-value g-audio-is-recording)
(not g-audio-is-recording))
(ZoneScopedN "Audio conversion")
(scope ;; Convert audio to playback format.
;; This was necessary in my case because my microphone records at 48kHz, but my sound card
@ -438,14 +454,14 @@
(var audio-conversion-settings SDL_AudioCVT)
(SDL_BuildAudioCVT (addr audio-conversion-settings)
;; Src
(field g-input-device-spec format)
(field g-input-device-spec channels)
(field g-input-device-spec freq)
(field g-audio-input-device-spec format)
(field g-audio-input-device-spec channels)
(field g-audio-input-device-spec freq)
;; Dest
(field g-output-device-spec format)
1 ;; (field g-output-device-spec channels) ;; We convert to stereo in playback callback
(field g-output-device-spec freq))
(set (field audio-conversion-settings len) audio-input-buffer-size)
(field g-audio-output-device-spec format)
1 ;; (field g-audio-output-device-spec channels) ;; We convert to stereo in playback callback
(field g-audio-output-device-spec freq))
(set (field audio-conversion-settings len) g-audio-input-buffer-size)
;; TODO: Does the buffer really nead to be 8x the src? Am I doing something wrong with my len?
;; Could be conversion to float then 2x that for resampling = 4x * 2x = 8x
(var converted-buffer-size-bytes int
@ -459,8 +475,8 @@
(type-cast (malloc converted-buffer-size-bytes) (* Uint8)))
(scope ;; Copy src to in-place conversion buffer
(var i int 0)
(while (< i audio-input-buffer-size)
(set (at i (field audio-conversion-settings buf)) (at i audio-input-buffer))
(while (< i g-audio-input-buffer-size)
(set (at i (field audio-conversion-settings buf)) (at i g-audio-input-buffer))
(incr i)))
(unless (= 0 (SDL_ConvertAudio (addr audio-conversion-settings)))
(set exit-reason "Failed to convert audio")
@ -475,16 +491,16 @@
(scope ;; Copy back to destination buffer. Note: this could drop samples, because it should
;; use the converted buffer size instead of the fixed array size!
(var i int 0)
(while (< i audio-input-buffer-size)
(set (at i audio-input-buffer)
(while (< i g-audio-input-buffer-size)
(set (at i g-audio-input-buffer)
(at i (type-cast (field audio-conversion-settings buf) (* Uint8))))
;; TODO: If my sample rate is higher when recorded, that means I need to record more
;; samples and play back fewer. Because I use the same buffer, it means I don't have
;; enough samples at the lower playback rate, and start hitting buffer workspace.
;; Zero them out for now. I should record with enough samples to make it exactly fit
;; after conversion
(when (>= i (- audio-input-buffer-size 3900))
(set (at i audio-input-buffer) 127))
(when (>= i (- g-audio-input-buffer-size 3900))
(set (at i g-audio-input-buffer) 127))
(incr i)))
(free (field audio-conversion-settings buf)))
@ -492,8 +508,8 @@
;; 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))
(while (< i g-audio-input-buffer-size)
(var current-value int (- (type-cast (at i g-audio-input-buffer) int) 127))
(when (< highest-peak (abs current-value))
(set highest-peak (abs current-value)))
(incr i))
@ -510,17 +526,19 @@
(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))
(while (< i g-audio-input-buffer-size)
(var current-value int (- (type-cast (at i g-audio-input-buffer) int) 127))
(set (at i g-audio-input-buffer) (type-cast
;; Map back to 0-255
(+ 127.f
(* current-value scaling-factor))
Uint8))
(incr i)))))
(scope-timed "Detect pitch"
(scope-timed
"Detect pitch"
(set pitch-hz
(audio-detect-pitch audio-input-buffer audio-input-buffer-size (field g-output-device-spec freq)))
(audio-detect-pitch g-audio-input-buffer g-audio-input-buffer-size
(field g-audio-output-device-spec freq)))
(printf "Pitch (hz): %f\n" pitch-hz)))
(when (!= 0 pitch-hz)
@ -534,32 +552,33 @@
;; Visualize audio playback
(var audio-read-head-to-world float (* 20.f
(/ audio-input-read-head
(type-cast audio-input-buffer-size float))))
(/ g-audio-input-read-head
(type-cast g-audio-input-buffer-size float))))
(var audio-volume-to-world float (* 7.f
(/ (- (at audio-input-read-head audio-input-buffer) 127)
(/ (- (at g-audio-input-read-head g-audio-input-buffer) 127)
127.f)))
(var audio-pos vec3 (array audio-read-head-to-world 0.f audio-volume-to-world))
;; (var final-pos vec3 (vec3-scale-add-vec3 delta-position delta-time tracker-position))
(var final-pos vec3 (vec3-scale-add-vec3 delta-position delta-time
(vec3-add tracker-position audio-pos)))
(when audio-is-recording
(when g-audio-is-recording
(set (vec-y final-pos) (+ (vec-y final-pos) -2.f)))
(ogre-node-set-position (addr monkey-node) (vec-xyz final-pos))
(ogre-node-set-position (addr g-monkey-node) (vec-xyz final-pos))
(ogre-animation-add-time monkey-anim-handle delta-time)
(ogre-animation-add-time g-monkey-anim-handle delta-time)
(set last-frame-perf-count (SDL_GetPerformanceCounter))
(scope-timed "Ogre render + vsync"
(scope-timed
"Ogre render + vsync"
(unless (ogre-render-frame)
(set exit-reason "Failed to render frame")
(break))))
(ZoneScopedN "Shutdown")
(set audio-is-recording false)
(audio-set-recording-state false)
;; Audio shutdown is extremely slow (~2-3 seconds on my machine). Let's do it on a
;; separate thread to let other things shutdown at the same time
@ -567,17 +586,19 @@
(SDL_CreateThread audio-shutdown-thread "AudioShutdown" null))
(audio-dump-recorded-buffer "out.dat"
audio-input-buffer audio-input-buffer-size)
g-audio-input-buffer g-audio-input-buffer-size)
(audio-input-buffer-destroy)
(scope-timed "Ogre shutdown"
(scope-timed
"Ogre shutdown"
(ogre-shutdown))
;; Make sure audio is shutdown before destroying SDL
(var status int -1)
(SDL_WaitThread audio-shutdown-thread-handle (addr status))
(scope-timed "SDL shutdown"
(scope-timed
"SDL shutdown"
(sdl-shutdown g-window))
(when exit-reason
@ -591,7 +612,7 @@
(return false))
((= 1 result)
(return false))
((= reload-sentinel result)
((= g-reload-sentinel result)
(return true)))
(return false))
@ -687,8 +708,8 @@
(set (at i texture-process-results) -1)
(run-process-start-or (addr (at i texture-process-results))
("convert" texture-asset texture-converted)
(Log "Asset-Building: failed to convert 2D texture. Is 'convert' on your path? You may need to
install ImageMagick. See https://www.imagemagick.org/script/download.php\n")
(Log "Asset-Building: failed to convert 2D texture. Is 'convert' on your
path? You may need to install ImageMagick. See https://www.imagemagick.org/script/download.php\n")
(return false))
(incr num-processes-running)
(incr i))

Loading…
Cancel
Save