From 75355eca4c376c04ff8bbce3a0a85951d64e2b80 Mon Sep 17 00:00:00 2001 From: Zach Bacon Date: Sat, 23 Dec 2023 21:33:03 -0500 Subject: [PATCH 1/4] faudio.cpp: Begin rework of faudio implementation This begins the rework of the faudio code begin with removing a good deal of the imme code that the driver used to poll audio interfaces Removed the use of HRESULT code since that's more windowisms. Also adapted more of the faudio stuff like WaveFormatEx is FAudioWaveFormatEx WAVE_FORMAT_PCM is FAUDIO_FORMAT_PCM. Also changed S_OK into R_OK but I'm pretty sure that isn't the way to go. That said and done, this is still a work in progress, but it compiles. But it doesn't initilize yet, I might need an extra set of eyes for that. Signed-off-by: Zach Bacon --- src/wx/faudio.cpp | 248 +++++++++++----------------------------------- 1 file changed, 58 insertions(+), 190 deletions(-) diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index a49bec67..0077a690 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -8,10 +8,8 @@ #include "../common/SoundDriver.h" // FAudio -#include +#include -// MMDevice API -#include #include #include @@ -22,21 +20,16 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, const wxString* match) { - HRESULT hr; - UINT32 dev_count = 0; + uint32_t dev_count = 0; - hr = FAudio_GetDeviceCount(fa, &dev_count); - - if (hr != S_OK) { + if (FAudio_GetDeviceCount(fa, &dev_count) != R_OK) { wxLogError(_("FAudio: Enumerating devices failed!")); return true; } else { FAudioDeviceDetails dd; - for (UINT32 i = 0; i < dev_count; i++) { - hr = FAudio_GetDeviceDetails(fa, i, &dd); - - if (hr != S_OK) { + for (uint32_t i = 0; i < dev_count; i++) { + if (FAudio_GetDeviceDetails(fa, i, &dd) != R_OK) { continue; } else { if (ids) { @@ -53,22 +46,18 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, bool GetFADevices(wxArrayString& names, wxArrayString& ids) { - HRESULT 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); //Apparently this needs 3 parameters, the processor. - - if (hr != S_OK) { + if (FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR) != R_OK) { wxLogError(_("The FAudio interface failed to initialize!")); return false; } GetFADevices(fa, &names, &ids, NULL); - //fa->Release(); FAudio_Release(fa); return true; } @@ -87,148 +76,47 @@ class FAudio_Output; static void faudio_device_changed(FAudio_Output*); -class FAudio_Device_Notifier : public IMMNotificationClient { - volatile LONG registered; - IMMDeviceEnumerator* pEnumerator; - +class FAudio_Device_Notifier { std::wstring last_device; - - CRITICAL_SECTION lock; std::vector instances; public: - FAudio_Device_Notifier() - : registered(0) - { - InitializeCriticalSection(&lock); - } - ~FAudio_Device_Notifier() - { - DeleteCriticalSection(&lock); - } - - ULONG STDMETHODCALLTYPE AddRef() - { - return 1; - } - - ULONG STDMETHODCALLTYPE Release() - { - return 1; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) - { - if (IID_IUnknown == riid) { - *ppvInterface = (IUnknown*)this; - } else if (__uuidof(IMMNotificationClient) == riid) { - *ppvInterface = (IMMNotificationClient*)this; - } else { - *ppvInterface = NULL; - return E_NOINTERFACE; - } - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) - { - if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) { - last_device = pwstrDeviceId; - EnterCriticalSection(&lock); - - for (auto it = instances.begin(); it < instances.end(); ++it) { - faudio_device_changed(*it); - } - - LeaveCriticalSection(&lock); - } - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; } - HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; } - HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { return S_OK; } - HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { return S_OK; } + FAudio_Device_Notifier() = default; + ~FAudio_Device_Notifier() = default; void do_register(FAudio_Output* p_instance) { - if (InterlockedIncrement(®istered) == 1) { - pEnumerator = NULL; - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); - - if (SUCCEEDED(hr)) { - pEnumerator->RegisterEndpointNotificationCallback(this); - } - } - - EnterCriticalSection(&lock); instances.push_back(p_instance); - LeaveCriticalSection(&lock); } void do_unregister(FAudio_Output* p_instance) { - if (InterlockedDecrement(®istered) == 0) { - if (pEnumerator) { - pEnumerator->UnregisterEndpointNotificationCallback(this); - pEnumerator->Release(); - pEnumerator = NULL; - } - } - - EnterCriticalSection(&lock); - for (auto it = instances.begin(); it < instances.end(); ++it) { if (*it == p_instance) { instances.erase(it); break; } } - - LeaveCriticalSection(&lock); + } + void OnDefaultDeviceChanged() + { + for (auto it = instances.begin(); it < instances.end(); ++it) + { + faudio_device_changed(*it); + } } } g_notifier; // Synchronization Event class FAudio_BufferNotify : public FAudioVoiceCallback { public: - HANDLE hBufferEndEvent; - - FAudio_BufferNotify() - { - hBufferEndEvent = NULL; - hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - assert(hBufferEndEvent != NULL); - } - - ~FAudio_BufferNotify() - { - CloseHandle(hBufferEndEvent); - hBufferEndEvent = NULL; - } - - STDMETHOD_(void, OnBufferEnd) - (void* pBufferContext) - { - assert(hBufferEndEvent != NULL); - SetEvent(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){}; + void OnBufferEnd(void* pBufferContext) {} + void OnVoiceProcessingPassStart(uint32_t BytesRequired) {} + void OnVoiceProcessingPassEnd() {} + void OnStreamEnd() {} + void OnBufferStart(void* pBufferContext) {} + void OnLoopEnd(void* pBufferContext) {} + void OnVoiceError(void* pBufferContext, int Error) {} }; // Class Declaration @@ -258,9 +146,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,8 +176,8 @@ FAudio_Output::FAudio_Output() faud = NULL; mVoice = NULL; sVoice = NULL; - ZeroMemory(&buf, sizeof(buf)); - ZeroMemory(&vState, sizeof(vState)); + memset(&buf, NULL, sizeof(buf)); + memset(&vState, NULL, sizeof(vState)); g_notifier.do_register(this); } @@ -305,8 +193,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) == R_OK); } FAudioVoice_DestroyVoice(sVoice); @@ -339,15 +226,13 @@ bool FAudio_Output::init(long sampleRate) if (failed || initialized) return false; - HRESULT 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 (FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR) != R_OK) { wxLogError(_("The FAudio interface failed to initialize!")); failed = true; return false; @@ -359,27 +244,19 @@ 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); - if (hr != S_OK) { + if (FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), NULL) != R_OK) { wxLogError(_("FAudio: Creating mastering voice failed!")); failed = true; return false; @@ -388,9 +265,8 @@ 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); - if (hr != S_OK) { + if (FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL) != R_OK) { wxLogError(_("FAudio: Creating source voice failed!")); failed = true; return false; @@ -399,9 +275,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) == R_OK); float* matrix = NULL; matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels); @@ -497,16 +372,14 @@ 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); + assert(FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS) == R_OK); } free(matrix); matrix = NULL; } - hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); playing = true; currentBuffer = 0; device_changed = false; @@ -516,7 +389,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; @@ -542,28 +415,27 @@ void FAudio_Output::write(uint16_t* finalWave, int length) // there is at least one free buffer break; - } else { + } //else { // the maximum number of buffers is currently queued - if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) { + //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; - } - } else { + // if (WaitForSingleObject(notify.hBufferEndEvent, 10000) == WAIT_TIMEOUT) { + // device_changed = true; + // } + //} else { // drop current audio frame - return; - } - } + // return; + //} + //} } // 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); + assert(FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL) == R_OK); } void FAudio_Output::pause() @@ -572,8 +444,7 @@ void FAudio_Output::pause() return; if (playing) { - HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); playing = false; } } @@ -584,8 +455,7 @@ void FAudio_Output::resume() return; if (!playing) { - HRESULT hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); playing = true; } } @@ -596,8 +466,7 @@ void FAudio_Output::reset() return; if (playing) { - HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); - assert(hr == S_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); } FAudioSourceVoice_FlushSourceBuffers(sVoice); @@ -613,8 +482,7 @@ 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); + assert(FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY) == R_OK); } void faudio_device_changed(FAudio_Output* instance) From 41552e372ac1e6c2ec8463bd818e99970a0a9a2a Mon Sep 17 00:00:00 2001 From: Zach Bacon Date: Thu, 11 Jan 2024 17:21:14 -0500 Subject: [PATCH 2/4] src/wx/faudio.cpp: fix some misconceptions R_OK isn't a replacement for S_OK, according to https://github.com/FNA-XNA/FAudio/blob/master/tests/FAudio_compat.h it should be defined as 0 Signed-off-by: Zach Bacon --- src/wx/faudio.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index 0077a690..b5786723 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -22,20 +22,20 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, { uint32_t dev_count = 0; - if (FAudio_GetDeviceCount(fa, &dev_count) != R_OK) { + if (FAudio_GetDeviceCount(fa, &dev_count) != 0) { wxLogError(_("FAudio: Enumerating devices failed!")); return true; } else { FAudioDeviceDetails dd; for (uint32_t i = 0; i < dev_count; i++) { - if (FAudio_GetDeviceDetails(fa, i, &dd) != R_OK) { + if (FAudio_GetDeviceDetails(fa, i, &dd) != 0) { 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) + } else if (*match == wxString((wchar_t*) dd.DeviceID)) return i; } } @@ -52,7 +52,7 @@ bool GetFADevices(wxArrayString& names, wxArrayString& ids) flags = FAUDIO_DEBUG_ENGINE; #endif - if (FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR) != R_OK) { + if (FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { wxLogError(_("The FAudio interface failed to initialize!")); return false; } @@ -193,7 +193,7 @@ void FAudio_Output::close() if (sVoice) { if (playing) { - assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); } FAudioVoice_DestroyVoice(sVoice); @@ -228,11 +228,11 @@ bool FAudio_Output::init(long sampleRate) // Initialize FAudio uint32_t flags = 0; - //#ifdef _DEBUG - // flags = FAUDIO_DEBUG_ENGINE; - //#endif + #ifdef _DEBUG + flags = FAUDIO_DEBUG_ENGINE; + #endif - if (FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR) != R_OK) { + if (FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { wxLogError(_("The FAudio interface failed to initialize!")); failed = true; return false; @@ -256,7 +256,7 @@ bool FAudio_Output::init(long sampleRate) wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; // create sound receiver - if (FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), NULL) != R_OK) { + if (FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), NULL) != 0) { wxLogError(_("FAudio: Creating mastering voice failed!")); failed = true; return false; @@ -266,7 +266,7 @@ bool FAudio_Output::init(long sampleRate) //This should be FAudio_CreateSourceVoice() //hr = faud->CreateSourceVoice(&sVoice, &wfx, 0, 4.0f, ¬ify); - if (FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL) != R_OK) { + if (FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL) != 0) { wxLogError(_("FAudio: Creating source voice failed!")); failed = true; return false; @@ -276,7 +276,7 @@ bool FAudio_Output::init(long sampleRate) // set up stereo upmixing FAudioDeviceDetails dd; memset(&dd, NULL, sizeof(dd)); - assert(FAudio_GetDeviceDetails(faud, 0, &dd) == R_OK); + assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0); float* matrix = NULL; matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels); @@ -372,14 +372,14 @@ bool FAudio_Output::init(long sampleRate) } if (matrixAvailable) { - assert(FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS) == R_OK); + assert(FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS) == 0); } free(matrix); matrix = NULL; } - assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); + assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); playing = true; currentBuffer = 0; device_changed = false; @@ -435,7 +435,7 @@ void FAudio_Output::write(uint16_t* finalWave, int length) buf.pAudioData = &buffers[currentBuffer * soundBufferLen]; currentBuffer++; currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer - assert(FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL) == R_OK); + assert(FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL) == 0); } void FAudio_Output::pause() @@ -444,7 +444,7 @@ void FAudio_Output::pause() return; if (playing) { - assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); playing = false; } } @@ -455,7 +455,7 @@ void FAudio_Output::resume() return; if (!playing) { - assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); + assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); playing = true; } } @@ -466,7 +466,7 @@ void FAudio_Output::reset() return; if (playing) { - assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == R_OK); + assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); } FAudioSourceVoice_FlushSourceBuffers(sVoice); @@ -482,7 +482,7 @@ void FAudio_Output::setThrottle(unsigned short throttle_) if (throttle_ == 0) throttle_ = 100; - assert(FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY) == R_OK); + assert(FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY) == 0); } void faudio_device_changed(FAudio_Output* instance) From 56056f1e9d40c813163543f7fbd6251ec865acce Mon Sep 17 00:00:00 2001 From: Zach Bacon Date: Wed, 31 Jan 2024 11:23:00 -0500 Subject: [PATCH 3/4] FAudio fix ups Slowly attempt to make FAudio implementation not use imme code Investigating a cross platform query hardware method Still a work in progress Signed-off-by: Zach Bacon --- src/wx/faudio.cpp | 148 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 38 deletions(-) diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index b5786723..568bb276 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -9,7 +9,7 @@ // FAudio #include - +#include #include #include @@ -20,20 +20,24 @@ 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 (FAudio_GetDeviceCount(fa, &dev_count) != 0) { + if (hr != 0) { wxLogError(_("FAudio: Enumerating devices failed!")); return true; } else { FAudioDeviceDetails dd; for (uint32_t i = 0; i < dev_count; i++) { - if (FAudio_GetDeviceDetails(fa, i, &dd) != 0) { + hr = FAudio_GetDeviceDetails(fa, i, &dd); + + if (hr != 0) { 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 + ids->push_back((wchar_t*) dd.DeviceID); names->push_back((wchar_t*) dd.DisplayName); } else if (*match == wxString((wchar_t*) dd.DeviceID)) return i; @@ -46,13 +50,15 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, bool GetFADevices(wxArrayString& names, wxArrayString& ids) { + uint32_t hr; FAudio* fa = NULL; uint32_t flags = 0; #ifdef _DEBUG flags = FAUDIO_DEBUG_ENGINE; #endif + hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR); - if (FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { + if (hr != 0) { wxLogError(_("The FAudio interface failed to initialize!")); return false; } @@ -78,47 +84,96 @@ static void faudio_device_changed(FAudio_Output*); class FAudio_Device_Notifier { std::wstring last_device; + pthread_mutex_t lock; + std::vector instances; public: - FAudio_Device_Notifier() = default; - ~FAudio_Device_Notifier() = default; + FAudio_Device_Notifier() + { + pthread_mutex_init(&lock, NULL); + } + + ~FAudio_Device_Notifier() + { + pthread_mutex_destroy(&lock); + } + + ULONG FAUDIOAPI AddRef() + { + return 1; + } + + ULONG FAUDIOAPI Release() + { + return 1; + } + + //uint32_t FAUDIOAPI QueryInterface or it should be, I still need to find a cross platform way to query devices. + + void OnDefaultDeviceChanged() // This is the callback that is called when the default device is changed. The XAudio2 driver leverages mmdeviceapi, but here we can't rely on that, so we need to find a method that work on all platforms. + { + + pthread_mutex_lock(&lock); + for (auto it = instances.begin(); it < instances.end(); ++it) + { + faudio_device_changed(*it); + } + pthread_mutex_unlock(&lock); + } void do_register(FAudio_Output* p_instance) { + pthread_mutex_lock(&lock); instances.push_back(p_instance); + pthread_mutex_unlock(&lock); } void do_unregister(FAudio_Output* p_instance) { + pthread_mutex_lock(&lock); + for (auto it = instances.begin(); it < instances.end(); ++it) { if (*it == p_instance) { instances.erase(it); break; } } + + pthread_mutex_unlock(&lock); } - void OnDefaultDeviceChanged() - { - for (auto it = instances.begin(); it < instances.end(); ++it) - { - faudio_device_changed(*it); - } - } + } g_notifier; // Synchronization Event class FAudio_BufferNotify : public FAudioVoiceCallback { public: - void OnBufferEnd(void* pBufferContext) {} + void *hBufferEndEvent; + + FAudio_BufferNotify() + { + hBufferEndEvent = NULL; + //I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now. + //hBufferEndEvent = pthread_cond_init(); + } + ~FAudio_BufferNotify() + { + //I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now. + //pthread_cond_destroy(hBufferEndEvent); + } + + void OnBufferEnd(void* pBufferContext) + { + + } void OnVoiceProcessingPassStart(uint32_t BytesRequired) {} void OnVoiceProcessingPassEnd() {} void OnStreamEnd() {} void OnBufferStart(void* pBufferContext) {} void OnLoopEnd(void* pBufferContext) {} - void OnVoiceError(void* pBufferContext, int Error) {} + void OnVoiceError(void* pBufferContext, uint32_t Error) {} }; - + // Class Declaration class FAudio_Output : public SoundDriver { @@ -226,13 +281,15 @@ bool FAudio_Output::init(long sampleRate) if (failed || initialized) return false; + uint32_t hr; // Initialize FAudio uint32_t flags = 0; - #ifdef _DEBUG - flags = FAUDIO_DEBUG_ENGINE; - #endif + //#ifdef _DEBUG + // flags = FAUDIO_DEBUG_ENGINE; + //#endif + hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR); - if (FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { + if (hr != 0) { wxLogError(_("The FAudio interface failed to initialize!")); failed = true; return false; @@ -255,8 +312,15 @@ bool FAudio_Output::init(long sampleRate) 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); - if (FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), NULL) != 0) { + if (hr != 0) { wxLogError(_("FAudio: Creating mastering voice failed!")); failed = true; return false; @@ -265,8 +329,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, &wfx, 0, 4.0f, ¬ify); - if (FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL) != 0) { + if (hr != 0) { wxLogError(_("FAudio: Creating source voice failed!")); failed = true; return false; @@ -372,14 +437,16 @@ bool FAudio_Output::init(long sampleRate) } if (matrixAvailable) { - assert(FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS) == 0); + hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS); + assert(hr == 0); } free(matrix); matrix = NULL; } - assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); + hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); playing = true; currentBuffer = 0; device_changed = false; @@ -417,16 +484,16 @@ void FAudio_Output::write(uint16_t* finalWave, int length) break; } //else { // the maximum number of buffers is currently queued - //if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) { + 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; - // } - //} else { + if (WaitForSingleObject(notify.hBufferEndEvent, 10000) == WAIT_TIMEOUT) { + device_changed = true; + } + } else { // drop current audio frame - // return; - //} - //} + return; + } + } } // copy & protect the audio data in own memory area while playing it @@ -435,7 +502,8 @@ void FAudio_Output::write(uint16_t* finalWave, int length) buf.pAudioData = &buffers[currentBuffer * soundBufferLen]; currentBuffer++; currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer - assert(FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL) == 0); + uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL); + assert(hr == 0); } void FAudio_Output::pause() @@ -444,7 +512,8 @@ void FAudio_Output::pause() return; if (playing) { - assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); + uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); playing = false; } } @@ -455,7 +524,8 @@ void FAudio_Output::resume() return; if (!playing) { - assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); + uint32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); playing = true; } } @@ -466,7 +536,8 @@ void FAudio_Output::reset() return; if (playing) { - assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); + uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW); + assert(hr == 0); } FAudioSourceVoice_FlushSourceBuffers(sVoice); @@ -482,7 +553,8 @@ void FAudio_Output::setThrottle(unsigned short throttle_) if (throttle_ == 0) throttle_ = 100; - assert(FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY) == 0); + uint32_t hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY); + assert(hr == 0); } void faudio_device_changed(FAudio_Output* instance) From d458f0f29e46984dd57eeb857332e4af16d55625 Mon Sep 17 00:00:00 2001 From: Zach Bacon Date: Mon, 4 Mar 2024 12:49:11 -0500 Subject: [PATCH 4/4] FAudio: Had to pass function pointers from the struct FAudioVoiceCallBack In Short, audio works. Just gotta deal with the windows event system pthreads might have what I need, winpthreads can even be used on windows Signed-off-by: Zach Bacon --- src/wx/faudio.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index 568bb276..d726d824 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -155,6 +155,13 @@ public: hBufferEndEvent = NULL; //I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now. //hBufferEndEvent = pthread_cond_init(); + 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() { @@ -162,16 +169,20 @@ public: //pthread_cond_destroy(hBufferEndEvent); } - void OnBufferEnd(void* pBufferContext) - { - + + static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void * pBufferContext) { + FAudio_BufferNotify* self = static_cast(callback); + if (self != nullptr && self->hBufferEndEvent != NULL) + { + SetEvent(self->hBufferEndEvent); + } } - void OnVoiceProcessingPassStart(uint32_t BytesRequired) {} - void OnVoiceProcessingPassEnd() {} - void OnStreamEnd() {} - void OnBufferStart(void* pBufferContext) {} - void OnLoopEnd(void* pBufferContext) {} - void OnVoiceError(void* pBufferContext, uint32_t 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 @@ -329,7 +340,7 @@ 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, &wfx, 0, 4.0f, ¬ify); + hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, ¬ify, NULL, NULL); if (hr != 0) { wxLogError(_("FAudio: Creating source voice failed!")); @@ -482,7 +493,7 @@ void FAudio_Output::write(uint16_t* finalWave, int length) // there is at least one free buffer break; - } //else { + } else { // the maximum number of buffers is currently queued if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) { // wait for one buffer to finish playing