SDL 2.0
SDL_wasapi.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "../../SDL_internal.h"
23
24#if SDL_AUDIO_DRIVER_WASAPI
25
26#include "../../core/windows/SDL_windows.h"
27#include "SDL_audio.h"
28#include "SDL_timer.h"
29#include "../SDL_audio_c.h"
30#include "../SDL_sysaudio.h"
31#include "SDL_assert.h"
32#include "SDL_log.h"
33
34#define COBJMACROS
35#include <mmdeviceapi.h>
36#include <audioclient.h>
37
38#include "SDL_wasapi.h"
39
40/* This constant isn't available on MinGW-w64 */
41#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
42#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
43#endif
44
45/* these increment as default devices change. Opened default devices pick up changes in their threads. */
48
49/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
50typedef struct DevIdList
51{
52 WCHAR *str;
53 struct DevIdList *next;
54} DevIdList;
55
56static DevIdList *deviceid_list = NULL;
57
58/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
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 } };
63
64static SDL_bool
65WStrEqual(const WCHAR *a, const WCHAR *b)
66{
67 while (*a) {
68 if (*a != *b) {
69 return SDL_FALSE;
70 }
71 a++;
72 b++;
73 }
74 return *b == 0;
75}
76
77static size_t
78WStrLen(const WCHAR *wstr)
79{
80 size_t retval = 0;
81 if (wstr) {
82 while (*(wstr++)) {
83 retval++;
84 }
85 }
86 return retval;
87}
88
89static WCHAR *
90WStrDupe(const WCHAR *wstr)
91{
92 const size_t len = (WStrLen(wstr) + 1) * sizeof (WCHAR);
93 WCHAR *retval = (WCHAR *) SDL_malloc(len);
94 if (retval) {
95 SDL_memcpy(retval, wstr, len);
96 }
97 return retval;
98}
99
100
101void
102WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
103{
104 DevIdList *i;
105 DevIdList *next;
106 DevIdList *prev = NULL;
107 for (i = deviceid_list; i; i = next) {
108 next = i->next;
109 if (WStrEqual(i->str, devid)) {
110 if (prev) {
111 prev->next = next;
112 } else {
113 deviceid_list = next;
114 }
115 SDL_RemoveAudioDevice(iscapture, i->str);
116 SDL_free(i->str);
117 SDL_free(i);
118 }
119 prev = i;
120 }
121}
122
123void
124WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
125{
126 DevIdList *devidlist;
127
128 /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
129 In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
130 phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
131 available and switch automatically. (!!! FIXME...?) */
132
133 /* see if we already have this one. */
134 for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
135 if (WStrEqual(devidlist->str, devid)) {
136 return; /* we already have this. */
137 }
138 }
139
140 devidlist = (DevIdList *) SDL_malloc(sizeof (*devidlist));
141 if (!devidlist) {
142 return; /* oh well. */
143 }
144
145 devid = WStrDupe(devid);
146 if (!devid) {
147 SDL_free(devidlist);
148 return; /* oh well. */
149 }
150
151 devidlist->str = (WCHAR *) devid;
152 devidlist->next = deviceid_list;
153 deviceid_list = devidlist;
154
155 SDL_AddAudioDevice(iscapture, devname, (void *) devid);
156}
157
158static void
159WASAPI_DetectDevices(void)
160{
162}
163
164static SDL_INLINE SDL_bool
165WasapiFailed(_THIS, const HRESULT err)
166{
167 if (err == S_OK) {
168 return SDL_FALSE;
169 }
170
171 if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
172 this->hidden->device_lost = SDL_TRUE;
173 } else if (SDL_AtomicGet(&this->enabled)) {
174 IAudioClient_Stop(this->hidden->client);
177 }
178
179 return SDL_TRUE;
180}
181
182static int
183UpdateAudioStream(_THIS, const SDL_AudioSpec *oldspec)
184{
185 /* Since WASAPI requires us to handle all audio conversion, and our
186 device format might have changed, we might have to add/remove/change
187 the audio stream that the higher level uses to convert data, so
188 SDL keeps firing the callback as if nothing happened here. */
189
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) ) {
194 /* no need to buffer/convert in an AudioStream! */
196 this->stream = NULL;
197 } else if ( (oldspec->channels == this->spec.channels) &&
198 (oldspec->format == this->spec.format) &&
199 (oldspec->freq == this->spec.freq) ) {
200 /* The existing audio stream is okay to keep using. */
201 } else {
202 /* replace the audiostream for new format */
204 if (this->iscapture) {
205 this->stream = SDL_NewAudioStream(this->spec.format,
206 this->spec.channels, this->spec.freq,
207 this->callbackspec.format,
208 this->callbackspec.channels,
209 this->callbackspec.freq);
210 } else {
211 this->stream = SDL_NewAudioStream(this->callbackspec.format,
212 this->callbackspec.channels,
213 this->callbackspec.freq, this->spec.format,
214 this->spec.channels, this->spec.freq);
215 }
216
217 if (!this->stream) {
218 return -1;
219 }
220 }
221
222 /* make sure our scratch buffer can cover the new device spec. */
223 if (this->spec.size > this->work_buffer_len) {
224 Uint8 *ptr = (Uint8 *) SDL_realloc(this->work_buffer, this->spec.size);
225 if (ptr == NULL) {
226 return SDL_OutOfMemory();
227 }
228 this->work_buffer = ptr;
229 this->work_buffer_len = this->spec.size;
230 }
231
232 return 0;
233}
234
235
236static void ReleaseWasapiDevice(_THIS);
237
238static SDL_bool
239RecoverWasapiDevice(_THIS)
240{
241 ReleaseWasapiDevice(this); /* dump the lost device's handles. */
242
243 if (this->hidden->default_device_generation) {
244 this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
245 }
246
247 /* this can fail for lots of reasons, but the most likely is we had a
248 non-default device that was disconnected, so we can't recover. Default
249 devices try to reinitialize whatever the new default is, so it's more
250 likely to carry on here, but this handles a non-default device that
251 simply had its format changed in the Windows Control Panel. */
252 if (WASAPI_ActivateDevice(this, SDL_TRUE) == -1) {
254 return SDL_FALSE;
255 }
256
257 this->hidden->device_lost = SDL_FALSE;
258
259 return SDL_TRUE; /* okay, carry on with new device details! */
260}
261
262static SDL_bool
263RecoverWasapiIfLost(_THIS)
264{
265 const int generation = this->hidden->default_device_generation;
266 SDL_bool lost = this->hidden->device_lost;
267
268 if (!SDL_AtomicGet(&this->enabled)) {
269 return SDL_FALSE; /* already failed. */
270 }
271
272 if (!this->hidden->client) {
273 return SDL_TRUE; /* still waiting for activation. */
274 }
275
276 if (!lost && (generation > 0)) { /* is a default device? */
278 if (generation != newgen) { /* the desired default device was changed, jump over to it. */
279 lost = SDL_TRUE;
280 }
281 }
282
283 return lost ? RecoverWasapiDevice(this) : SDL_TRUE;
284}
285
286static Uint8 *
287WASAPI_GetDeviceBuf(_THIS)
288{
289 /* get an endpoint buffer from WASAPI. */
290 BYTE *buffer = NULL;
291
292 while (RecoverWasapiIfLost(this) && this->hidden->render) {
293 if (!WasapiFailed(this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
294 return (Uint8 *) buffer;
295 }
297 }
298
299 return (Uint8 *) buffer;
300}
301
302static void
303WASAPI_PlayDevice(_THIS)
304{
305 if (this->hidden->render != NULL) { /* definitely activated? */
306 /* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
307 WasapiFailed(this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
308 }
309}
310
311static void
312WASAPI_WaitDevice(_THIS)
313{
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) {
317 const UINT32 maxpadding = this->spec.samples;
318 UINT32 padding = 0;
319 if (!WasapiFailed(this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
320 /*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
321 if (padding <= maxpadding) {
322 break;
323 }
324 }
325 } else if (waitResult != WAIT_TIMEOUT) {
326 /*SDL_Log("WASAPI FAILED EVENT!");*/
327 IAudioClient_Stop(this->hidden->client);
329 }
330 }
331}
332
333static int
334WASAPI_CaptureFromDevice(_THIS, void *buffer, int buflen)
335{
336 SDL_AudioStream *stream = this->hidden->capturestream;
337 const int avail = SDL_AudioStreamAvailable(stream);
338 if (avail > 0) {
339 const int cpy = SDL_min(buflen, avail);
341 return cpy;
342 }
343
344 while (RecoverWasapiIfLost(this)) {
345 HRESULT ret;
346 BYTE *ptr = NULL;
347 UINT32 frames = 0;
348 DWORD flags = 0;
349
350 /* uhoh, client isn't activated yet, just return silence. */
351 if (!this->hidden->capture) {
352 /* Delay so we run at about the speed that audio would be arriving. */
353 SDL_Delay(((this->spec.samples * 1000) / this->spec.freq));
354 SDL_memset(buffer, this->spec.silence, buflen);
355 return buflen;
356 }
357
358 ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
359 if (ret != AUDCLNT_S_BUFFER_EMPTY) {
360 WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
361 }
362
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;
369 const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
370
371 if (silent) {
372 SDL_memset(buffer, this->spec.silence, cpy);
373 } else {
374 SDL_memcpy(buffer, ptr, cpy);
375 }
376
377 if (leftover > 0) {
378 ptr += cpy;
379 if (silent) {
380 SDL_memset(ptr, this->spec.silence, leftover); /* I guess this is safe? */
381 }
382
383 if (SDL_AudioStreamPut(stream, ptr, leftover) == -1) {
384 return -1; /* uhoh, out of memory, etc. Kill device. :( */
385 }
386 }
387
388 ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
389 WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
390
391 return cpy;
392 }
393 }
394
395 return -1; /* unrecoverable error. */
396}
397
398static void
399WASAPI_FlushCapture(_THIS)
400{
401 BYTE *ptr = NULL;
402 UINT32 frames = 0;
403 DWORD flags = 0;
404
405 if (!this->hidden->capture) {
406 return; /* not activated yet? */
407 }
408
409 /* just read until we stop getting packets, throwing them away. */
410 while (SDL_TRUE) {
411 const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
412 if (ret == AUDCLNT_S_BUFFER_EMPTY) {
413 break; /* no more buffered data; we're done. */
414 } else if (WasapiFailed(this, ret)) {
415 break; /* failed for some other reason, abort. */
416 } else if (WasapiFailed(this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
417 break; /* something broke. */
418 }
419 }
420 SDL_AudioStreamClear(this->hidden->capturestream);
421}
422
423static void
424ReleaseWasapiDevice(_THIS)
425{
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;
431 }
432
433 if (this->hidden->render) {
434 IAudioRenderClient_Release(this->hidden->render);
435 this->hidden->render = NULL;
436 }
437
438 if (this->hidden->capture) {
439 IAudioCaptureClient_Release(this->hidden->capture);
440 this->hidden->capture = NULL;
441 }
442
443 if (this->hidden->waveformat) {
444 CoTaskMemFree(this->hidden->waveformat);
445 this->hidden->waveformat = NULL;
446 }
447
448 if (this->hidden->capturestream) {
449 SDL_FreeAudioStream(this->hidden->capturestream);
450 this->hidden->capturestream = NULL;
451 }
452
453 if (this->hidden->activation_handler) {
454 WASAPI_PlatformDeleteActivationHandler(this->hidden->activation_handler);
455 this->hidden->activation_handler = NULL;
456 }
457
458 if (this->hidden->event) {
459 CloseHandle(this->hidden->event);
460 this->hidden->event = NULL;
461 }
462}
463
464static void
465WASAPI_CloseDevice(_THIS)
466{
467 WASAPI_UnrefDevice(this);
468}
469
470void
472{
473 SDL_AtomicIncRef(&this->hidden->refcount);
474}
475
476void
478{
479 if (!SDL_AtomicDecRef(&this->hidden->refcount)) {
480 return;
481 }
482
483 /* actual closing happens here. */
484
485 /* don't touch this->hidden->task in here; it has to be reverted from
486 our callback thread. We do that in WASAPI_ThreadDeinit().
487 (likewise for this->hidden->coinitialized). */
488 ReleaseWasapiDevice(this);
489 SDL_free(this->hidden->devid);
490 SDL_free(this->hidden);
491}
492
493/* This is called once a device is activated, possibly asynchronously. */
494int
495WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
496{
497 /* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
498 !!! it will write into the kernel's audio buffer directly instead of
499 !!! shared memory that a user-mode mixer then writes to the kernel with
500 !!! everything else. Doing this means any other sound using this device will
501 !!! stop playing, including the user's MP3 player and system notification
502 !!! sounds. You'd probably need to release the device when the app isn't in
503 !!! the foreground, to be a good citizen of the system. It's doable, but it's
504 !!! more work and causes some annoyances, and I don't know what the latency
505 !!! wins actually look like. Maybe add a hint to force exclusive mode at
506 !!! some point. To be sure, defaulting to shared mode is the right thing to
507 !!! do in any case. */
508 const SDL_AudioSpec oldspec = this->spec;
509 const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
510 UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
511 REFERENCE_TIME duration = 0;
512 IAudioClient *client = this->hidden->client;
513 IAudioRenderClient *render = NULL;
514 IAudioCaptureClient *capture = NULL;
515 WAVEFORMATEX *waveformat = NULL;
516 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
517 SDL_AudioFormat wasapi_format = 0;
518 SDL_bool valid_format = SDL_FALSE;
519 HRESULT ret = S_OK;
520 DWORD streamflags = 0;
521
522 SDL_assert(client != NULL);
523
524#ifdef __WINRT__ /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
525 this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
526#else
527 this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
528#endif
529
530 if (this->hidden->event == NULL) {
531 return WIN_SetError("WASAPI can't create an event handle");
532 }
533
534 ret = IAudioClient_GetMixFormat(client, &waveformat);
535 if (FAILED(ret)) {
536 return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
537 }
538
539 SDL_assert(waveformat != NULL);
540 this->hidden->waveformat = waveformat;
541
542 this->spec.channels = (Uint8) waveformat->nChannels;
543
544 /* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
545 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
546 wasapi_format = AUDIO_F32SYS;
547 } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
548 wasapi_format = AUDIO_S16SYS;
549 } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
550 wasapi_format = AUDIO_S32SYS;
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)) {
554 wasapi_format = AUDIO_F32SYS;
555 } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
556 wasapi_format = AUDIO_S16SYS;
557 } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
558 wasapi_format = AUDIO_S32SYS;
559 }
560 }
561
562 while ((!valid_format) && (test_format)) {
563 if (test_format == wasapi_format) {
564 this->spec.format = test_format;
565 valid_format = SDL_TRUE;
566 break;
567 }
568 test_format = SDL_NextAudioFormat();
569 }
570
571 if (!valid_format) {
572 return SDL_SetError("WASAPI: Unsupported audio format");
573 }
574
575 ret = IAudioClient_GetDevicePeriod(client, NULL, &duration);
576 if (FAILED(ret)) {
577 return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
578 }
579
580 /* favor WASAPI's resampler over our own, in Win7+. */
581 if (this->spec.freq != waveformat->nSamplesPerSec) {
582 /* RATEADJUST only works with output devices in share mode, and is available in Win7 and later.*/
583 if (WIN_IsWindows7OrGreater() && !this->iscapture && (sharemode == AUDCLNT_SHAREMODE_SHARED)) {
584 streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
585 waveformat->nSamplesPerSec = this->spec.freq;
586 waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
587 }
588 else {
589 this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in. */
590 }
591 }
592
593 streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
594 ret = IAudioClient_Initialize(client, sharemode, streamflags, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, waveformat, NULL);
595 if (FAILED(ret)) {
596 return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
597 }
598
599 ret = IAudioClient_SetEventHandle(client, this->hidden->event);
600 if (FAILED(ret)) {
601 return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
602 }
603
604 ret = IAudioClient_GetBufferSize(client, &bufsize);
605 if (FAILED(ret)) {
606 return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
607 }
608
609 this->spec.samples = (Uint16) bufsize;
610 if (!this->iscapture) {
611 this->spec.samples /= 2; /* fill half of the DMA buffer on each run. */
612 }
613
614 /* Update the fragment size as size in bytes */
616
617 this->hidden->framesize = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
618
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) {
622 return -1; /* already set SDL_Error */
623 }
624
625 ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void**) &capture);
626 if (FAILED(ret)) {
627 return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
628 }
629
630 SDL_assert(capture != NULL);
631 this->hidden->capture = capture;
632 ret = IAudioClient_Start(client);
633 if (FAILED(ret)) {
634 return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
635 }
636
637 WASAPI_FlushCapture(this); /* MSDN says you should flush capture endpoint right after startup. */
638 } else {
639 ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void**) &render);
640 if (FAILED(ret)) {
641 return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
642 }
643
645 this->hidden->render = render;
646 ret = IAudioClient_Start(client);
647 if (FAILED(ret)) {
648 return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
649 }
650 }
651
652 if (updatestream) {
653 if (UpdateAudioStream(this, &oldspec) == -1) {
654 return -1;
655 }
656 }
657
658 return 0; /* good to go. */
659}
660
661
662static int
663WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
664{
665 LPCWSTR devid = (LPCWSTR) handle;
666
667 /* Initialize all variables that we clean on shutdown */
668 this->hidden = (struct SDL_PrivateAudioData *)
669 SDL_malloc((sizeof *this->hidden));
670 if (this->hidden == NULL) {
671 return SDL_OutOfMemory();
672 }
673 SDL_zerop(this->hidden);
674
675 WASAPI_RefDevice(this); /* so CloseDevice() will unref to zero. */
676
677 if (!devid) { /* is default device? */
678 this->hidden->default_device_generation = SDL_AtomicGet(iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
679 } else {
680 this->hidden->devid = WStrDupe(devid);
681 if (!this->hidden->devid) {
682 return SDL_OutOfMemory();
683 }
684 }
685
686 if (WASAPI_ActivateDevice(this, SDL_FALSE) == -1) {
687 return -1; /* already set error. */
688 }
689
690 /* Ready, but waiting for async device activation.
691 Until activation is successful, we will report silence from capture
692 devices and ignore data on playback devices.
693 Also, since we don't know the _actual_ device format until after
694 activation, we let the app have whatever it asks for. We set up
695 an SDL_AudioStream to convert, if necessary, once the activation
696 completes. */
697
698 return 0;
699}
700
701static void
702WASAPI_ThreadInit(_THIS)
703{
705}
706
707static void
708WASAPI_ThreadDeinit(_THIS)
709{
711}
712
713void
715{
716 /* no-op. */
717}
718
719static void
720WASAPI_Deinitialize(void)
721{
722 DevIdList *devidlist;
723 DevIdList *next;
724
726
727 for (devidlist = deviceid_list; devidlist; devidlist = next) {
728 next = devidlist->next;
729 SDL_free(devidlist->str);
730 SDL_free(devidlist);
731 }
732 deviceid_list = NULL;
733}
734
735static int
736WASAPI_Init(SDL_AudioDriverImpl * impl)
737{
740
741 if (WASAPI_PlatformInit() == -1) {
742 return 0;
743 }
744
745 /* Set the function pointers */
746 impl->DetectDevices = WASAPI_DetectDevices;
747 impl->ThreadInit = WASAPI_ThreadInit;
748 impl->ThreadDeinit = WASAPI_ThreadDeinit;
750 impl->OpenDevice = WASAPI_OpenDevice;
751 impl->PlayDevice = WASAPI_PlayDevice;
752 impl->WaitDevice = WASAPI_WaitDevice;
753 impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
754 impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
755 impl->FlushCapture = WASAPI_FlushCapture;
756 impl->CloseDevice = WASAPI_CloseDevice;
757 impl->Deinitialize = WASAPI_Deinitialize;
758 impl->HasCaptureSupport = 1;
759
760 return 1; /* this audio target is available. */
761}
762
764 "wasapi", "WASAPI", WASAPI_Init, 0
765};
766
767#endif /* SDL_AUDIO_DRIVER_WASAPI */
768
769/* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:531
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:469
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1659
#define AUDIO_S16SYS
Definition: SDL_audio.h:123
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define AUDIO_S32SYS
Definition: SDL_audio.h:124
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define AUDIO_F32SYS
Definition: SDL_audio.h:125
#define S_OK
Definition: SDL_directx.h:47
#define FAILED(x)
Definition: SDL_directx.h:54
#define SDL_AtomicSet
#define SDL_SetError
#define SDL_memset
#define SDL_AudioStreamGet
#define SDL_AudioStreamClear
#define SDL_NewAudioStream
#define SDL_AudioStreamAvailable
#define SDL_FreeAudioStream
#define SDL_malloc
#define SDL_realloc
#define SDL_free
#define SDL_AudioStreamPut
#define SDL_memcmp
#define SDL_Delay
#define SDL_AtomicGet
#define SDL_memcpy
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLboolean GLboolean GLboolean b
GLuint GLuint stream
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum GLsizei len
GLboolean GLboolean GLboolean GLboolean a
GLuint buffer
GLbitfield flags
GLenum GLuint GLsizei bufsize
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
uint16_t Uint16
Definition: SDL_stdinc.h:191
uint8_t Uint8
Definition: SDL_stdinc.h:179
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)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
#define SDL_INLINE
Definition: begin_code.h:134
#define FALSE
Definition: edid-parse.c:34
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_AudioSpec spec
Definition: loopwave.c:31
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
void(* ThreadDeinit)(_THIS)
Definition: SDL_sysaudio.h:70
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
void(* BeginLoopIteration)(_THIS)
Definition: SDL_sysaudio.h:71
void(* ThreadInit)(_THIS)
Definition: SDL_sysaudio.h:69
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
Uint32 size
Definition: SDL_audio.h:186
Uint16 samples
Definition: SDL_audio.h:184
Uint8 channels
Definition: SDL_audio.h:182
Uint8 silence
Definition: SDL_audio.h:183
SDL_AudioFormat format
Definition: SDL_audio.h:181
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_bool retval
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
Definition: testshape.c:29
static Uint32 frames
Definition: testsprite2.c:40