22 #include "../../SDL_internal.h" 30 #if SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) 32 #include "../../core/windows/SDL_windows.h" 35 #include "../SDL_audio_c.h" 36 #include "../SDL_sysaudio.h" 41 #include <mmdeviceapi.h> 42 #include <audioclient.h> 46 static const ERole SDL_WASAPI_role = eConsole;
49 static IMMDeviceEnumerator *enumerator =
NULL;
52 #ifdef PropVariantInit 53 #undef PropVariantInit 55 #define PropVariantInit(p) SDL_zerop(p) 58 static HMODULE libavrt =
NULL;
59 typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPWSTR, LPDWORD);
60 typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
61 static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW =
NULL;
62 static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics =
NULL;
65 static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
66 static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
67 static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
68 static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
69 static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
70 static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
74 GetWasapiDeviceName(IMMDevice *
device)
81 if (
SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
83 PropVariantInit(&var);
84 if (
SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
87 PropVariantClear(&var);
88 IPropertyStore_Release(props);
99 typedef struct SDLMMNotificationClient
101 const IMMNotificationClientVtbl *lpVtbl;
103 } SDLMMNotificationClient;
105 static HRESULT STDMETHODCALLTYPE
106 SDLMMNotificationClient_QueryInterface(IMMNotificationClient *
this, REFIID iid,
void **ppv)
111 this->lpVtbl->AddRef(
this);
119 static ULONG STDMETHODCALLTYPE
120 SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
122 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
126 static ULONG STDMETHODCALLTYPE
127 SDLMMNotificationClient_Release(IMMNotificationClient *ithis)
130 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
140 static HRESULT STDMETHODCALLTYPE
141 SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
143 if (role != SDL_WASAPI_role) {
163 SDL_assert(!
"uhoh, unexpected OnDefaultDeviceChange flow!");
170 static HRESULT STDMETHODCALLTYPE
171 SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
181 static HRESULT STDMETHODCALLTYPE
182 SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
188 static HRESULT STDMETHODCALLTYPE
189 SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState)
191 IMMDevice *device =
NULL;
193 if (
SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
194 IMMEndpoint *endpoint =
NULL;
195 if (
SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (
void **) &endpoint))) {
197 if (
SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
198 const SDL_bool iscapture = (flow == eCapture);
199 if (dwNewState == DEVICE_STATE_ACTIVE) {
200 char *utf8dev = GetWasapiDeviceName(device);
209 IMMEndpoint_Release(endpoint);
211 IMMDevice_Release(device);
217 static HRESULT STDMETHODCALLTYPE
218 SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *
this, LPCWSTR pwstrDeviceId,
const PROPERTYKEY
key)
223 static const IMMNotificationClientVtbl notification_client_vtbl = {
224 SDLMMNotificationClient_QueryInterface,
225 SDLMMNotificationClient_AddRef,
226 SDLMMNotificationClient_Release,
227 SDLMMNotificationClient_OnDeviceStateChanged,
228 SDLMMNotificationClient_OnDeviceAdded,
229 SDLMMNotificationClient_OnDeviceRemoved,
230 SDLMMNotificationClient_OnDefaultDeviceChanged,
231 SDLMMNotificationClient_OnPropertyValueChanged
234 static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 } };
244 return SDL_SetError(
"WASAPI support requires Windows Vista or later");
251 ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator,
NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID) &enumerator);
257 libavrt = LoadLibraryW(L
"avrt.dll");
259 pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW) GetProcAddress(libavrt,
"AvSetMmThreadCharacteristicsW");
260 pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics) GetProcAddress(libavrt,
"AvRevertMmThreadCharacteristics");
270 IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
271 IMMDeviceEnumerator_Release(enumerator);
276 FreeLibrary(libavrt);
280 pAvSetMmThreadCharacteristicsW =
NULL;
281 pAvRevertMmThreadCharacteristics =
NULL;
291 this->hidden->coinitialized =
SDL_TRUE;
295 if (pAvSetMmThreadCharacteristicsW) {
297 this->hidden->task = pAvSetMmThreadCharacteristicsW(TEXT(
"Pro Audio"), &idx);
305 if (this->hidden->task && pAvRevertMmThreadCharacteristics) {
306 pAvRevertMmThreadCharacteristics(this->hidden->task);
307 this->hidden->task =
NULL;
310 if (this->hidden->coinitialized) {
319 LPCWSTR devid = this->hidden->devid;
320 IMMDevice *device =
NULL;
324 const EDataFlow dataflow = this->iscapture ? eCapture : eRender;
325 ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
327 ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, &device);
332 this->hidden->client =
NULL;
337 ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL,
NULL, (
void **) &this->hidden->client);
338 IMMDevice_Release(device);
355 WASAPI_EnumerateEndpointsForFlow(
const SDL_bool iscapture)
357 IMMDeviceCollection *collection =
NULL;
363 if (
FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
367 if (
FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
368 IMMDeviceCollection_Release(collection);
372 for (i = 0; i < total; i++) {
373 IMMDevice *device =
NULL;
374 if (
SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
376 if (
SUCCEEDED(IMMDevice_GetId(device, &devid))) {
377 char *devname = GetWasapiDeviceName(device);
382 CoTaskMemFree(devid);
384 IMMDevice_Release(device);
388 IMMDeviceCollection_Release(collection);
394 WASAPI_EnumerateEndpointsForFlow(
SDL_FALSE);
395 WASAPI_EnumerateEndpointsForFlow(
SDL_TRUE);
398 IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
405 SDL_assert(!
"This function should have only been called on WinRT.");
BOOL WIN_IsWindowsVistaOrGreater(void)
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
void WASAPI_BeginLoopIteration(_THIS)
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
void WASAPI_PlatformThreadDeinit(_THIS)
void WASAPI_PlatformDeinit(void)
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
static SDL_AudioDeviceID device
void WASAPI_EnumerateEndpoints(void)
HRESULT WIN_CoInitialize(void)
#define WIN_StringToUTF8(S)
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
int WASAPI_PlatformInit(void)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
#define SDL_assert(condition)
void WIN_CoUninitialize(void)
SDL_atomic_t WASAPI_DefaultCaptureGeneration
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
void WASAPI_PlatformDeleteActivationHandler(void *handler)
BOOL WIN_IsEqualIID(REFIID a, REFIID b)
void WASAPI_PlatformThreadInit(_THIS)
GLenum GLuint GLsizei const GLenum * props