22 #include "../../SDL_internal.h" 24 #if SDL_AUDIO_DRIVER_WASAPI 26 #include "../../core/windows/SDL_windows.h" 29 #include "../SDL_audio_c.h" 30 #include "../SDL_sysaudio.h" 35 #include <mmdeviceapi.h> 36 #include <audioclient.h> 40 static const ERole SDL_WASAPI_role = eConsole;
43 static IMMDeviceEnumerator *enumerator =
NULL;
50 typedef struct DevIdList
53 struct DevIdList *next;
56 static DevIdList *deviceid_list =
NULL;
60 static HMODULE libavrt =
NULL;
62 typedef HANDLE (WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPWSTR,LPDWORD);
63 typedef BOOL (WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
64 static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW =
NULL;
65 static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics =
NULL;
68 static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, { 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
69 static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, { 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
70 static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85, { 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
71 static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089, { 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
72 static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
73 static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
74 static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
75 static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
76 static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
77 static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
80 #ifdef PropVariantInit 81 #undef PropVariantInit 83 #define PropVariantInit(p) SDL_zerop(p) 85 static void AddWASAPIDevice(
const SDL_bool iscapture, IMMDevice *
device, LPCWSTR devid);
86 static void RemoveWASAPIDevice(
const SDL_bool iscapture, LPCWSTR devid);
93 typedef struct SDLMMNotificationClient
95 const IMMNotificationClientVtbl *lpVtbl;
97 } SDLMMNotificationClient;
99 static HRESULT STDMETHODCALLTYPE
100 SDLMMNotificationClient_QueryInterface(IMMNotificationClient *
this, REFIID iid,
void **ppv)
105 this->lpVtbl->AddRef(
this);
113 static ULONG STDMETHODCALLTYPE
114 SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
116 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
120 static ULONG STDMETHODCALLTYPE
121 SDLMMNotificationClient_Release(IMMNotificationClient *ithis)
124 SDLMMNotificationClient *
this = (SDLMMNotificationClient *) ithis;
134 static HRESULT STDMETHODCALLTYPE
135 SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
137 if (role != SDL_WASAPI_role) {
157 SDL_assert(!
"uhoh, unexpected OnDefaultDeviceChange flow!");
164 static HRESULT STDMETHODCALLTYPE
165 SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
175 static HRESULT STDMETHODCALLTYPE
176 SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
182 static HRESULT STDMETHODCALLTYPE
183 SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState)
187 if (
SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
188 IMMEndpoint *endpoint =
NULL;
189 if (
SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (
void **) &endpoint))) {
191 if (
SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
192 const SDL_bool iscapture = (flow == eCapture);
193 if (dwNewState == DEVICE_STATE_ACTIVE) {
194 AddWASAPIDevice(iscapture, device, pwstrDeviceId);
196 RemoveWASAPIDevice(iscapture, pwstrDeviceId);
199 IMMEndpoint_Release(endpoint);
201 IMMDevice_Release(device);
207 static HRESULT STDMETHODCALLTYPE
208 SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *
this, LPCWSTR pwstrDeviceId,
const PROPERTYKEY
key)
213 static const IMMNotificationClientVtbl notification_client_vtbl = {
214 SDLMMNotificationClient_QueryInterface,
215 SDLMMNotificationClient_AddRef,
216 SDLMMNotificationClient_Release,
217 SDLMMNotificationClient_OnDeviceStateChanged,
218 SDLMMNotificationClient_OnDeviceAdded,
219 SDLMMNotificationClient_OnDeviceRemoved,
220 SDLMMNotificationClient_OnDefaultDeviceChanged,
221 SDLMMNotificationClient_OnPropertyValueChanged
224 static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 } };
227 WStrEqual(
const WCHAR *
a,
const WCHAR *
b)
240 WStrDupe(
const WCHAR *wstr)
242 const int len = (lstrlenW(wstr) + 1) *
sizeof (WCHAR);
251 RemoveWASAPIDevice(
const SDL_bool iscapture, LPCWSTR devid)
255 DevIdList *prev =
NULL;
256 for (i = deviceid_list;
i; i = next) {
258 if (WStrEqual(i->str, devid)) {
262 deviceid_list = next;
273 AddWASAPIDevice(
const SDL_bool iscapture, IMMDevice *device, LPCWSTR devid)
276 char *utf8dev =
NULL;
277 DevIdList *devidlist;
290 for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
291 if (WStrEqual(devidlist->str, devid)) {
296 devidlist = (DevIdList *)
SDL_malloc(
sizeof (*devidlist));
301 devid = WStrDupe(devid);
307 devidlist->str = (WCHAR *) devid;
308 devidlist->next = deviceid_list;
309 deviceid_list = devidlist;
311 if (
SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
312 PropVariantInit(&var);
313 if (
SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
320 PropVariantClear(&var);
321 IPropertyStore_Release(props);
326 EnumerateEndpoints(
const SDL_bool iscapture)
328 IMMDeviceCollection *collection =
NULL;
334 if (
FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
338 if (
FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
339 IMMDeviceCollection_Release(collection);
343 for (i = 0; i < total; i++) {
344 IMMDevice *device =
NULL;
345 if (
SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
347 if (
SUCCEEDED(IMMDevice_GetId(device, &devid))) {
348 AddWASAPIDevice(iscapture, device, devid);
349 CoTaskMemFree(devid);
351 IMMDevice_Release(device);
355 IMMDeviceCollection_Release(collection);
359 WASAPI_DetectDevices(
void)
365 IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
369 WASAPI_GetPendingBytes(
_THIS)
375 if (!this->hidden->client ||
FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
379 return ((
int) frames) * this->hidden->framesize;
383 WasapiFailed(
_THIS,
const HRESULT err)
389 if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
390 this->hidden->device_lost =
SDL_TRUE;
392 IAudioClient_Stop(this->hidden->client);
400 static int PrepWasapiDevice(
_THIS,
const int iscapture, IMMDevice *device);
401 static void ReleaseWasapiDevice(
_THIS);
404 RecoverWasapiDevice(
_THIS)
407 IMMDevice *device =
NULL;
410 if (this->hidden->default_device_generation) {
411 const EDataFlow dataflow = this->iscapture ? eCapture : eRender;
412 ReleaseWasapiDevice(
this);
413 this->hidden->default_device_generation =
SDL_AtomicGet(this->iscapture ? &default_capture_generation : &default_playback_generation);
414 ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
419 device = this->hidden->device;
420 this->hidden->device =
NULL;
421 ReleaseWasapiDevice(
this);
431 if (PrepWasapiDevice(
this, this->iscapture, device) == -1) {
440 if ( (this->callbackspec.channels == this->spec.channels) &&
441 (this->callbackspec.format == this->spec.format) &&
442 (this->callbackspec.freq == this->spec.freq) &&
443 (this->callbackspec.samples == this->spec.samples) ) {
447 }
else if ( (oldspec.
channels == this->spec.channels) &&
448 (oldspec.
format == this->spec.format) &&
449 (oldspec.
freq == this->spec.freq) ) {
454 if (this->iscapture) {
456 this->spec.channels, this->spec.freq,
457 this->callbackspec.format,
458 this->callbackspec.channels,
459 this->callbackspec.freq);
462 this->callbackspec.channels,
463 this->callbackspec.freq, this->spec.format,
464 this->spec.channels, this->spec.freq);
473 if (this->
spec.
size > this->work_buffer_len) {
479 this->work_buffer = ptr;
480 this->work_buffer_len = this->
spec.
size;
489 RecoverWasapiIfLost(
_THIS)
491 const int generation = this->hidden->default_device_generation;
492 SDL_bool lost = this->hidden->device_lost;
498 if (!lost && (generation > 0)) {
499 const int newgen =
SDL_AtomicGet(this->iscapture ? &default_capture_generation : &default_playback_generation);
500 if (generation != newgen) {
505 return lost ? RecoverWasapiDevice(
this) :
SDL_TRUE;
509 WASAPI_GetDeviceBuf(
_THIS)
514 while (RecoverWasapiIfLost(
this)) {
515 if (!WasapiFailed(
this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
525 WASAPI_PlayDevice(
_THIS)
528 WasapiFailed(
this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
532 WASAPI_WaitDevice(
_THIS)
535 while (RecoverWasapiIfLost(
this)) {
538 if (!WasapiFailed(
this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
539 if (padding <= maxpadding) {
549 WASAPI_CaptureFromDevice(
_THIS,
void *buffer,
int buflen)
554 const int cpy =
SDL_min(buflen, avail);
559 while (RecoverWasapiIfLost(
this)) {
565 ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags,
NULL,
NULL);
566 if (ret != AUDCLNT_S_BUFFER_EMPTY) {
567 WasapiFailed(
this, ret);
570 if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
571 WASAPI_WaitDevice(
this);
572 }
else if (ret ==
S_OK) {
573 const int total = ((int) frames) * this->hidden->framesize;
574 const int cpy =
SDL_min(buflen, total);
575 const int leftover = total - cpy;
595 ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
596 WasapiFailed(
this, ret);
606 WASAPI_FlushCapture(
_THIS)
614 const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags,
NULL,
NULL);
615 if (ret == AUDCLNT_S_BUFFER_EMPTY) {
617 }
else if (WasapiFailed(
this, ret)) {
619 }
else if (WasapiFailed(
this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
627 ReleaseWasapiDevice(
_THIS)
629 if (this->hidden->client) {
630 IAudioClient_Stop(this->hidden->client);
631 this->hidden->client =
NULL;
634 if (this->hidden->render) {
635 IAudioRenderClient_Release(this->hidden->render);
636 this->hidden->render =
NULL;
639 if (this->hidden->capture) {
640 IAudioCaptureClient_Release(this->hidden->capture);
641 this->hidden->capture =
NULL;
644 if (this->hidden->waveformat) {
645 CoTaskMemFree(this->hidden->waveformat);
646 this->hidden->waveformat =
NULL;
649 if (this->hidden->device) {
650 IMMDevice_Release(this->hidden->device);
651 this->hidden->device =
NULL;
654 if (this->hidden->capturestream) {
656 this->hidden->capturestream =
NULL;
661 WASAPI_CloseDevice(
_THIS)
666 ReleaseWasapiDevice(
this);
672 PrepWasapiDevice(
_THIS,
const int iscapture, IMMDevice *device)
685 const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
687 REFERENCE_TIME duration = 0;
688 IAudioClient *client =
NULL;
690 IAudioCaptureClient *capture =
NULL;
691 WAVEFORMATEX *waveformat =
NULL;
697 this->hidden->device =
device;
699 ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL,
NULL, (
void **) &client);
705 this->hidden->client = client;
707 ret = IAudioClient_GetMixFormat(client, &waveformat);
713 this->hidden->waveformat = waveformat;
717 this->spec.freq = waveformat->nSamplesPerSec;
720 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
722 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
724 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
726 }
else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
727 const WAVEFORMATEXTENSIBLE *ext = (
const WAVEFORMATEXTENSIBLE *) waveformat;
728 if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
730 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
732 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
737 while ((!valid_format) && (test_format)) {
738 if (test_format == wasapi_format) {
747 return SDL_SetError(
"WASAPI: Unsupported audio format");
750 ret = IAudioClient_GetDevicePeriod(client,
NULL, &duration);
755 ret = IAudioClient_Initialize(client, sharemode, 0, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, waveformat,
NULL);
760 ret = IAudioClient_GetBufferSize(client, &bufsize);
776 this->hidden->capturestream =
SDL_NewAudioStream(this->
spec.
format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
777 if (!this->hidden->capturestream) {
781 ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (
void**) &capture);
787 this->hidden->capture = capture;
788 ret = IAudioClient_Start(client);
793 WASAPI_FlushCapture(
this);
795 ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (
void**) &render);
801 this->hidden->render =
render;
802 ret = IAudioClient_Start(client);
812 WASAPI_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
815 IMMDevice *device =
NULL;
821 if (this->hidden ==
NULL) {
826 if (is_default_device) {
827 const EDataFlow dataflow = iscapture ? eCapture : eRender;
828 this->hidden->default_device_generation =
SDL_AtomicGet(iscapture ? &default_capture_generation : &default_playback_generation);
829 ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
831 ret = IMMDeviceEnumerator_GetDevice(enumerator, (LPCWSTR) handle, &device);
839 return PrepWasapiDevice(
this, iscapture, device);
843 WASAPI_ThreadInit(
_THIS)
847 this->hidden->coinitialized =
SDL_TRUE;
851 if (pAvSetMmThreadCharacteristicsW) {
853 this->hidden->task = pAvSetMmThreadCharacteristicsW(TEXT(
"Pro Audio"), &idx);
858 WASAPI_ThreadDeinit(
_THIS)
861 if (this->hidden->task && pAvRevertMmThreadCharacteristics) {
862 pAvRevertMmThreadCharacteristics(this->hidden->task);
863 this->hidden->task =
NULL;
866 if (this->hidden->coinitialized) {
872 WASAPI_Deinitialize(
void)
874 DevIdList *devidlist;
878 IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
879 IMMDeviceEnumerator_Release(enumerator);
885 FreeLibrary(libavrt);
890 pAvSetMmThreadCharacteristicsW =
NULL;
891 pAvRevertMmThreadCharacteristics =
NULL;
893 for (devidlist = deviceid_list; devidlist; devidlist = next) {
894 next = devidlist->next;
898 deviceid_list =
NULL;
910 SDL_SetError(
"WASAPI support requires Windows Vista or later");
922 ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator,
NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID) &enumerator);
930 pAvSetMmThreadCharacteristicsW = AvSetMmThreadCharacteristicsW;
931 pAvRevertMmThreadCharacteristics = AvRevertMmThreadCharacteristics;
933 libavrt = LoadLibraryW(L
"avrt.dll");
935 pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW) GetProcAddress(libavrt,
"AvSetMmThreadCharacteristicsW");
936 pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics) GetProcAddress(libavrt,
"AvRevertMmThreadCharacteristics");
959 "wasapi",
"WASAPI", WASAPI_Init, 0
SDL_AudioStream * SDL_NewAudioStream(const SDL_AudioFormat src_format, const Uint8 src_channels, const int src_rate, const SDL_AudioFormat dst_format, const Uint8 dst_channels, const int dst_rate)
void SDL_FreeAudioStream(SDL_AudioStream *stream)
BOOL WIN_IsWindowsVistaOrGreater(void)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void(* DetectDevices)(void)
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
int SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
void(* ThreadDeinit)(_THIS)
void(* PlayDevice)(_THIS)
void(* WaitDevice)(_THIS)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Uint16 SDL_AudioFormat
Audio format flags.
SDL_AudioFormat SDL_NextAudioFormat(void)
GLenum GLuint GLsizei bufsize
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
static SDL_AudioDeviceID device
void(* ThreadInit)(_THIS)
EGLImageKHR EGLint EGLint * handle
AudioBootStrap WASAPI_bootstrap
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
HRESULT WIN_CoInitialize(void)
uint8_t Uint8
An unsigned 8-bit integer type.
#define SDL_AUDIO_BITSIZE(x)
int SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 len)
void(* Deinitialize)(void)
#define WIN_StringToUTF8(S)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
GLenum GLenum GLsizei const GLuint GLboolean enabled
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)
#define SDL_assert(condition)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
#define SDL_OutOfMemory()
void WIN_CoUninitialize(void)
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
void(* CloseDevice)(_THIS)
int SDL_AudioStreamAvailable(SDL_AudioStream *stream)
void(* FlushCapture)(_THIS)
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
BOOL WIN_IsEqualIID(REFIID a, REFIID b)
Uint8 *(* GetDeviceBuf)(_THIS)
uint16_t Uint16
An unsigned 16-bit integer type.
void SDL_AudioStreamClear(SDL_AudioStream *stream)
int(* GetPendingBytes)(_THIS)
GLenum GLuint GLsizei const GLenum * props
GLboolean GLboolean GLboolean GLboolean a
GLboolean GLboolean GLboolean b
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)