From b90641a2423f6a1aebbea2ad23dd174628d64bf7 Mon Sep 17 00:00:00 2001 From: L zard Date: Fri, 8 Nov 2024 15:23:52 +0100 Subject: [PATCH] Merge SDL_wasapi_win32 into SDL_wasapi --- VisualC-GDK/SDL/SDL.vcxproj | 1 - VisualC-GDK/SDL/SDL.vcxproj.filters | 1 - VisualC/SDL/SDL.vcxproj | 1 - VisualC/SDL/SDL.vcxproj.filters | 3 - src/audio/wasapi/SDL_wasapi.c | 141 ++++++++++++++++++-- src/audio/wasapi/SDL_wasapi.h | 11 -- src/audio/wasapi/SDL_wasapi_win32.c | 200 ---------------------------- 7 files changed, 130 insertions(+), 228 deletions(-) delete mode 100644 src/audio/wasapi/SDL_wasapi_win32.c diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index c779b40ccea90..168802386616a 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -616,7 +616,6 @@ - diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index 46d3b47750832..b073c50fe0889 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -35,7 +35,6 @@ - diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index a6371238f965c..382c1b3cd922b 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -522,7 +522,6 @@ - diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index ada794006fb9c..c9075d11f15c1 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -1178,9 +1178,6 @@ audio\dummy - - audio\wasapi - audio\wasapi diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c index 6c8eee6cae54f..df048dcaf8f15 100644 --- a/src/audio/wasapi/SDL_wasapi.c +++ b/src/audio/wasapi/SDL_wasapi.c @@ -43,13 +43,22 @@ #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 #endif +// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). +static HMODULE libavrt = NULL; +typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); +typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); +static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; +static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; + // Some GUIDs we need to know without linking to libraries that aren't available before Vista. static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; +static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; #ifdef __IAudioClient3_INTERFACE_DEFINED__ static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } }; #endif // +static bool immdevice_initialized = false; // WASAPI is _really_ particular about various things happening on the same thread, for COM and such, // so we proxy various stuff to a single background thread to manage. @@ -155,15 +164,83 @@ bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, b return true; // successfully added (and possibly executed)! } +static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + SDL_AudioDeviceDisconnected(device); + UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. + return true; +} + +static void AudioDeviceDisconnected(SDL_AudioDevice *device) +{ + // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. + if (device) { + RefPhysicalAudioDevice(device); // make sure this lives until the task completes. + WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL); + } +} + +static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + SDL_DefaultAudioDeviceChanged(device); + UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. + return true; +} + +static void DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) +{ + // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. + if (new_default_device) { + RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes. + WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL); + } +} + +static void StopWasapiHotplug(void) +{ + if (immdevice_initialized) { + SDL_IMMDevice_Quit(); + immdevice_initialized = false; + } +} + +static void Deinit(void) +{ + if (libavrt) { + FreeLibrary(libavrt); + libavrt = NULL; + } + + pAvSetMmThreadCharacteristicsW = NULL; + pAvRevertMmThreadCharacteristics = NULL; + + StopWasapiHotplug(); + + WIN_CoUninitialize(); +} + static bool ManagementThreadPrepare(void) { - if (!WASAPI_PlatformInit()) { - return false; + const SDL_IMMDevice_callbacks callbacks = { AudioDeviceDisconnected, DefaultAudioDeviceChanged }; + if (FAILED(WIN_CoInitialize())) { + return SDL_SetError("CoInitialize() failed"); + } else if (!SDL_IMMDevice_Init(&callbacks)) { + return false; // Error string is set by SDL_IMMDevice_Init + } + + immdevice_initialized = true; + + libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! + if (libavrt) { + pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); + pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); } ManagementThreadLock = SDL_CreateMutex(); if (!ManagementThreadLock) { - WASAPI_PlatformDeinit(); + Deinit(); return false; } @@ -171,7 +248,7 @@ static bool ManagementThreadPrepare(void) if (!ManagementThreadCondition) { SDL_DestroyMutex(ManagementThreadLock); ManagementThreadLock = NULL; - WASAPI_PlatformDeinit(); + Deinit(); return false; } @@ -197,7 +274,7 @@ static int ManagementThreadEntry(void *userdata) SDL_SignalSemaphore(data->ready_sem); // unblock calling thread. ManagementThreadMainloop(); - WASAPI_PlatformDeinit(); + Deinit(); return 0; } @@ -260,7 +337,7 @@ typedef struct static bool mgmtthrtask_DetectDevices(void *userdata) { mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata; - WASAPI_EnumerateEndpoints(data->default_playback, data->default_recording); + SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording); return true; } @@ -373,7 +450,29 @@ static void ResetWasapiDevice(SDL_AudioDevice *device) static bool mgmtthrtask_ActivateDevice(void *userdata) { - return WASAPI_ActivateDevice((SDL_AudioDevice *)userdata); + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + + IMMDevice *immdevice = NULL; + if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) { + device->hidden->client = NULL; + return false; // This is already set by SDL_IMMDevice_Get + } + + // this is _not_ async in standard win32, yay! + HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client); + IMMDevice_Release(immdevice); + + if (FAILED(ret)) { + SDL_assert(device->hidden->client == NULL); + return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); + } + + SDL_assert(device->hidden->client != NULL); + if (!WASAPI_PrepDevice(device)) { // not async, fire it right away. + return false; + } + + return true; // good to go. } static bool ActivateWasapiDevice(SDL_AudioDevice *device) @@ -773,17 +872,37 @@ static bool WASAPI_OpenDevice(SDL_AudioDevice *device) static void WASAPI_ThreadInit(SDL_AudioDevice *device) { - WASAPI_PlatformThreadInit(device); + // this thread uses COM. + if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked! + device->hidden->coinitialized = true; + } + + // Set this thread to very high "Pro Audio" priority. + if (pAvSetMmThreadCharacteristicsW) { + DWORD idx = 0; + device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); + } else { + SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); + } } static void WASAPI_ThreadDeinit(SDL_AudioDevice *device) { - WASAPI_PlatformThreadDeinit(device); + // Set this thread back to normal priority. + if (device->hidden->task && pAvRevertMmThreadCharacteristics) { + pAvRevertMmThreadCharacteristics(device->hidden->task); + device->hidden->task = NULL; + } + + if (device->hidden->coinitialized) { + WIN_CoUninitialize(); + device->hidden->coinitialized = false; + } } static bool mgmtthrtask_FreeDeviceHandle(void *userdata) { - WASAPI_PlatformFreeDeviceHandle((SDL_AudioDevice *)userdata); + SDL_IMMDevice_FreeDeviceHandle((SDL_AudioDevice *) userdata); return true; } @@ -795,7 +914,7 @@ static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device) static bool mgmtthrtask_DeinitializeStart(void *userdata) { - WASAPI_PlatformDeinitializeStart(); + StopWasapiHotplug(); return true; } diff --git a/src/audio/wasapi/SDL_wasapi.h b/src/audio/wasapi/SDL_wasapi.h index 3d4744cf19319..6c5f45f42ef6f 100644 --- a/src/audio/wasapi/SDL_wasapi.h +++ b/src/audio/wasapi/SDL_wasapi.h @@ -53,17 +53,6 @@ void WASAPI_DisconnectDevice(SDL_AudioDevice *device); // don't hold the device typedef bool (*ManagementThreadTask)(void *userdata); bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, bool *wait_until_complete); -// These are functions that are (were...?) implemented differently for various Windows versions. -// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD. -bool WASAPI_PlatformInit(void); -void WASAPI_PlatformDeinit(void); -void WASAPI_PlatformDeinitializeStart(void); -void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); -bool WASAPI_ActivateDevice(SDL_AudioDevice *device); -void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread. -void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread. -void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device); - #ifdef __cplusplus } #endif diff --git a/src/audio/wasapi/SDL_wasapi_win32.c b/src/audio/wasapi/SDL_wasapi_win32.c deleted file mode 100644 index 5ef776d43e1ba..0000000000000 --- a/src/audio/wasapi/SDL_wasapi_win32.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -/* !!! FIXME: merge this all into SDL_wasapi.c, now that WinRT is gone. - This is code that Windows uses to talk to WASAPI-related system APIs. - This is for non-WinRT desktop apps. The C++/CX implementation of these - functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp. - The code in SDL_wasapi.c is used by both standard Windows and WinRT builds - to deal with audio and calls into these functions. */ - -#if defined(SDL_AUDIO_DRIVER_WASAPI) - -#include "../../core/windows/SDL_windows.h" -#include "../../core/windows/SDL_immdevice.h" -#include "../SDL_sysaudio.h" - -#include - -#include "SDL_wasapi.h" - -// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). -static HMODULE libavrt = NULL; -typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); -typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); -static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; -static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; - -static bool immdevice_initialized = false; - -// Some GUIDs we need to know without linking to libraries that aren't available before Vista. -static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; - -static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_AudioDeviceDisconnected(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void WASAPI_AudioDeviceDisconnected(SDL_AudioDevice *device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (device) { - RefPhysicalAudioDevice(device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL); - } -} - -static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_DefaultAudioDeviceChanged(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void WASAPI_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (new_default_device) { - RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL); - } -} - -bool WASAPI_PlatformInit(void) -{ - const SDL_IMMDevice_callbacks callbacks = { WASAPI_AudioDeviceDisconnected, WASAPI_DefaultAudioDeviceChanged }; - if (FAILED(WIN_CoInitialize())) { - return SDL_SetError("CoInitialize() failed"); - } else if (!SDL_IMMDevice_Init(&callbacks)) { - return false; // Error string is set by SDL_IMMDevice_Init - } - - immdevice_initialized = true; - - libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! - if (libavrt) { - pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); - pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); - } - - return true; -} - -static void StopWasapiHotplug(void) -{ - if (immdevice_initialized) { - SDL_IMMDevice_Quit(); - immdevice_initialized = false; - } -} - -void WASAPI_PlatformDeinit(void) -{ - if (libavrt) { - FreeLibrary(libavrt); - libavrt = NULL; - } - - pAvSetMmThreadCharacteristicsW = NULL; - pAvRevertMmThreadCharacteristics = NULL; - - StopWasapiHotplug(); - - WIN_CoUninitialize(); -} - -void WASAPI_PlatformDeinitializeStart(void) -{ - StopWasapiHotplug(); -} - -void WASAPI_PlatformThreadInit(SDL_AudioDevice *device) -{ - // this thread uses COM. - if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked! - device->hidden->coinitialized = true; - } - - // Set this thread to very high "Pro Audio" priority. - if (pAvSetMmThreadCharacteristicsW) { - DWORD idx = 0; - device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); - } else { - SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); - } - -} - -void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device) -{ - // Set this thread back to normal priority. - if (device->hidden->task && pAvRevertMmThreadCharacteristics) { - pAvRevertMmThreadCharacteristics(device->hidden->task); - device->hidden->task = NULL; - } - - if (device->hidden->coinitialized) { - WIN_CoUninitialize(); - device->hidden->coinitialized = false; - } -} - -bool WASAPI_ActivateDevice(SDL_AudioDevice *device) -{ - IMMDevice *immdevice = NULL; - if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) { - device->hidden->client = NULL; - return false; // This is already set by SDL_IMMDevice_Get - } - - // this is _not_ async in standard win32, yay! - HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client); - IMMDevice_Release(immdevice); - - if (FAILED(ret)) { - SDL_assert(device->hidden->client == NULL); - return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); - } - - SDL_assert(device->hidden->client != NULL); - if (!WASAPI_PrepDevice(device)) { // not async, fire it right away. - return false; - } - - return true; // good to go. -} - -void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording); -} - -void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device) -{ - SDL_IMMDevice_FreeDeviceHandle(device); -} - -#endif // SDL_AUDIO_DRIVER_WASAPI