21 #include "../../SDL_internal.h"
23 #if SDL_AUDIO_DRIVER_WASAPI
25 #include "../../core/windows/SDL_windows.h"
28 #include "../SDL_audio_c.h"
29 #include "../SDL_sysaudio.h"
32 #include <mmdeviceapi.h>
33 #include <audioclient.h>
38 #ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
39 #define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
47 typedef struct DevIdList
50 struct DevIdList *next;
53 static DevIdList *deviceid_list =
NULL;
56 static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
57 static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
58 static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
59 static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
62 WStrEqual(
const WCHAR *
a,
const WCHAR *
b)
75 WStrLen(
const WCHAR *wstr)
87 WStrDupe(
const WCHAR *wstr)
89 const size_t len = (WStrLen(wstr) + 1) *
sizeof (WCHAR);
103 DevIdList *prev =
NULL;
104 for (
i = deviceid_list;
i;
i = next) {
106 if (WStrEqual(
i->str, devid)) {
110 deviceid_list = next;
123 DevIdList *devidlist;
131 for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
132 if (WStrEqual(devidlist->str, devid)) {
137 devidlist = (DevIdList *)
SDL_malloc(
sizeof (*devidlist));
142 devid = WStrDupe(devid);
148 devidlist->str = (WCHAR *) devid;
149 devidlist->next = deviceid_list;
150 deviceid_list = devidlist;
156 WASAPI_DetectDevices(
void)
162 WasapiFailed(
_THIS,
const HRESULT err)
168 if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
169 this->hidden->device_lost =
SDL_TRUE;
171 IAudioClient_Stop(this->hidden->client);
187 if ( (this->callbackspec.channels == this->spec.channels) &&
188 (this->callbackspec.format == this->spec.format) &&
189 (this->callbackspec.freq == this->spec.freq) &&
190 (this->callbackspec.samples == this->spec.samples) ) {
194 }
else if ( (oldspec->
channels == this->spec.channels) &&
195 (oldspec->
format == this->spec.format) &&
196 (oldspec->
freq == this->spec.freq) ) {
201 if (this->iscapture) {
203 this->spec.channels, this->spec.freq,
204 this->callbackspec.format,
205 this->callbackspec.channels,
206 this->callbackspec.freq);
209 this->callbackspec.channels,
210 this->callbackspec.freq, this->spec.format,
211 this->spec.channels, this->spec.freq);
220 if (this->
spec.
size > this->work_buffer_len) {
225 this->work_buffer =
ptr;
226 this->work_buffer_len = this->
spec.
size;
233 static void ReleaseWasapiDevice(
_THIS);
236 RecoverWasapiDevice(
_THIS)
238 ReleaseWasapiDevice(
this);
240 if (this->hidden->default_device_generation) {
260 RecoverWasapiIfLost(
_THIS)
262 const int generation = this->hidden->default_device_generation;
263 SDL_bool lost = this->hidden->device_lost;
269 if (!this->hidden->client) {
273 if (!lost && (generation > 0)) {
275 if (generation != newgen) {
280 return lost ? RecoverWasapiDevice(
this) :
SDL_TRUE;
284 WASAPI_GetDeviceBuf(
_THIS)
289 while (RecoverWasapiIfLost(
this) && this->hidden->render) {
290 if (!WasapiFailed(
this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &
buffer))) {
300 WASAPI_PlayDevice(
_THIS)
302 if (this->hidden->render !=
NULL) {
304 WasapiFailed(
this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
309 WASAPI_WaitDevice(
_THIS)
311 while (RecoverWasapiIfLost(
this) && this->hidden->client && this->hidden->event) {
312 DWORD waitResult = WaitForSingleObjectEx(this->hidden->event, 200,
FALSE);
313 if (waitResult == WAIT_OBJECT_0) {
316 if (!WasapiFailed(
this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
318 if (padding <= maxpadding) {
322 }
else if (waitResult != WAIT_TIMEOUT) {
324 IAudioClient_Stop(this->hidden->client);
331 WASAPI_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
333 SDL_AudioStream *
stream = this->hidden->capturestream;
336 const int cpy =
SDL_min(buflen, avail);
341 while (RecoverWasapiIfLost(
this)) {
348 if (!this->hidden->capture) {
356 if (ret != AUDCLNT_S_BUFFER_EMPTY) {
357 WasapiFailed(
this, ret);
360 if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !
frames) {
361 WASAPI_WaitDevice(
this);
362 }
else if (ret ==
S_OK) {
363 const int total = ((
int)
frames) * this->hidden->framesize;
364 const int cpy =
SDL_min(buflen, total);
365 const int leftover = total - cpy;
385 ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture,
frames);
386 WasapiFailed(
this, ret);
396 WASAPI_FlushCapture(
_THIS)
402 if (!this->hidden->capture) {
408 const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &
ptr, &
frames, &
flags,
NULL,
NULL);
409 if (ret == AUDCLNT_S_BUFFER_EMPTY) {
411 }
else if (WasapiFailed(
this, ret)) {
413 }
else if (WasapiFailed(
this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture,
frames))) {
421 ReleaseWasapiDevice(
_THIS)
423 if (this->hidden->client) {
424 IAudioClient_Stop(this->hidden->client);
425 IAudioClient_SetEventHandle(this->hidden->client,
NULL);
426 IAudioClient_Release(this->hidden->client);
427 this->hidden->client =
NULL;
430 if (this->hidden->render) {
431 IAudioRenderClient_Release(this->hidden->render);
432 this->hidden->render =
NULL;
435 if (this->hidden->capture) {
436 IAudioCaptureClient_Release(this->hidden->capture);
437 this->hidden->capture =
NULL;
440 if (this->hidden->waveformat) {
441 CoTaskMemFree(this->hidden->waveformat);
442 this->hidden->waveformat =
NULL;
445 if (this->hidden->capturestream) {
447 this->hidden->capturestream =
NULL;
450 if (this->hidden->activation_handler) {
452 this->hidden->activation_handler =
NULL;
455 if (this->hidden->event) {
456 CloseHandle(this->hidden->event);
457 this->hidden->event =
NULL;
462 WASAPI_CloseDevice(
_THIS)
485 ReleaseWasapiDevice(
this);
506 const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
508 REFERENCE_TIME default_period = 0;
509 IAudioClient *client = this->hidden->client;
511 IAudioCaptureClient *capture =
NULL;
512 WAVEFORMATEX *waveformat =
NULL;
517 DWORD streamflags = 0;
522 this->hidden->event = CreateEventEx(
NULL,
NULL, 0, EVENT_ALL_ACCESS);
524 this->hidden->event = CreateEventW(
NULL, 0, 0,
NULL);
527 if (this->hidden->event ==
NULL) {
528 return WIN_SetError(
"WASAPI can't create an event handle");
531 ret = IAudioClient_GetMixFormat(client, &waveformat);
537 this->hidden->waveformat = waveformat;
542 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
544 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
546 }
else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
548 }
else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
549 const WAVEFORMATEXTENSIBLE *ext = (
const WAVEFORMATEXTENSIBLE *) waveformat;
550 if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
552 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
554 }
else if ((
SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
559 while ((!valid_format) && (test_format)) {
560 if (test_format == wasapi_format) {
569 return SDL_SetError(
"WASAPI: Unsupported audio format");
572 ret = IAudioClient_GetDevicePeriod(client, &default_period,
NULL);
578 if (this->
spec.
freq != waveformat->nSamplesPerSec) {
581 streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
582 waveformat->nSamplesPerSec = this->
spec.
freq;
583 waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
586 this->
spec.
freq = waveformat->nSamplesPerSec;
590 streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
591 ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat,
NULL);
596 ret = IAudioClient_SetEventHandle(client, this->hidden->event);
601 ret = IAudioClient_GetBufferSize(client, &
bufsize);
609 const float period_millis = default_period / 10000.0f;
610 const float period_frames = period_millis * this->
spec.
freq / 1000.0f;
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
#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
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 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d ®2 endm macro vzip8 reg2 vzip d d ®2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF ptr
void(* PlayDevice)(_THIS)
void(* WaitDevice)(_THIS)
void(* CloseDevice)(_THIS)
void(* FlushCapture)(_THIS)
void(* DetectDevices)(void)
void(* ThreadDeinit)(_THIS)
Uint8 *(* GetDeviceBuf)(_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)
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)
typedef int(__stdcall *FARPROC)()