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)
735 WASAPI_Deinitialize(
void)
737 DevIdList *devidlist;
742 for (devidlist = deviceid_list; devidlist; devidlist = next) {
743 next = devidlist->next;
747 deviceid_list =
NULL;
780 "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
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
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
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 local skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
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)