From d5afcf7ab1cd52d14a120c3857fff5801f063d9c Mon Sep 17 00:00:00 2001 From: Marcelo Fernandez Date: Fri, 19 Jan 2018 17:55:20 -0300 Subject: [PATCH] Improved WASAPI driver so that it always uses the default audio device --- drivers/wasapi/audio_driver_wasapi.cpp | 88 +++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 4c80e70a95a..36acfb10d14 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -40,6 +40,76 @@ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); +static bool default_device_changed = false; + +class CMMNotificationClient : public IMMNotificationClient { + LONG _cRef; + IMMDeviceEnumerator *_pEnumerator; + +public: + CMMNotificationClient() : + _cRef(1), + _pEnumerator(NULL) {} + ~CMMNotificationClient() { + if ((_pEnumerator) != NULL) { + (_pEnumerator)->Release(); + (_pEnumerator) = NULL; + } + } + + ULONG STDMETHODCALLTYPE AddRef() { + return InterlockedIncrement(&_cRef); + } + + ULONG STDMETHODCALLTYPE Release() { + ULONG ulRef = InterlockedDecrement(&_cRef); + if (0 == ulRef) { + delete this; + } + return ulRef; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) { + if (IID_IUnknown == riid) { + AddRef(); + *ppvInterface = (IUnknown *)this; + } else if (__uuidof(IMMNotificationClient) == riid) { + AddRef(); + *ppvInterface = (IMMNotificationClient *)this; + } else { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + 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 OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { + if (flow == eRender && role == eConsole) { + default_device_changed = true; + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + return S_OK; + } +}; + +static CMMNotificationClient notif_client; + Error AudioDriverWASAPI::init_device(bool reinit) { WAVEFORMATEX *pwfex; @@ -54,7 +124,7 @@ Error AudioDriverWASAPI::init_device(bool reinit) { hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, - // otherwise if there is currently no devie available this will spam the console. + // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; } @@ -62,6 +132,11 @@ Error AudioDriverWASAPI::init_device(bool reinit) { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } + hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); + if (hr != S_OK) { + ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); + } + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audio_client); if (reinit) { if (hr != S_OK) { @@ -148,6 +223,8 @@ Error AudioDriverWASAPI::finish_device() { if (audio_client) { if (active) { audio_client->Stop(); + audio_client->Release(); + audio_client = NULL; active = false; } } @@ -323,6 +400,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } + if (default_device_changed) { + Error err = ad->finish_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_device error"); + } + + default_device_changed = false; + } + if (!ad->audio_client) { Error err = ad->init_device(true); if (err == OK) {