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> 41 #ifndef AUDCLNT_STREAMFLAGS_RATEADJUST 42 #define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 50 typedef struct DevIdList
53 struct DevIdList *next;
56 static DevIdList *deviceid_list =
NULL;
59 static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
60 static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
61 static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
62 static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
65 WStrEqual(
const WCHAR *
a,
const WCHAR *
b)
78 WStrLen(
const WCHAR *wstr)
90 WStrDupe(
const WCHAR *wstr)
92 const size_t len = (WStrLen(wstr) + 1) *
sizeof (WCHAR);
106 DevIdList *prev =
NULL;
107 for (i = deviceid_list;
i; i = next) {
109 if (WStrEqual(i->str, devid)) {
113 deviceid_list = next;
126 DevIdList *devidlist;
134 for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
135 if (WStrEqual(devidlist->str, devid)) {
140 devidlist = (DevIdList *)
SDL_malloc(
sizeof (*devidlist));
145 devid = WStrDupe(devid);
151 devidlist->str = (WCHAR *) devid;
152 devidlist->next = deviceid_list;
153 deviceid_list = devidlist;
159 WASAPI_DetectDevices(
void)
165 WASAPI_GetPendingBytes(
_THIS)
171 if (this->hidden->client !=
NULL) {
172 if (
FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
176 return ((
int) frames) * this->hidden->framesize;
180 WasapiFailed(
_THIS,
const HRESULT err)
186 if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
187 this->hidden->device_lost =
SDL_TRUE;
189 IAudioClient_Stop(this->hidden->client);
205 if ( (this->callbackspec.channels == this->spec.channels) &&
206 (this->callbackspec.format == this->spec.format) &&
207 (this->callbackspec.freq == this->spec.freq) &&
208 (this->callbackspec.samples == this->spec.samples) ) {
212 }
else if ( (oldspec->
channels == this->spec.channels) &&
213 (oldspec->
format == this->spec.format) &&
214 (oldspec->
freq == this->spec.freq) ) {
219 if (this->iscapture) {
221 this->spec.channels, this->spec.freq,
222 this->callbackspec.format,
223 this->callbackspec.channels,
224 this->callbackspec.freq);
227 this->callbackspec.channels,
228 this->callbackspec.freq, this->spec.format,
229 this->spec.channels, this->spec.freq);
238 if (this->
spec.
size > this->work_buffer_len) {
243 this->work_buffer = ptr;
244 this->work_buffer_len = this->
spec.
size;
251 static void ReleaseWasapiDevice(
_THIS);
254 RecoverWasapiDevice(
_THIS)
256 ReleaseWasapiDevice(
this);
258 if (this->hidden->default_device_generation) {
259 this->hidden->default_device_generation =
SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
278 RecoverWasapiIfLost(
_THIS)
280 const int generation = this->hidden->default_device_generation;
281 SDL_bool lost = this->hidden->device_lost;
287 if (!this->hidden->client) {
291 if (!lost && (generation > 0)) {
292 const int newgen =
SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
293 if (generation != newgen) {
298 return lost ? RecoverWasapiDevice(
this) :
SDL_TRUE;
302 WASAPI_GetDeviceBuf(
_THIS)
307 while (RecoverWasapiIfLost(
this) && this->hidden->render) {
308 if (!WasapiFailed(
this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
318 WASAPI_PlayDevice(
_THIS)
320 if (this->hidden->render !=
NULL) {
322 WasapiFailed(
this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
327 WASAPI_WaitDevice(
_THIS)
329 while (RecoverWasapiIfLost(
this) && this->hidden->client && this->hidden->event) {
331 if (WaitForSingleObjectEx(this->hidden->event, INFINITE,
FALSE) == WAIT_OBJECT_0) {
334 if (!WasapiFailed(
this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
336 if (padding <= maxpadding) {
342 IAudioClient_Stop(this->hidden->client);
349 WASAPI_CaptureFromDevice(
_THIS,
void *buffer,
int buflen)
351 SDL_AudioStream *
stream = this->hidden->capturestream;
354 const int cpy =
SDL_min(buflen, avail);
359 while (RecoverWasapiIfLost(
this)) {
366 if (!this->hidden->capture) {
373 ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags,
NULL,
NULL);
374 if (ret != AUDCLNT_S_BUFFER_EMPTY) {
375 WasapiFailed(
this, ret);
378 if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
379 WASAPI_WaitDevice(
this);
380 }
else if (ret ==
S_OK) {
381 const int total = ((int) frames) * this->hidden->framesize;
382 const int cpy =
SDL_min(buflen, total);
383 const int leftover = total - cpy;
403 ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
404 WasapiFailed(
this, ret);
414 WASAPI_FlushCapture(
_THIS)
420 if (!this->hidden->capture) {
426 const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags,
NULL,
NULL);
427 if (ret == AUDCLNT_S_BUFFER_EMPTY) {
429 }
else if (WasapiFailed(
this, ret)) {
431 }
else if (WasapiFailed(
this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
439 ReleaseWasapiDevice(
_THIS)
441 if (this->hidden->client) {
442 IAudioClient_Stop(this->hidden->client);
443 IAudioClient_SetEventHandle(this->hidden->client,
NULL);
444 IAudioClient_Release(this->hidden->client);
445 this->hidden->client =
NULL;
448 if (this->hidden->render) {
449 IAudioRenderClient_Release(this->hidden->render);
450 this->hidden->render =
NULL;
453 if (this->hidden->capture) {
454 IAudioCaptureClient_Release(this->hidden->capture);
455 this->hidden->capture =
NULL;
458 if (this->hidden->waveformat) {
459 CoTaskMemFree(this->hidden->waveformat);
460 this->hidden->waveformat =
NULL;
463 if (this->hidden->capturestream) {
465 this->hidden->capturestream =
NULL;
468 if (this->hidden->activation_handler) {
470 this->hidden->activation_handler =
NULL;
473 if (this->hidden->event) {
474 CloseHandle(this->hidden->event);
475 this->hidden->event =
NULL;
480 WASAPI_CloseDevice(
_THIS)
503 ReleaseWasapiDevice(
this);
524 const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
526 REFERENCE_TIME duration = 0;
527 IAudioClient *client = this->hidden->client;
529 IAudioCaptureClient *capture =
NULL;
530 WAVEFORMATEX *waveformat =
NULL;
535 DWORD streamflags = 0;
540 this->hidden->event = CreateEventEx(
NULL,
NULL, 0, EVENT_ALL_ACCESS);
542 this->hidden->event = CreateEventW(
NULL, 0, 0,
NULL);
545 if (this->hidden->event ==
NULL) {
546 return WIN_SetError(
"WASAPI can't create an event handle");
549 ret = IAudioClient_GetMixFormat(client, &waveformat);
555 this->hidden->waveformat = waveformat;
560 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
562 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
564 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
566 }
else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
567 const WAVEFORMATEXTENSIBLE *ext = (
const WAVEFORMATEXTENSIBLE *) waveformat;
568 if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
570 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
572 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
577 while ((!valid_format) && (test_format)) {
578 if (test_format == wasapi_format) {
587 return SDL_SetError(
"WASAPI: Unsupported audio format");
590 ret = IAudioClient_GetDevicePeriod(client,
NULL, &duration);
596 if (this->
spec.
freq != waveformat->nSamplesPerSec) {
599 streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
600 waveformat->nSamplesPerSec = this->
spec.
freq;
601 waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
604 this->
spec.
freq = waveformat->nSamplesPerSec;
608 streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
609 ret = IAudioClient_Initialize(client, sharemode, streamflags, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, waveformat,
NULL);
614 ret = IAudioClient_SetEventHandle(client, this->hidden->event);
619 ret = IAudioClient_GetBufferSize(client, &bufsize);
625 if (!this->iscapture) {
634 if (this->iscapture) {
635 this->hidden->capturestream =
SDL_NewAudioStream(this->
spec.
format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
636 if (!this->hidden->capturestream) {
640 ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (
void**) &capture);
646 this->hidden->capture = capture;
647 ret = IAudioClient_Start(client);
652 WASAPI_FlushCapture(
this);
654 ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (
void**) &render);
660 this->hidden->render =
render;
661 ret = IAudioClient_Start(client);
668 if (UpdateAudioStream(
this, &oldspec) == -1) {
678 WASAPI_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
680 LPCWSTR devid = (LPCWSTR) handle;
685 if (this->hidden ==
NULL) {
693 this->hidden->default_device_generation =
SDL_AtomicGet(iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
695 this->hidden->devid = WStrDupe(devid);
696 if (!this->hidden->devid) {
717 WASAPI_ThreadInit(
_THIS)
723 WASAPI_ThreadDeinit(
_THIS)
729 WASAPI_Deinitialize(
void)
731 DevIdList *devidlist;
736 for (devidlist = deviceid_list; devidlist; devidlist = next) {
737 next = devidlist->next;
741 deviceid_list =
NULL;
774 "wasapi",
"WASAPI", WASAPI_Init, 0
#define SDL_AudioStreamAvailable
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
void(* DetectDevices)(void)
void WASAPI_UnrefDevice(_THIS)
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
void WASAPI_BeginLoopIteration(_THIS)
#define SDL_AudioStreamGet
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.
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
void WASAPI_PlatformThreadDeinit(_THIS)
void WASAPI_PlatformDeinit(void)
SDL_AudioFormat SDL_NextAudioFormat(void)
GLenum GLuint GLsizei bufsize
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
void WASAPI_RefDevice(_THIS)
void(* ThreadInit)(_THIS)
void WASAPI_EnumerateEndpoints(void)
EGLImageKHR EGLint EGLint * handle
AudioBootStrap WASAPI_bootstrap
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
BOOL WIN_IsWindows7OrGreater(void)
#define SDL_AUDIO_BITSIZE(x)
#define SDL_AudioStreamPut
void(* Deinitialize)(void)
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
int WASAPI_PlatformInit(void)
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)
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
#define SDL_assert(condition)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
#define SDL_OutOfMemory()
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
int WIN_SetError(const char *prefix)
SDL_atomic_t WASAPI_DefaultCaptureGeneration
void(* CloseDevice)(_THIS)
void(* FlushCapture)(_THIS)
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
void WASAPI_PlatformDeleteActivationHandler(void *handler)
Uint8 *(* GetDeviceBuf)(_THIS)
#define SDL_NewAudioStream
#define SDL_AudioStreamClear
void(* BeginLoopIteration)(_THIS)
void WASAPI_PlatformThreadInit(_THIS)
#define SDL_FreeAudioStream
int(* GetPendingBytes)(_THIS)
GLboolean GLboolean GLboolean GLboolean a
GLboolean GLboolean GLboolean b
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)