From 2491b8fbb96f949a9cd274b0d7f14b20f2880531 Mon Sep 17 00:00:00 2001 From: Macoy Madson Date: Wed, 25 Nov 2020 15:27:34 -0800 Subject: [PATCH] Ogre resources, audio resampling * Ogre materials are now loading, though my Monkey material is incompatible with Ogre 2 * Convert audio from recording format to playback format. This was necessary because I record at 48kHz but playback is at 44.1kHz. Without this conversion, recorded audio would sound lower, because it was being played at a slower rate --- src/OgreInitialize.cake | 22 +++++++--- test/AudioPlot.gnuplot | 2 +- test/src/VocalGame.cake | 95 +++++++++++++++++++++++++++++++++++------ 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/src/OgreInitialize.cake b/src/OgreInitialize.cake index 6ca3154..c6d2e77 100644 --- a/src/OgreInitialize.cake +++ b/src/OgreInitialize.cake @@ -58,11 +58,11 @@ (defun-local registerHlms () (var resourcePath (in Ogre String) "data/") - (var cf (in Ogre ConfigFile)) - (on-call cf load (+ resourcePath "resources2.cfg")) + (var config (in Ogre ConfigFile)) + (on-call config load (+ resourcePath "resources2.cfg")) (var rootHlmsFolder (in Ogre String) - (+ resourcePath (on-call cf getSetting "DoNotUseAsResource" "Hlms" ""))) + (+ resourcePath (on-call config getSetting "DoNotUseAsResource" "Hlms" ""))) (cond ((on-call rootHlmsFolder empty) (set rootHlmsFolder "./")) @@ -224,10 +224,20 @@ (on-call-ptr compositorManager addWorkspace g_sceneManager (on-call-ptr g_window getTexture) camera workspaceName true) - (on-call (call (in Ogre ResourceGroupManager getSingleton)) addResourceLocation + (var resource-group-manager (& (in Ogre ResourceGroupManager)) + (call (in Ogre ResourceGroupManager getSingleton))) + (on-call resource-group-manager addResourceLocation "data/Models" "FileSystem" "Models") - (on-call (call (in Ogre ResourceGroupManager getSingleton)) addResourceLocation - "data/Materials" "FileSystem" "Materials") + + (scope ;; Materials + ;; I had to read https://forums.ogre3d.org/viewtopic.php?f=5&t=94769 before figuring out + ;; groups needed to be initialized and loaded in order to work (at least, materials do) + (on-call resource-group-manager createResourceGroup "Materials") + (on-call resource-group-manager addResourceLocation + "data/Materials" "FileSystem" "Materials") + (on-call resource-group-manager initialiseResourceGroup "Materials" + true) ;; changeLocaleTemporarily: See comment above initialiseResourceGroup + (on-call resource-group-manager loadResourceGroup "Materials")) ;; (call (in Ogre WindowEventUtilities addWindowEventListener) ;; g_window (addr g_myWindowEventListener)) diff --git a/test/AudioPlot.gnuplot b/test/AudioPlot.gnuplot index 5884d40..faf92c3 100644 --- a/test/AudioPlot.gnuplot +++ b/test/AudioPlot.gnuplot @@ -38,7 +38,7 @@ set key textcolor rgb text_color set object rectangle from screen 0,0 to screen 1,1 behind fillcolor rgb background_color fillstyle solid noborder set yrange [0:255] -plot 'out.dat' with lines +plot 'out.dat' with lines, 'converted.dat' with lines # Let the user interact with it, e.g. right click drag to zoom in pause mouse close \ No newline at end of file diff --git a/test/src/VocalGame.cake b/test/src/VocalGame.cake index 05b459c..1d1d52f 100644 --- a/test/src/VocalGame.cake +++ b/test/src/VocalGame.cake @@ -19,17 +19,21 @@ (var audio-input-write-head int 0) (var audio-input-read-head int 0) +;; (var enable-audio bool false) +(var enable-audio bool true) + ;; Outputs to a format that can be plotted with gnuplot: ;; gnuplot> plot 'out.dat' with lines -(defun-local audio-dump-recorded-buffer () - (var dest-file (* FILE) (fopen "out.dat" "w")) +(defun-local audio-dump-recorded-buffer (output-filename (* (const char)) + buffer (* Uint8) buffer-size int) + (var dest-file (* FILE) (fopen output-filename "w")) (unless dest-file (printf "Could not open file to write data\n") (return)) (var i int 0) - (while (< i (array-size audio-input-buffer)) - (fprintf dest-file "%d %d\n" i (at i audio-input-buffer)) + (while (< i buffer-size) + (fprintf dest-file "%d %d\n" i (at i buffer)) (incr i)) (fclose dest-file)) @@ -128,7 +132,10 @@ (free device-names)) (defun-local initialize-audio (output-device-out (* SDL_AudioDeviceID) - input-device-out (* SDL_AudioDeviceID) &return bool) + input-device-out (* SDL_AudioDeviceID) + output-device-spec-out (* SDL_AudioSpec) + input-device-spec-out (* SDL_AudioSpec) + &return bool) (var audio-driver (* (const char)) (SDL_GetCurrentAudioDriver)) (scope ;; Drivers (printf "Available drivers:\n") @@ -185,7 +192,8 @@ SDL_AUDIO_ALLOW_SAMPLES_CHANGE SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) (if (>= output-device-id valid-device-start-num) - (set selected-output-device device-name) + (block (set selected-output-device device-name) + (set (deref output-device-spec-out) obtained-output-spec)) (block (sdl-print-error) (sdl-audio-free-device-list devices num-devices) (return false)))) @@ -224,6 +232,7 @@ (sdl-print-error) (sdl-audio-free-device-list capture-devices num-capture-devices) (return false)) + (set (deref input-device-spec-out) obtained-input-spec) (set selected-input-device device-name) (when selected-input-device ;; print final settings @@ -265,10 +274,14 @@ (audio-input-buffer-initialize) (var output-device SDL_AudioDeviceID 0) (var input-device SDL_AudioDeviceID 0) - (unless (initialize-audio (addr output-device) (addr input-device)) - (sdl-shutdown window) - (sdl-audio-close output-device input-device) - (return 1)) + (var output-device-spec SDL_AudioSpec) + (var input-device-spec SDL_AudioSpec) + (when enable-audio + (unless (initialize-audio (addr output-device) (addr input-device) + (addr output-device-spec) (addr input-device-spec)) + (sdl-shutdown window) + (sdl-audio-close output-device input-device) + (return 1))) ;; 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) @@ -316,6 +329,63 @@ (set audio-is-recording (at SDL_SCANCODE_SPACE currentKeyStates)) (when (and (!= prev-recording-value audio-is-recording) (not audio-is-recording)) + (scope ;; Convert audio to playback format. + ;; This was necessary in my case because my microphone records at 48kHz, but my sound card + ;; expects 44.1kHz output + ;; Audio Streams are worth looking at in the future, so it's not all one big batch process + (var audio-conversion-settings SDL_AudioCVT) + (SDL_BuildAudioCVT (addr audio-conversion-settings) + ;; Src + (field input-device-spec format) + (field input-device-spec channels) + (field input-device-spec freq) + ;; Dest + (field output-device-spec format) + 1 ;; (field output-device-spec channels) ;; We convert to stereo in playback callback + (field output-device-spec freq)) + (set (field audio-conversion-settings len) (array-size audio-input-buffer)) + ;; 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 + (* (field audio-conversion-settings len) + (field audio-conversion-settings len_mult))) + (printf "Converted buffer size: %d bytes because %d len %d len_mult\n" + converted-buffer-size-bytes + (field audio-conversion-settings len) + (field audio-conversion-settings len_mult)) + (set (field audio-conversion-settings buf) + (type-cast (malloc converted-buffer-size-bytes) (* Uint8))) + (scope ;; Copy src to in-place conversion buffer + (var i int 0) + (while (< i (array-size audio-input-buffer)) + (set (at i (field audio-conversion-settings buf)) (at i audio-input-buffer)) + (incr i))) + (unless (= 0 (SDL_ConvertAudio (addr audio-conversion-settings))) + (set exit-reason "Failed to convert audio") + (free (field audio-conversion-settings buf)) + (break)) + ;; TODO: This shouldn't be byte size, it should be sample count, to support other formats + (audio-dump-recorded-buffer "converted.dat" + (field audio-conversion-settings buf) + ;; Remove the extra buffer used by the converter + (/ converted-buffer-size-bytes + (field audio-conversion-settings len_mult))) + (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 (array-size audio-input-buffer)) + (set (at i 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 (- (array-size audio-input-buffer) 3900)) + (set (at i audio-input-buffer) 127)) + (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) @@ -326,7 +396,7 @@ (set highest-peak (abs current-value))) (incr i)) (when highest-peak - ;; Get within 90% of the peak, to avoid clipping + ;; 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" @@ -374,7 +444,8 @@ (set exit-reason "Failed to render frame") (break))) - (audio-dump-recorded-buffer) + (audio-dump-recorded-buffer "out.dat" + audio-input-buffer (array-size audio-input-buffer)) (ogre-shutdown) (sdl-audio-close output-device