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 WasapiFailed(
_THIS,
const HRESULT err)
171 if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
172 this->hidden->device_lost =
SDL_TRUE;
174 IAudioClient_Stop(this->hidden->client);
190 if ( (this->callbackspec.channels == this->spec.channels) &&
191 (this->callbackspec.format == this->spec.format) &&
192 (this->callbackspec.freq == this->spec.freq) &&
193 (this->callbackspec.samples == this->spec.samples) ) {
197 }
else if ( (oldspec->
channels == this->spec.channels) &&
198 (oldspec->
format == this->spec.format) &&
199 (oldspec->
freq == this->spec.freq) ) {
204 if (this->iscapture) {
206 this->spec.channels, this->spec.freq,
207 this->callbackspec.format,
208 this->callbackspec.channels,
209 this->callbackspec.freq);
212 this->callbackspec.channels,
213 this->callbackspec.freq, this->spec.format,
214 this->spec.channels, this->spec.freq);
223 if (this->
spec.
size > this->work_buffer_len) {
228 this->work_buffer =
ptr;
229 this->work_buffer_len = this->
spec.
size;
236 static void ReleaseWasapiDevice(
_THIS);
239 RecoverWasapiDevice(
_THIS)
241 ReleaseWasapiDevice(
this);
243 if (this->hidden->default_device_generation) {
263 RecoverWasapiIfLost(
_THIS)
265 const int generation = this->hidden->default_device_generation;
266 SDL_bool lost = this->hidden->device_lost;
272 if (!this->hidden->client) {
276 if (!lost && (generation > 0)) {
278 if (generation != newgen) {
283 return lost ? RecoverWasapiDevice(
this) :
SDL_TRUE;
287 WASAPI_GetDeviceBuf(
_THIS)
292 while (RecoverWasapiIfLost(
this) && this->hidden->render) {
293 if (!WasapiFailed(
this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &
buffer))) {
303 WASAPI_PlayDevice(
_THIS)
305 if (this->hidden->render !=
NULL) {
307 WasapiFailed(
this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
312 WASAPI_WaitDevice(
_THIS)
314 while (RecoverWasapiIfLost(
this) && this->hidden->client && this->hidden->event) {
315 DWORD waitResult = WaitForSingleObjectEx(this->hidden->event, 200,
FALSE);
316 if (waitResult == WAIT_OBJECT_0) {
319 if (!WasapiFailed(
this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
321 if (padding <= maxpadding) {
325 }
else if (waitResult != WAIT_TIMEOUT) {
327 IAudioClient_Stop(this->hidden->client);
334 WASAPI_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
336 SDL_AudioStream *
stream = this->hidden->capturestream;
339 const int cpy =
SDL_min(buflen, avail);
344 while (RecoverWasapiIfLost(
this)) {
351 if (!this->hidden->capture) {
359 if (ret != AUDCLNT_S_BUFFER_EMPTY) {
360 WasapiFailed(
this, ret);
363 if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !
frames) {
364 WASAPI_WaitDevice(
this);
365 }
else if (ret ==
S_OK) {
366 const int total = ((int)
frames) * this->hidden->framesize;
367 const int cpy =
SDL_min(buflen, total);
368 const int leftover = total - cpy;
388 ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture,
frames);
389 WasapiFailed(
this, ret);
399 WASAPI_FlushCapture(
_THIS)
405 if (!this->hidden->capture) {
411 const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &
ptr, &
frames, &
flags,
NULL,
NULL);
412 if (ret == AUDCLNT_S_BUFFER_EMPTY) {
414 }
else if (WasapiFailed(
this, ret)) {
416 }
else if (WasapiFailed(
this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture,
frames))) {
424 ReleaseWasapiDevice(
_THIS)
426 if (this->hidden->client) {
427 IAudioClient_Stop(this->hidden->client);
428 IAudioClient_SetEventHandle(this->hidden->client,
NULL);
429 IAudioClient_Release(this->hidden->client);
430 this->hidden->client =
NULL;
433 if (this->hidden->render) {
434 IAudioRenderClient_Release(this->hidden->render);
435 this->hidden->render =
NULL;
438 if (this->hidden->capture) {
439 IAudioCaptureClient_Release(this->hidden->capture);
440 this->hidden->capture =
NULL;
443 if (this->hidden->waveformat) {
444 CoTaskMemFree(this->hidden->waveformat);
445 this->hidden->waveformat =
NULL;
448 if (this->hidden->capturestream) {
450 this->hidden->capturestream =
NULL;
453 if (this->hidden->activation_handler) {
455 this->hidden->activation_handler =
NULL;
458 if (this->hidden->event) {
459 CloseHandle(this->hidden->event);
460 this->hidden->event =
NULL;
465 WASAPI_CloseDevice(
_THIS)
488 ReleaseWasapiDevice(
this);
509 const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
511 REFERENCE_TIME duration = 0;
512 IAudioClient *client = this->hidden->client;
514 IAudioCaptureClient *capture =
NULL;
515 WAVEFORMATEX *waveformat =
NULL;
520 DWORD streamflags = 0;
525 this->hidden->event = CreateEventEx(
NULL,
NULL, 0, EVENT_ALL_ACCESS);
527 this->hidden->event = CreateEventW(
NULL, 0, 0,
NULL);
530 if (this->hidden->event ==
NULL) {
531 return WIN_SetError(
"WASAPI can't create an event handle");
534 ret = IAudioClient_GetMixFormat(client, &waveformat);
540 this->hidden->waveformat = waveformat;
545 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
547 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
549 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
551 }
else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
552 const WAVEFORMATEXTENSIBLE *ext = (
const WAVEFORMATEXTENSIBLE *) waveformat;
553 if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
555 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
557 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
562 while ((!valid_format) && (test_format)) {
563 if (test_format == wasapi_format) {
572 return SDL_SetError(
"WASAPI: Unsupported audio format");
575 ret = IAudioClient_GetDevicePeriod(client,
NULL, &duration);
581 if (this->
spec.
freq != waveformat->nSamplesPerSec) {
584 streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
585 waveformat->nSamplesPerSec = this->
spec.
freq;
586 waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
589 this->
spec.
freq = waveformat->nSamplesPerSec;
593 streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
594 ret = IAudioClient_Initialize(client, sharemode, streamflags, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, waveformat,
NULL);
599 ret = IAudioClient_SetEventHandle(client, this->hidden->event);
604 ret = IAudioClient_GetBufferSize(client, &
bufsize);
610 if (!this->iscapture) {
619 if (this->iscapture) {
620 this->hidden->capturestream =
SDL_NewAudioStream(this->
spec.
format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
621 if (!this->hidden->capturestream) {
625 ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (
void**) &capture);
631 this->hidden->capture = capture;
632 ret = IAudioClient_Start(client);
637 WASAPI_FlushCapture(
this);
639 ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (
void**) &
render);
645 this->hidden->render =
render;
646 ret = IAudioClient_Start(client);
653 if (UpdateAudioStream(
this, &oldspec) == -1) {
663 WASAPI_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
665 LPCWSTR devid = (LPCWSTR)
handle;
670 if (this->hidden ==
NULL) {
680 this->hidden->devid = WStrDupe(
devid);
681 if (!this->hidden->devid) {
702 WASAPI_ThreadInit(
_THIS)
708 WASAPI_ThreadDeinit(
_THIS)
720 WASAPI_Deinitialize(
void)
722 DevIdList *devidlist;
727 for (devidlist = deviceid_list; devidlist; devidlist = next) {
728 next = devidlist->next;
732 deviceid_list =
NULL;
764 "wasapi",
"WASAPI", WASAPI_Init, 0