SDL  2.0
SDL_wasapi.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 int
165 WASAPI_GetPendingBytes(_THIS)
166 {
167  UINT32 frames = 0;
168 
169  /* it's okay to fail here; we'll deal with failures in the audio thread. */
170  /* FIXME: need a lock around checking this->hidden->client */
171  if (this->hidden->client != NULL) { /* definitely activated? */
172  if (FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
173  return 0; /* oh well. */
174  }
175  }
176  return ((int) frames) * this->hidden->framesize;
177 }
178 
179 static SDL_INLINE SDL_bool
180 WasapiFailed(_THIS, const HRESULT err)
181 {
182  if (err == S_OK) {
183  return SDL_FALSE;
184  }
185 
186  if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
187  this->hidden->device_lost = SDL_TRUE;
188  } else if (SDL_AtomicGet(&this->enabled)) {
189  IAudioClient_Stop(this->hidden->client);
191  SDL_assert(!SDL_AtomicGet(&this->enabled));
192  }
193 
194  return SDL_TRUE;
195 }
196 
197 static int
198 UpdateAudioStream(_THIS, const SDL_AudioSpec *oldspec)
199 {
200  /* Since WASAPI requires us to handle all audio conversion, and our
201  device format might have changed, we might have to add/remove/change
202  the audio stream that the higher level uses to convert data, so
203  SDL keeps firing the callback as if nothing happened here. */
204 
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) ) {
209  /* no need to buffer/convert in an AudioStream! */
211  this->stream = NULL;
212  } else if ( (oldspec->channels == this->spec.channels) &&
213  (oldspec->format == this->spec.format) &&
214  (oldspec->freq == this->spec.freq) ) {
215  /* The existing audio stream is okay to keep using. */
216  } else {
217  /* replace the audiostream for new format */
219  if (this->iscapture) {
220  this->stream = SDL_NewAudioStream(this->spec.format,
221  this->spec.channels, this->spec.freq,
222  this->callbackspec.format,
223  this->callbackspec.channels,
224  this->callbackspec.freq);
225  } else {
226  this->stream = SDL_NewAudioStream(this->callbackspec.format,
227  this->callbackspec.channels,
228  this->callbackspec.freq, this->spec.format,
229  this->spec.channels, this->spec.freq);
230  }
231 
232  if (!this->stream) {
233  return -1;
234  }
235  }
236 
237  /* make sure our scratch buffer can cover the new device spec. */
238  if (this->spec.size > this->work_buffer_len) {
239  Uint8 *ptr = (Uint8 *) SDL_realloc(this->work_buffer, this->spec.size);
240  if (ptr == NULL) {
241  return SDL_OutOfMemory();
242  }
243  this->work_buffer = ptr;
244  this->work_buffer_len = this->spec.size;
245  }
246 
247  return 0;
248 }
249 
250 
251 static void ReleaseWasapiDevice(_THIS);
252 
253 static SDL_bool
254 RecoverWasapiDevice(_THIS)
255 {
256  ReleaseWasapiDevice(this); /* dump the lost device's handles. */
257 
258  if (this->hidden->default_device_generation) {
259  this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
260  }
261 
262  /* this can fail for lots of reasons, but the most likely is we had a
263  non-default device that was disconnected, so we can't recover. Default
264  devices try to reinitialize whatever the new default is, so it's more
265  likely to carry on here, but this handles a non-default device that
266  simply had its format changed in the Windows Control Panel. */
267  if (WASAPI_ActivateDevice(this, SDL_TRUE) == -1) {
269  return SDL_FALSE;
270  }
271 
272  this->hidden->device_lost = SDL_FALSE;
273 
274  return SDL_TRUE; /* okay, carry on with new device details! */
275 }
276 
277 static SDL_bool
278 RecoverWasapiIfLost(_THIS)
279 {
280  const int generation = this->hidden->default_device_generation;
281  SDL_bool lost = this->hidden->device_lost;
282 
283  if (!SDL_AtomicGet(&this->enabled)) {
284  return SDL_FALSE; /* already failed. */
285  }
286 
287  if (!this->hidden->client) {
288  return SDL_TRUE; /* still waiting for activation. */
289  }
290 
291  if (!lost && (generation > 0)) { /* is a default device? */
292  const int newgen = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
293  if (generation != newgen) { /* the desired default device was changed, jump over to it. */
294  lost = SDL_TRUE;
295  }
296  }
297 
298  return lost ? RecoverWasapiDevice(this) : SDL_TRUE;
299 }
300 
301 static Uint8 *
302 WASAPI_GetDeviceBuf(_THIS)
303 {
304  /* get an endpoint buffer from WASAPI. */
305  BYTE *buffer = NULL;
306 
307  while (RecoverWasapiIfLost(this) && this->hidden->render) {
308  if (!WasapiFailed(this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
309  return (Uint8 *) buffer;
310  }
311  SDL_assert(buffer == NULL);
312  }
313 
314  return (Uint8 *) buffer;
315 }
316 
317 static void
318 WASAPI_PlayDevice(_THIS)
319 {
320  if (this->hidden->render != NULL) { /* definitely activated? */
321  /* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
322  WasapiFailed(this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
323  }
324 }
325 
326 static void
327 WASAPI_WaitDevice(_THIS)
328 {
329  while (RecoverWasapiIfLost(this) && this->hidden->client && this->hidden->event) {
330  /*SDL_Log("WAITDEVICE");*/
331  if (WaitForSingleObjectEx(this->hidden->event, INFINITE, FALSE) == WAIT_OBJECT_0) {
332  const UINT32 maxpadding = this->spec.samples;
333  UINT32 padding = 0;
334  if (!WasapiFailed(this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
335  /*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
336  if (padding <= maxpadding) {
337  break;
338  }
339  }
340  } else {
341  /*SDL_Log("WASAPI FAILED EVENT!");*/
342  IAudioClient_Stop(this->hidden->client);
344  }
345  }
346 }
347 
348 static int
349 WASAPI_CaptureFromDevice(_THIS, void *buffer, int buflen)
350 {
351  SDL_AudioStream *stream = this->hidden->capturestream;
352  const int avail = SDL_AudioStreamAvailable(stream);
353  if (avail > 0) {
354  const int cpy = SDL_min(buflen, avail);
355  SDL_AudioStreamGet(stream, buffer, cpy);
356  return cpy;
357  }
358 
359  while (RecoverWasapiIfLost(this)) {
360  HRESULT ret;
361  BYTE *ptr = NULL;
362  UINT32 frames = 0;
363  DWORD flags = 0;
364 
365  /* uhoh, client isn't activated yet, just return silence. */
366  if (!this->hidden->capture) {
367  /* Delay so we run at about the speed that audio would be arriving. */
368  SDL_Delay(((this->spec.samples * 1000) / this->spec.freq));
369  SDL_memset(buffer, this->spec.silence, buflen);
370  return buflen;
371  }
372 
373  ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
374  if (ret != AUDCLNT_S_BUFFER_EMPTY) {
375  WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
376  }
377 
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;
384  const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
385 
386  if (silent) {
387  SDL_memset(buffer, this->spec.silence, cpy);
388  } else {
389  SDL_memcpy(buffer, ptr, cpy);
390  }
391 
392  if (leftover > 0) {
393  ptr += cpy;
394  if (silent) {
395  SDL_memset(ptr, this->spec.silence, leftover); /* I guess this is safe? */
396  }
397 
398  if (SDL_AudioStreamPut(stream, ptr, leftover) == -1) {
399  return -1; /* uhoh, out of memory, etc. Kill device. :( */
400  }
401  }
402 
403  ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
404  WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
405 
406  return cpy;
407  }
408  }
409 
410  return -1; /* unrecoverable error. */
411 }
412 
413 static void
414 WASAPI_FlushCapture(_THIS)
415 {
416  BYTE *ptr = NULL;
417  UINT32 frames = 0;
418  DWORD flags = 0;
419 
420  if (!this->hidden->capture) {
421  return; /* not activated yet? */
422  }
423 
424  /* just read until we stop getting packets, throwing them away. */
425  while (SDL_TRUE) {
426  const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
427  if (ret == AUDCLNT_S_BUFFER_EMPTY) {
428  break; /* no more buffered data; we're done. */
429  } else if (WasapiFailed(this, ret)) {
430  break; /* failed for some other reason, abort. */
431  } else if (WasapiFailed(this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
432  break; /* something broke. */
433  }
434  }
435  SDL_AudioStreamClear(this->hidden->capturestream);
436 }
437 
438 static void
439 ReleaseWasapiDevice(_THIS)
440 {
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;
446  }
447 
448  if (this->hidden->render) {
449  IAudioRenderClient_Release(this->hidden->render);
450  this->hidden->render = NULL;
451  }
452 
453  if (this->hidden->capture) {
454  IAudioCaptureClient_Release(this->hidden->capture);
455  this->hidden->capture = NULL;
456  }
457 
458  if (this->hidden->waveformat) {
459  CoTaskMemFree(this->hidden->waveformat);
460  this->hidden->waveformat = NULL;
461  }
462 
463  if (this->hidden->capturestream) {
464  SDL_FreeAudioStream(this->hidden->capturestream);
465  this->hidden->capturestream = NULL;
466  }
467 
468  if (this->hidden->activation_handler) {
469  WASAPI_PlatformDeleteActivationHandler(this->hidden->activation_handler);
470  this->hidden->activation_handler = NULL;
471  }
472 
473  if (this->hidden->event) {
474  CloseHandle(this->hidden->event);
475  this->hidden->event = NULL;
476  }
477 }
478 
479 static void
480 WASAPI_CloseDevice(_THIS)
481 {
482  WASAPI_UnrefDevice(this);
483 }
484 
485 void
487 {
488  SDL_AtomicIncRef(&this->hidden->refcount);
489 }
490 
491 void
493 {
494  if (!SDL_AtomicDecRef(&this->hidden->refcount)) {
495  return;
496  }
497 
498  /* actual closing happens here. */
499 
500  /* don't touch this->hidden->task in here; it has to be reverted from
501  our callback thread. We do that in WASAPI_ThreadDeinit().
502  (likewise for this->hidden->coinitialized). */
503  ReleaseWasapiDevice(this);
504  SDL_free(this->hidden->devid);
505  SDL_free(this->hidden);
506 }
507 
508 /* This is called once a device is activated, possibly asynchronously. */
509 int
510 WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
511 {
512  /* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
513  !!! it will write into the kernel's audio buffer directly instead of
514  !!! shared memory that a user-mode mixer then writes to the kernel with
515  !!! everything else. Doing this means any other sound using this device will
516  !!! stop playing, including the user's MP3 player and system notification
517  !!! sounds. You'd probably need to release the device when the app isn't in
518  !!! the foreground, to be a good citizen of the system. It's doable, but it's
519  !!! more work and causes some annoyances, and I don't know what the latency
520  !!! wins actually look like. Maybe add a hint to force exclusive mode at
521  !!! some point. To be sure, defaulting to shared mode is the right thing to
522  !!! do in any case. */
523  const SDL_AudioSpec oldspec = this->spec;
524  const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
525  UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
526  REFERENCE_TIME duration = 0;
527  IAudioClient *client = this->hidden->client;
528  IAudioRenderClient *render = NULL;
529  IAudioCaptureClient *capture = NULL;
530  WAVEFORMATEX *waveformat = NULL;
531  SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
532  SDL_AudioFormat wasapi_format = 0;
533  SDL_bool valid_format = SDL_FALSE;
534  HRESULT ret = S_OK;
535  DWORD streamflags = 0;
536 
537  SDL_assert(client != NULL);
538 
539 #ifdef __WINRT__ /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
540  this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
541 #else
542  this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
543 #endif
544 
545  if (this->hidden->event == NULL) {
546  return WIN_SetError("WASAPI can't create an event handle");
547  }
548 
549  ret = IAudioClient_GetMixFormat(client, &waveformat);
550  if (FAILED(ret)) {
551  return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
552  }
553 
554  SDL_assert(waveformat != NULL);
555  this->hidden->waveformat = waveformat;
556 
557  this->spec.channels = (Uint8) waveformat->nChannels;
558 
559  /* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
560  if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
561  wasapi_format = AUDIO_F32SYS;
562  } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
563  wasapi_format = AUDIO_S16SYS;
564  } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
565  wasapi_format = AUDIO_S32SYS;
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)) {
569  wasapi_format = AUDIO_F32SYS;
570  } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
571  wasapi_format = AUDIO_S16SYS;
572  } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
573  wasapi_format = AUDIO_S32SYS;
574  }
575  }
576 
577  while ((!valid_format) && (test_format)) {
578  if (test_format == wasapi_format) {
579  this->spec.format = test_format;
580  valid_format = SDL_TRUE;
581  break;
582  }
583  test_format = SDL_NextAudioFormat();
584  }
585 
586  if (!valid_format) {
587  return SDL_SetError("WASAPI: Unsupported audio format");
588  }
589 
590  ret = IAudioClient_GetDevicePeriod(client, NULL, &duration);
591  if (FAILED(ret)) {
592  return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
593  }
594 
595  /* favor WASAPI's resampler over our own, in Win7+. */
596  if (this->spec.freq != waveformat->nSamplesPerSec) {
597  /* RATEADJUST only works with output devices in share mode, and is available in Win7 and later.*/
598  if (WIN_IsWindows7OrGreater() && !this->iscapture && (sharemode == AUDCLNT_SHAREMODE_SHARED)) {
599  streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
600  waveformat->nSamplesPerSec = this->spec.freq;
601  waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
602  }
603  else {
604  this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in. */
605  }
606  }
607 
608  streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
609  ret = IAudioClient_Initialize(client, sharemode, streamflags, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, waveformat, NULL);
610  if (FAILED(ret)) {
611  return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
612  }
613 
614  ret = IAudioClient_SetEventHandle(client, this->hidden->event);
615  if (FAILED(ret)) {
616  return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
617  }
618 
619  ret = IAudioClient_GetBufferSize(client, &bufsize);
620  if (FAILED(ret)) {
621  return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
622  }
623 
624  this->spec.samples = (Uint16) bufsize;
625  if (!this->iscapture) {
626  this->spec.samples /= 2; /* fill half of the DMA buffer on each run. */
627  }
628 
629  /* Update the fragment size as size in bytes */
631 
632  this->hidden->framesize = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
633 
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) {
637  return -1; /* already set SDL_Error */
638  }
639 
640  ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void**) &capture);
641  if (FAILED(ret)) {
642  return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
643  }
644 
645  SDL_assert(capture != NULL);
646  this->hidden->capture = capture;
647  ret = IAudioClient_Start(client);
648  if (FAILED(ret)) {
649  return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
650  }
651 
652  WASAPI_FlushCapture(this); /* MSDN says you should flush capture endpoint right after startup. */
653  } else {
654  ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void**) &render);
655  if (FAILED(ret)) {
656  return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
657  }
658 
659  SDL_assert(render != NULL);
660  this->hidden->render = render;
661  ret = IAudioClient_Start(client);
662  if (FAILED(ret)) {
663  return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
664  }
665  }
666 
667  if (updatestream) {
668  if (UpdateAudioStream(this, &oldspec) == -1) {
669  return -1;
670  }
671  }
672 
673  return 0; /* good to go. */
674 }
675 
676 
677 static int
678 WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
679 {
680  LPCWSTR devid = (LPCWSTR) handle;
681 
682  /* Initialize all variables that we clean on shutdown */
683  this->hidden = (struct SDL_PrivateAudioData *)
684  SDL_malloc((sizeof *this->hidden));
685  if (this->hidden == NULL) {
686  return SDL_OutOfMemory();
687  }
688  SDL_zerop(this->hidden);
689 
690  WASAPI_RefDevice(this); /* so CloseDevice() will unref to zero. */
691 
692  if (!devid) { /* is default device? */
693  this->hidden->default_device_generation = SDL_AtomicGet(iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
694  } else {
695  this->hidden->devid = WStrDupe(devid);
696  if (!this->hidden->devid) {
697  return SDL_OutOfMemory();
698  }
699  }
700 
701  if (WASAPI_ActivateDevice(this, SDL_FALSE) == -1) {
702  return -1; /* already set error. */
703  }
704 
705  /* Ready, but waiting for async device activation.
706  Until activation is successful, we will report silence from capture
707  devices and ignore data on playback devices.
708  Also, since we don't know the _actual_ device format until after
709  activation, we let the app have whatever it asks for. We set up
710  an SDL_AudioStream to convert, if necessary, once the activation
711  completes. */
712 
713  return 0;
714 }
715 
716 static void
717 WASAPI_ThreadInit(_THIS)
718 {
720 }
721 
722 static void
723 WASAPI_ThreadDeinit(_THIS)
724 {
726 }
727 
728 static void
729 WASAPI_Deinitialize(void)
730 {
731  DevIdList *devidlist;
732  DevIdList *next;
733 
735 
736  for (devidlist = deviceid_list; devidlist; devidlist = next) {
737  next = devidlist->next;
738  SDL_free(devidlist->str);
739  SDL_free(devidlist);
740  }
741  deviceid_list = NULL;
742 }
743 
744 static int
745 WASAPI_Init(SDL_AudioDriverImpl * impl)
746 {
747  SDL_AtomicSet(&WASAPI_DefaultPlaybackGeneration, 1);
748  SDL_AtomicSet(&WASAPI_DefaultCaptureGeneration, 1);
749 
750  if (WASAPI_PlatformInit() == -1) {
751  return 0;
752  }
753 
754  /* Set the function pointers */
755  impl->DetectDevices = WASAPI_DetectDevices;
756  impl->ThreadInit = WASAPI_ThreadInit;
757  impl->ThreadDeinit = WASAPI_ThreadDeinit;
759  impl->OpenDevice = WASAPI_OpenDevice;
760  impl->PlayDevice = WASAPI_PlayDevice;
761  impl->WaitDevice = WASAPI_WaitDevice;
762  impl->GetPendingBytes = WASAPI_GetPendingBytes;
763  impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
764  impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
765  impl->FlushCapture = WASAPI_FlushCapture;
766  impl->CloseDevice = WASAPI_CloseDevice;
767  impl->Deinitialize = WASAPI_Deinitialize;
768  impl->HasCaptureSupport = 1;
769 
770  return 1; /* this audio target is available. */
771 }
772 
774  "wasapi", "WASAPI", WASAPI_Init, 0
775 };
776 
777 #endif /* SDL_AUDIO_DRIVER_WASAPI */
778 
779 /* 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:1584
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:182
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:198
void WASAPI_BeginLoopIteration(_THIS)
#define SDL_AudioStreamGet
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
void(* ThreadDeinit)(_THIS)
Definition: SDL_sysaudio.h:70
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
Uint16 samples
Definition: SDL_audio.h:183
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:449
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:1596
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:490
Uint8 channels
Definition: SDL_audio.h:181
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#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:83
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
#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:234
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:185
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:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
GLuint buffer
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:76
int WIN_SetError(const char *prefix)
#define SDL_SetError
GLbitfield flags
SDL_atomic_t WASAPI_DefaultCaptureGeneration
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
SDL_AudioFormat format
Definition: SDL_audio.h:180
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:77
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:244
void WASAPI_PlatformDeleteActivationHandler(void *handler)
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_AtomicSet
#define SDL_NewAudioStream
#define SDL_AudioStreamClear
#define AUDIO_S32SYS
Definition: SDL_audio.h:124
#define SDL_AtomicGet
uint16_t Uint16
Definition: SDL_stdinc.h:169
void(* BeginLoopIteration)(_THIS)
Definition: SDL_sysaudio.h:71
void WASAPI_PlatformThreadInit(_THIS)
#define SDL_INLINE
Definition: begin_code.h:131
#define SDL_malloc
#define SDL_FreeAudioStream
int(* GetPendingBytes)(_THIS)
Definition: SDL_sysaudio.h:74
#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:432