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);
360 static int sort_endpoints(
const void *_a,
const void *_b)
362 LPWSTR
a = ((
const EndpointItem *) _a)->devid;
363 LPWSTR
b = ((
const EndpointItem *) _b)->devid;
366 }
else if (a && !b) {
373 }
else if (*a > *b) {
375 }
else if (*a == 0) {
386 WASAPI_EnumerateEndpointsForFlow(
const SDL_bool iscapture)
388 IMMDeviceCollection *collection =
NULL;
395 if (
FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
399 if (
FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
400 IMMDeviceCollection_Release(collection);
404 items = (EndpointItem *)
SDL_calloc(total,
sizeof (EndpointItem));
409 for (i = 0; i < total; i++) {
410 EndpointItem *item = items +
i;
411 IMMDevice *device =
NULL;
412 if (
SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
413 if (
SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
414 item->devname = GetWasapiDeviceName(device);
416 IMMDevice_Release(device);
421 SDL_qsort(items, total,
sizeof (*items), sort_endpoints);
424 for (i = 0; i < total; i++) {
425 EndpointItem *item = items +
i;
426 if ((item->devid) && (item->devname)) {
430 CoTaskMemFree(item->devid);
434 IMMDeviceCollection_Release(collection);
440 WASAPI_EnumerateEndpointsForFlow(
SDL_FALSE);
441 WASAPI_EnumerateEndpointsForFlow(
SDL_TRUE);
444 IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
451 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 ...
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)
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 idx
void WASAPI_PlatformThreadInit(_THIS)
GLenum GLuint GLsizei const GLenum * props
GLboolean GLboolean GLboolean GLboolean a
GLboolean GLboolean GLboolean b