21 #include "../../SDL_internal.h"
29 #if SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__)
31 #include "../../core/windows/SDL_windows.h"
34 #include "../SDL_audio_c.h"
35 #include "../SDL_sysaudio.h"
38 #include <mmdeviceapi.h>
39 #include <audioclient.h>
43 static const ERole SDL_WASAPI_role = eConsole;
46 static IMMDeviceEnumerator *enumerator =
NULL;
49 #ifdef PropVariantInit
50 #undef PropVariantInit
52 #define PropVariantInit(p) SDL_zerop(p)
55 static HMODULE libavrt =
NULL;
56 typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPWSTR, LPDWORD);
57 typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
58 static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW =
NULL;
59 static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics =
NULL;
62 static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
63 static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
64 static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
65 static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
66 static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
67 static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
71 GetWasapiDeviceName(IMMDevice *
device)
80 PropVariantInit(&var);
81 if (
SUCCEEDED(IPropertyStore_GetValue(
props, &SDL_PKEY_Device_FriendlyName, &var))) {
84 PropVariantClear(&var);
85 IPropertyStore_Release(
props);
96 typedef struct SDLMMNotificationClient
98 const IMMNotificationClientVtbl *lpVtbl;
100 } SDLMMNotificationClient;
102 static HRESULT STDMETHODCALLTYPE
103 SDLMMNotificationClient_QueryInterface(IMMNotificationClient *
this, REFIID iid,
void **ppv)
108 this->lpVtbl->AddRef(
this);
116 static ULONG STDMETHODCALLTYPE
117 SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
119 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
123 static ULONG STDMETHODCALLTYPE
124 SDLMMNotificationClient_Release(IMMNotificationClient *ithis)
127 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
137 static HRESULT STDMETHODCALLTYPE
138 SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
140 if (role != SDL_WASAPI_role) {
160 SDL_assert(!
"uhoh, unexpected OnDefaultDeviceChange flow!");
167 static HRESULT STDMETHODCALLTYPE
168 SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
178 static HRESULT STDMETHODCALLTYPE
179 SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
185 static HRESULT STDMETHODCALLTYPE
186 SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState)
190 if (
SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &
device))) {
191 IMMEndpoint *endpoint =
NULL;
192 if (
SUCCEEDED(IMMDevice_QueryInterface(
device, &SDL_IID_IMMEndpoint, (
void **) &endpoint))) {
194 if (
SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
195 const SDL_bool iscapture = (flow == eCapture);
196 if (dwNewState == DEVICE_STATE_ACTIVE) {
197 char *utf8dev = GetWasapiDeviceName(
device);
206 IMMEndpoint_Release(endpoint);
208 IMMDevice_Release(
device);
214 static HRESULT STDMETHODCALLTYPE
215 SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *
this, LPCWSTR pwstrDeviceId,
const PROPERTYKEY
key)
220 static const IMMNotificationClientVtbl notification_client_vtbl = {
221 SDLMMNotificationClient_QueryInterface,
222 SDLMMNotificationClient_AddRef,
223 SDLMMNotificationClient_Release,
224 SDLMMNotificationClient_OnDeviceStateChanged,
225 SDLMMNotificationClient_OnDeviceAdded,
226 SDLMMNotificationClient_OnDeviceRemoved,
227 SDLMMNotificationClient_OnDefaultDeviceChanged,
228 SDLMMNotificationClient_OnPropertyValueChanged
231 static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 } };
241 return SDL_SetError(
"WASAPI support requires Windows Vista or later");
248 ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator,
NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *) &enumerator);
254 libavrt = LoadLibraryW(L
"avrt.dll");
256 pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW) GetProcAddress(libavrt,
"AvSetMmThreadCharacteristicsW");
257 pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics) GetProcAddress(libavrt,
"AvRevertMmThreadCharacteristics");
267 IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
268 IMMDeviceEnumerator_Release(enumerator);
273 FreeLibrary(libavrt);
277 pAvSetMmThreadCharacteristicsW =
NULL;
278 pAvRevertMmThreadCharacteristics =
NULL;
288 this->hidden->coinitialized =
SDL_TRUE;
292 if (pAvSetMmThreadCharacteristicsW) {
294 this->hidden->task = pAvSetMmThreadCharacteristicsW(TEXT(
"Pro Audio"), &
idx);
302 if (this->hidden->task && pAvRevertMmThreadCharacteristics) {
303 pAvRevertMmThreadCharacteristics(this->hidden->task);
304 this->hidden->task =
NULL;
307 if (this->hidden->coinitialized) {
316 LPCWSTR devid = this->hidden->devid;
321 const EDataFlow dataflow = this->iscapture ? eCapture : eRender;
322 ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &
device);
324 ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, &
device);
329 this->hidden->client =
NULL;
334 ret = IMMDevice_Activate(
device, &SDL_IID_IAudioClient, CLSCTX_ALL,
NULL, (
void **) &this->hidden->client);
335 IMMDevice_Release(
device);
357 static int sort_endpoints(
const void *_a,
const void *_b)
359 LPWSTR
a = ((
const EndpointItem *) _a)->devid;
360 LPWSTR
b = ((
const EndpointItem *) _b)->devid;
363 }
else if (
a && !
b) {
370 }
else if (*
a > *
b) {
372 }
else if (*
a == 0) {
383 WASAPI_EnumerateEndpointsForFlow(
const SDL_bool iscapture)
385 IMMDeviceCollection *collection =
NULL;
392 if (
FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
396 if (
FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
397 IMMDeviceCollection_Release(collection);
401 items = (EndpointItem *)
SDL_calloc(total,
sizeof (EndpointItem));
406 for (
i = 0;
i < total;
i++) {
407 EndpointItem *item = items +
i;
411 item->devname = GetWasapiDeviceName(
device);
413 IMMDevice_Release(
device);
418 SDL_qsort(items, total,
sizeof (*items), sort_endpoints);
421 for (
i = 0;
i < total;
i++) {
422 EndpointItem *item = items +
i;
423 if ((item->devid) && (item->devname)) {
427 CoTaskMemFree(item->devid);
431 IMMDeviceCollection_Release(collection);
437 WASAPI_EnumerateEndpointsForFlow(
SDL_FALSE);
438 WASAPI_EnumerateEndpointsForFlow(
SDL_TRUE);
441 IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
448 SDL_assert(!
"This function should have only been called on WinRT.");
#define SDL_assert(condition)
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
GLboolean GLboolean GLboolean b
GLenum GLuint GLsizei const GLenum * props
GLboolean GLboolean GLboolean GLboolean a
void WASAPI_PlatformDeleteActivationHandler(void *handler)
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
void WASAPI_PlatformThreadDeinit(_THIS)
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
SDL_atomic_t WASAPI_DefaultCaptureGeneration
void WASAPI_PlatformThreadInit(_THIS)
int WASAPI_PlatformInit(void)
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
void WASAPI_EnumerateEndpoints(void)
void WASAPI_PlatformDeinit(void)
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
HRESULT WIN_CoInitialize(void)
void WIN_CoUninitialize(void)
BOOL WIN_IsWindowsVistaOrGreater(void)
BOOL WIN_IsEqualIID(REFIID a, REFIID b)
#define WIN_StringToUTF8(S)
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
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)
static SDL_AudioDeviceID device
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
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...