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
50typedef struct DevIdList
53 struct DevIdList *next;
56static DevIdList *deviceid_list =
NULL;
59static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
60static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
61static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
62static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
65WStrEqual(
const WCHAR *
a,
const WCHAR *
b)
78WStrLen(
const WCHAR *wstr)
90WStrDupe(
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;
159WASAPI_DetectDevices(
void)
165WasapiFailed(
_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;
236static void ReleaseWasapiDevice(
_THIS);
239RecoverWasapiDevice(
_THIS)
241 ReleaseWasapiDevice(
this);
243 if (this->hidden->default_device_generation) {
263RecoverWasapiIfLost(
_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;
287WASAPI_GetDeviceBuf(
_THIS)
292 while (RecoverWasapiIfLost(
this) && this->hidden->render) {
293 if (!WasapiFailed(
this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &
buffer))) {
303WASAPI_PlayDevice(
_THIS)
305 if (this->hidden->render !=
NULL) {
307 WasapiFailed(
this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
312WASAPI_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);
334WASAPI_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) {
358 ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &
frames, &
flags,
NULL,
NULL);
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);
399WASAPI_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))) {
424ReleaseWasapiDevice(
_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;
465WASAPI_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) {
663WASAPI_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) {
702WASAPI_ThreadInit(
_THIS)
708WASAPI_ThreadDeinit(
_THIS)
720WASAPI_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
#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.
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
SDL_AudioFormat SDL_NextAudioFormat(void)
Uint16 SDL_AudioFormat
Audio format flags.
#define SDL_AUDIO_BITSIZE(x)
#define SDL_AudioStreamGet
#define SDL_AudioStreamClear
#define SDL_NewAudioStream
#define SDL_AudioStreamAvailable
#define SDL_FreeAudioStream
#define SDL_AudioStreamPut
#define SDL_OutOfMemory()
GLboolean GLboolean GLboolean b
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLboolean GLboolean GLboolean GLboolean a
GLenum GLuint GLsizei bufsize
AudioBootStrap WASAPI_bootstrap
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)
void WASAPI_RefDevice(_THIS)
void WASAPI_BeginLoopIteration(_THIS)
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)
void WASAPI_UnrefDevice(_THIS)
BOOL WIN_IsWindows7OrGreater(void)
int WIN_SetError(const char *prefix)
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)
EGLImageKHR EGLint EGLint * handle
void(* PlayDevice)(_THIS)
void(* WaitDevice)(_THIS)
void(* CloseDevice)(_THIS)
void(* FlushCapture)(_THIS)
void(* DetectDevices)(void)
void(* ThreadDeinit)(_THIS)
void(* Deinitialize)(void)
void(* BeginLoopIteration)(_THIS)
void(* ThreadInit)(_THIS)
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Uint8 *(* GetDeviceBuf)(_THIS)
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)