From 9e5a6a24921ff0cbcd9e2ff4f6937e6ebc0957f5 Mon Sep 17 00:00:00 2001 From: Zach Bacon Date: Sat, 23 Dec 2023 21:33:03 -0500 Subject: [PATCH] FAudio: Implement and have functional FAudio output Corrected the current FAudio output code, FAudio api wasn't a direct 1 for 1 code replacement. Adjusted the existing code structure so that FAudioVoiceCallBack struct was being properly called on. Signed-off-by: Zach Bacon --- src/wx/faudio.cpp | 198 +++++++++++++++++++++++----------------------- 1 file changed, 97 insertions(+), 101 deletions(-) diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index a49bec67..9d44da41 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -8,9 +8,8 @@ #include "../common/SoundDriver.h" // FAudio -#include +#include -// MMDevice API #include #include #include @@ -19,56 +18,56 @@ #include "../System.h" // for systemMessage() #include "../gba/Globals.h" -int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, - const wxString* match) -{ - HRESULT hr; - UINT32 dev_count = 0; +namespace { - hr = FAudio_GetDeviceCount(fa, &dev_count); + int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, + const wxString* match) + { + uint32_t hr; + uint32_t dev_count = 0; + hr = FAudio_GetDeviceCount(fa, &dev_count); - if (hr != S_OK) { - wxLogError(_("FAudio: Enumerating devices failed!")); - return true; - } else { - FAudioDeviceDetails dd; + if (hr != 0) { + wxLogError(_("FAudio: Enumerating devices failed!")); + return -1; + } else { + FAudioDeviceDetails dd; - for (UINT32 i = 0; i < dev_count; i++) { - hr = FAudio_GetDeviceDetails(fa, i, &dd); + for (uint32_t i = 0; i < dev_count; i++) { + hr = FAudio_GetDeviceDetails(fa, i, &dd); - if (hr != S_OK) { - continue; - } else { - if (ids) { - ids->push_back((wchar_t*) dd.DeviceID); //FAudio is an interesting beast, but not that hard to adapt... once you get used to it, XAudio2 wouldn't need this, but FAudio declares FAudioDeviceDetails as int32_t - names->push_back((wchar_t*) dd.DisplayName); - } else if (*match == dd.DeviceID) - return i; + if (hr != 0) { + continue; + } else { + if (ids) { + ids->push_back((wchar_t*) dd.DeviceID); + names->push_back((wchar_t*) dd.DisplayName); + } else if (*match == wxString((wchar_t*) dd.DeviceID)) + return i; + } } } - } - return -1; + return -1; + } } bool GetFADevices(wxArrayString& names, wxArrayString& ids) { - HRESULT hr; + uint32_t hr; FAudio* fa = NULL; - UINT32 flags = 0; + uint32_t flags = 0; #ifdef _DEBUG flags = FAUDIO_DEBUG_ENGINE; #endif + hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR); - hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR); //Apparently this needs 3 parameters, the processor. - - if (hr != S_OK) { + if (hr != 0) { wxLogError(_("The FAudio interface failed to initialize!")); return false; } GetFADevices(fa, &names, &ids, NULL); - //fa->Release(); FAudio_Release(fa); return true; } @@ -189,48 +188,49 @@ public: LeaveCriticalSection(&lock); } -} g_notifier; +} f_notifier; // Synchronization Event class FAudio_BufferNotify : public FAudioVoiceCallback { public: - HANDLE hBufferEndEvent; + void *hBufferEndEvent; FAudio_BufferNotify() { hBufferEndEvent = NULL; hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL); assert(hBufferEndEvent != NULL); - } + OnBufferEnd = &FAudio_BufferNotify::StaticOnBufferEnd; + OnVoiceProcessingPassStart = &FAudio_BufferNotify::StaticOnVoiceProcessingPassStart; + OnVoiceProcessingPassEnd = &FAudio_BufferNotify::StaticOnVoiceProcessingPassEnd; + OnStreamEnd = &FAudio_BufferNotify::StaticOnStreamEnd; + OnBufferStart = &FAudio_BufferNotify::StaticOnBufferStart; + OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd; + OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError; + } ~FAudio_BufferNotify() { CloseHandle(hBufferEndEvent); hBufferEndEvent = NULL; } - STDMETHOD_(void, OnBufferEnd) - (void* pBufferContext) - { - assert(hBufferEndEvent != NULL); - SetEvent(hBufferEndEvent); + + static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void * pBufferContext) { + FAudio_BufferNotify* self = static_cast(callback); + if (self != nullptr && self->hBufferEndEvent != NULL) + { + SetEvent(self->hBufferEndEvent); + } } - - // dummies: - STDMETHOD_(void, OnVoiceProcessingPassStart) - (UINT32 BytesRequired) {} - STDMETHOD_(void, OnVoiceProcessingPassEnd) - () {} - STDMETHOD_(void, OnStreamEnd) - () {} - STDMETHOD_(void, OnBufferStart) - (void* pBufferContext) {} - STDMETHOD_(void, OnLoopEnd) - (void* pBufferContext) {} - STDMETHOD_(void, OnVoiceError) - (void* pBufferContext, HRESULT Error){}; + static void StaticOnVoiceProcessingPassStart(FAudioVoiceCallback* callback, uint32_t BytesRequired) {} + static void StaticOnVoiceProcessingPassEnd(FAudioVoiceCallback* callback) {} + static void StaticOnStreamEnd(FAudioVoiceCallback* callback) {} + static void StaticOnBufferStart(FAudioVoiceCallback* callback, void * pBufferContext) {} + static void StaticOnLoopEnd(FAudioVoiceCallback* callback, void * pBufferContext) {} + static void StaticOnVoiceError(FAudioVoiceCallback* callback, void * pBufferContext, uint32_t Error) {} }; - + // Class Declaration class FAudio_Output : public SoundDriver { @@ -258,9 +258,9 @@ private: bool failed; bool initialized; bool playing; - UINT32 freq; - UINT32 bufferCount; - BYTE* buffers; + uint32_t freq; + uint32_t bufferCount; + uint8_t* buffers; int currentBuffer; int soundBufferLen; @@ -288,14 +288,14 @@ FAudio_Output::FAudio_Output() faud = NULL; mVoice = NULL; sVoice = NULL; - ZeroMemory(&buf, sizeof(buf)); - ZeroMemory(&vState, sizeof(vState)); - g_notifier.do_register(this); + memset(&buf, NULL, sizeof(buf)); + memset(&vState, NULL, sizeof(vState)); + f_notifier.do_register(this); } FAudio_Output::~FAudio_Output() { - g_notifier.do_unregister(this); + f_notifier.do_unregister(this); close(); } @@ -305,8 +305,7 @@ void FAudio_Output::close() if (sVoice) { if (playing) { - HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); } FAudioVoice_DestroyVoice(sVoice); @@ -339,15 +338,15 @@ bool FAudio_Output::init(long sampleRate) if (failed || initialized) return false; - HRESULT hr; + uint32_t hr; // Initialize FAudio - UINT32 flags = 0; + uint32_t flags = 0; //#ifdef _DEBUG // flags = FAUDIO_DEBUG_ENGINE; //#endif hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR); - if (hr != S_OK) { + if (hr != 0) { wxLogError(_("The FAudio interface failed to initialize!")); failed = true; return false; @@ -359,27 +358,26 @@ bool FAudio_Output::init(long sampleRate) soundBufferLen = (freq / 60) * 4; // create own buffers to store sound data because it must not be // manipulated while the voice plays from it - buffers = (BYTE*)malloc((bufferCount + 1) * soundBufferLen); + buffers = (uint8_t*)malloc((bufferCount + 1) * soundBufferLen); // + 1 because we need one temporary buffer when all others are in use - WAVEFORMATEX wfx; - ZeroMemory(&wfx, sizeof(wfx)); - wfx.wFormatTag = WAVE_FORMAT_PCM; + FAudioWaveFormatEx wfx; + memset(&wfx, NULL, sizeof(wfx)); + wfx.wFormatTag = FAUDIO_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = freq; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; // create sound receiver - hr = FAudio_CreateMasteringVoice( - faud, - &mVoice, - FAUDIO_DEFAULT_CHANNELS, - FAUDIO_DEFAULT_SAMPLERATE, - 0, - FAGetDev(faud), - NULL); + hr = FAudio_CreateMasteringVoice(faud, + &mVoice, + FAUDIO_DEFAULT_CHANNELS, + FAUDIO_DEFAULT_SAMPLERATE, + 0, + FAGetDev(faud), + NULL); - if (hr != S_OK) { + if (hr != 0) { wxLogError(_("FAudio: Creating mastering voice failed!")); failed = true; return false; @@ -387,10 +385,9 @@ bool FAudio_Output::init(long sampleRate) // create sound emitter //This should be FAudio_CreateSourceVoice() - //hr = faud->CreateSourceVoice(&sVoice, &wfx, 0, 4.0f, ¬ify); - hr = FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL); + hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, ¬ify, NULL, NULL); - if (hr != S_OK) { + if (hr != 0) { wxLogError(_("FAudio: Creating source voice failed!")); failed = true; return false; @@ -399,9 +396,8 @@ bool FAudio_Output::init(long sampleRate) if (gopts.upmix) { // set up stereo upmixing FAudioDeviceDetails dd; - ZeroMemory(&dd, sizeof(dd)); - hr = FAudio_GetDeviceDetails(faud, 0, &dd); - assert(hr == S_OK); + memset(&dd, NULL, sizeof(dd)); + assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0); float* matrix = NULL; matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels); @@ -497,8 +493,8 @@ bool FAudio_Output::init(long sampleRate) } if (matrixAvailable) { - hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS); //What I have here for the OperationSet maybe wrong... - assert(hr == S_OK); + hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS); + assert(hr == 0); } free(matrix); @@ -506,7 +502,7 @@ bool FAudio_Output::init(long sampleRate) } hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(hr == 0); playing = true; currentBuffer = 0; device_changed = false; @@ -516,7 +512,7 @@ bool FAudio_Output::init(long sampleRate) void FAudio_Output::write(uint16_t* finalWave, int length) { - UINT32 flags = 0; + uint32_t flags = 0; if (!initialized || failed) return; @@ -547,7 +543,7 @@ void FAudio_Output::write(uint16_t* finalWave, int length) if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) { // wait for one buffer to finish playing if (WaitForSingleObject(notify.hBufferEndEvent, 10000) == WAIT_TIMEOUT) { - device_changed = true; + device_changed = true; } } else { // drop current audio frame @@ -557,13 +553,13 @@ void FAudio_Output::write(uint16_t* finalWave, int length) } // copy & protect the audio data in own memory area while playing it - CopyMemory(&buffers[currentBuffer * soundBufferLen], finalWave, soundBufferLen); + memcpy(&buffers[currentBuffer * soundBufferLen], finalWave, soundBufferLen); buf.AudioBytes = soundBufferLen; buf.pAudioData = &buffers[currentBuffer * soundBufferLen]; currentBuffer++; currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer - HRESULT hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL); // send buffer to queue. - assert(hr == S_OK); + uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL); + assert(hr == 0); } void FAudio_Output::pause() @@ -572,8 +568,8 @@ void FAudio_Output::pause() return; if (playing) { - HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); playing = false; } } @@ -584,8 +580,8 @@ void FAudio_Output::resume() return; if (!playing) { - HRESULT hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + uint32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); playing = true; } } @@ -596,8 +592,8 @@ void FAudio_Output::reset() return; if (playing) { - HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); } FAudioSourceVoice_FlushSourceBuffers(sVoice); @@ -613,8 +609,8 @@ void FAudio_Output::setThrottle(unsigned short throttle_) if (throttle_ == 0) throttle_ = 100; - HRESULT hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY); - assert(hr == S_OK); + uint32_t hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY); + assert(hr == 0); } void faudio_device_changed(FAudio_Output* instance)