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. */
50 typedef struct DevIdList
51 {
52  WCHAR *str;
53  struct DevIdList *next;
54 } DevIdList;
55 
56 static DevIdList *deviceid_list = NULL;
57 
58 /* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
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 } };
63 
64 static SDL_bool
65 WStrEqual(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 
77 static size_t
78 WStrLen(const WCHAR *wstr)
79 {
80  size_t retval = 0;
81  if (wstr) {
82  while (*(wstr++)) {
83  retval++;
84  }
85  }
86  return retval;
87 }
88 
89 static WCHAR *
90 WStrDupe(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 
101 void
102 WASAPI_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 
123 void
124 WASAPI_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 
158 static void
159 WASAPI_DetectDevices(void)
160 {
162 }
163 
164 static SDL_INLINE SDL_bool
165 WasapiFailed(_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);
176  SDL_assert(!SDL_AtomicGet(&this->enabled));
177  }
178 
179  return SDL_TRUE;
180 }
181 
182 static int
183 UpdateAudioStream(_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 
236 static void ReleaseWasapiDevice(_THIS);
237 
238 static SDL_bool
239 RecoverWasapiDevice(_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 
262 static SDL_bool
263 RecoverWasapiIfLost(_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? */
277  const int newgen = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
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 
286 static Uint8 *
287 WASAPI_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  }
296  SDL_assert(buffer == NULL);
297  }
298 
299  return (Uint8 *) buffer;
300 }
301 
302 static void
303 WASAPI_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 
311 static void
312 WASAPI_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 
333 static int
334 WASAPI_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);
340  SDL_AudioStreamGet(stream, buffer, cpy);
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 
398 static void
399 WASAPI_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 
423 static void
424 ReleaseWasapiDevice(_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 
464 static void
465 WASAPI_CloseDevice(_THIS)
466 {
467  WASAPI_UnrefDevice(this);
468 }
469 
470 void
472 {
473  SDL_AtomicIncRef(&this->hidden->refcount);
474 }
475 
476 void
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. */
494 int
495 WASAPI_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 
644  SDL_assert(render != NULL);
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 
662 static int
663 WASAPI_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 
701 static void
702 WASAPI_ThreadInit(_THIS)
703 {
705 }
706 
707 static void
708 WASAPI_ThreadDeinit(_THIS)
709 {
711 }
712 
713 void
715 {
716  /* no-op. */
717 }
718 
719 static void
720 WASAPI_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 
735 static int
736 WASAPI_Init(SDL_AudioDriverImpl * impl)
737 {
738  SDL_AtomicSet(&WASAPI_DefaultPlaybackGeneration, 1);
739  SDL_AtomicSet(&WASAPI_DefaultCaptureGeneration, 1);
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 SDL_AudioStreamAvailable
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
Uint8 silence
Definition: SDL_audio.h:183
void WASAPI_UnrefDevice(_THIS)
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:216
void WASAPI_BeginLoopIteration(_THIS)
#define SDL_AudioStreamGet
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
void(* ThreadDeinit)(_THIS)
Definition: SDL_sysaudio.h:70
uint16_t Uint16
Definition: SDL_stdinc.h:191
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
Uint16 samples
Definition: SDL_audio.h:184
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
void WASAPI_PlatformThreadDeinit(_THIS)
#define SDL_realloc
#define AUDIO_S16SYS
Definition: SDL_audio.h:123
void WASAPI_PlatformDeinit(void)
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
GLenum GLsizei len
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1659
GLenum GLuint GLsizei bufsize
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
Definition: testshape.c:29
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_bool retval
#define FAILED(x)
Definition: SDL_directx.h:54
void WASAPI_RefDevice(_THIS)
#define SDL_memcpy
void(* ThreadInit)(_THIS)
Definition: SDL_sysaudio.h:69
void WASAPI_EnumerateEndpoints(void)
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
#define AUDIO_F32SYS
Definition: SDL_audio.h:125
GLuint GLuint stream
AudioBootStrap WASAPI_bootstrap
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:531
Uint8 channels
Definition: SDL_audio.h:182
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:179
#define SDL_free
BOOL WIN_IsWindows7OrGreater(void)
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define SDL_AudioStreamPut
#define SDL_memcmp
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
#define S_OK
Definition: SDL_directx.h:47
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
int WASAPI_PlatformInit(void)
#define SDL_Delay
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)
Definition: SDL_x11sym.h:50
Uint32 size
Definition: SDL_audio.h:186
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
#define SDL_assert(condition)
Definition: SDL_assert.h:169
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
#define NULL
Definition: begin_code.h:167
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
GLuint buffer
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
int WIN_SetError(const char *prefix)
#define SDL_SetError
GLbitfield flags
SDL_atomic_t WASAPI_DefaultCaptureGeneration
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
SDL_AudioFormat format
Definition: SDL_audio.h:181
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
void WASAPI_PlatformDeleteActivationHandler(void *handler)
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
#define SDL_AtomicSet
#define SDL_NewAudioStream
#define SDL_AudioStreamClear
#define AUDIO_S32SYS
Definition: SDL_audio.h:124
#define SDL_AtomicGet
static Uint32 frames
Definition: testsprite2.c:40
void(* BeginLoopIteration)(_THIS)
Definition: SDL_sysaudio.h:71
void WASAPI_PlatformThreadInit(_THIS)
#define SDL_INLINE
Definition: begin_code.h:134
#define SDL_malloc
#define SDL_FreeAudioStream
#define FALSE
Definition: edid-parse.c:34
GLboolean GLboolean GLboolean GLboolean a
GLboolean GLboolean GLboolean b
#define SDL_memset
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:469