diff --git a/src/wx/faudio.cpp b/src/wx/faudio.cpp index a49bec67..d726d824 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/faudio.cpp @@ -8,10 +8,8 @@ #include "../common/SoundDriver.h" // FAudio -#include - -// MMDevice API -#include +#include +#include #include #include @@ -22,27 +20,26 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, const wxString* match) { - HRESULT hr; - UINT32 dev_count = 0; - + uint32_t hr; + uint32_t dev_count = 0; hr = FAudio_GetDeviceCount(fa, &dev_count); - if (hr != S_OK) { + if (hr != 0) { wxLogError(_("FAudio: Enumerating devices failed!")); return true; } else { FAudioDeviceDetails dd; - for (UINT32 i = 0; i < dev_count; i++) { + for (uint32_t i = 0; i < dev_count; i++) { hr = FAudio_GetDeviceDetails(fa, i, &dd); - if (hr != S_OK) { + 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 == dd.DeviceID) + } else if (*match == wxString((wchar_t*) dd.DeviceID)) return i; } } @@ -53,22 +50,20 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, 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; } @@ -87,98 +82,56 @@ 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; + pthread_mutex_t lock; + std::vector instances; public: FAudio_Device_Notifier() - : registered(0) { - InitializeCriticalSection(&lock); + pthread_mutex_init(&lock, NULL); } + ~FAudio_Device_Notifier() { - DeleteCriticalSection(&lock); + pthread_mutex_destroy(&lock); } - ULONG STDMETHODCALLTYPE AddRef() + ULONG FAUDIOAPI AddRef() { return 1; } - ULONG STDMETHODCALLTYPE Release() + ULONG FAUDIOAPI Release() { return 1; } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) + //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. { - if (IID_IUnknown == riid) { - *ppvInterface = (IUnknown*)this; - } else if (__uuidof(IMMNotificationClient) == riid) { - *ppvInterface = (IMMNotificationClient*)this; - } else { - *ppvInterface = NULL; - return E_NOINTERFACE; + + pthread_mutex_lock(&lock); + for (auto it = instances.begin(); it < instances.end(); ++it) + { + faudio_device_changed(*it); } - - return S_OK; + pthread_mutex_unlock(&lock); } - 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; } - 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); + pthread_mutex_lock(&lock); instances.push_back(p_instance); - LeaveCriticalSection(&lock); + pthread_mutex_unlock(&lock); } void do_unregister(FAudio_Output* p_instance) { - if (InterlockedDecrement(®istered) == 0) { - if (pEnumerator) { - pEnumerator->UnregisterEndpointNotificationCallback(this); - pEnumerator->Release(); - pEnumerator = NULL; - } - } - - EnterCriticalSection(&lock); + pthread_mutex_lock(&lock); for (auto it = instances.begin(); it < instances.end(); ++it) { if (*it == p_instance) { @@ -186,51 +139,52 @@ public: break; } } - - LeaveCriticalSection(&lock); + + pthread_mutex_unlock(&lock); } + } g_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); + //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() { - CloseHandle(hBufferEndEvent); - hBufferEndEvent = NULL; + //I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now. + //pthread_cond_destroy(hBufferEndEvent); } - 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 +212,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 +242,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 +259,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 +292,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 +312,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; @@ -388,9 +340,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 +351,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 +448,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 +457,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 +467,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 +498,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 +508,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 +523,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 +535,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 +547,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 +564,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)