SDL  2.0
SDL_wasapi.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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);
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 {
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: */
render
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
Definition: testshape.c:29
SDL_memset
#define SDL_memset
Definition: SDL_dynapi_overrides.h:386
SDL_FirstAudioFormat
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
SDL_AudioSpec::channels
Uint8 channels
Definition: SDL_audio.h:182
SDL_AudioDriverImpl::HasCaptureSupport
int HasCaptureSupport
Definition: SDL_sysaudio.h:90
WIN_IsWindows7OrGreater
BOOL WIN_IsWindows7OrGreater(void)
WASAPI_AddDevice
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
if
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 &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
Definition: pixman-arm-neon-asm.h:469
WASAPI_PlatformDeleteActivationHandler
void WASAPI_PlatformDeleteActivationHandler(void *handler)
WASAPI_PlatformInit
int WASAPI_PlatformInit(void)
SDL_AudioDriverImpl::FlushCapture
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
NULL
#define NULL
Definition: begin_code.h:167
SDL_AudioStreamAvailable
#define SDL_AudioStreamAvailable
Definition: SDL_dynapi_overrides.h:647
handle
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_timer.h
b
GLboolean GLboolean GLboolean b
Definition: SDL_opengl_glext.h:1112
SDL_AudioSpec::samples
Uint16 samples
Definition: SDL_audio.h:184
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:419
SDL_log.h
SDL_AudioSpec::format
SDL_AudioFormat format
Definition: SDL_audio.h:181
SDL_wasapi.h
stream
GLuint GLuint stream
Definition: SDL_opengl_glext.h:1779
SDL_realloc
#define SDL_realloc
Definition: SDL_dynapi_overrides.h:376
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
SDL_AudioDriverImpl::OpenDevice
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
SDL_AudioStreamGet
#define SDL_AudioStreamGet
Definition: SDL_dynapi_overrides.h:645
SDL_AudioSpec
Definition: SDL_audio.h:178
a
GLboolean GLboolean GLboolean GLboolean a
Definition: SDL_opengl_glext.h:1112
SDL_NextAudioFormat
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1659
SDL_OpenedAudioDeviceDisconnected
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
SDL_PrivateAudioData::iscapture
SDL_bool iscapture
Definition: SDL_qsa_audio.h:37
SDL_AudioFormat
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
AudioBootStrap
Definition: SDL_sysaudio.h:176
AUDIO_S16SYS
#define AUDIO_S16SYS
Definition: SDL_audio.h:128
SDL_PrivateAudioData
Definition: SDL_alsa_audio.h:33
WASAPI_PlatformThreadDeinit
void WASAPI_PlatformThreadDeinit(_THIS)
SDL_AtomicDecRef
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
SDL_AudioDriverImpl::WaitDevice
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
len
GLenum GLsizei len
Definition: SDL_opengl_glext.h:2929
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
buffer
GLuint buffer
Definition: SDL_opengl_glext.h:536
SDL_audio.h
retval
SDL_bool retval
Definition: testgamecontroller.c:65
SDL_AudioDriverImpl
Definition: SDL_sysaudio.h:65
SDL_memcmp
#define SDL_memcmp
Definition: SDL_dynapi_overrides.h:389
SDL_INLINE
#define SDL_INLINE
Definition: begin_code.h:134
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_AudioDriverImpl::ThreadDeinit
void(* ThreadDeinit)(_THIS)
Definition: SDL_sysaudio.h:70
WASAPI_UnrefDevice
void WASAPI_UnrefDevice(_THIS)
AUDIO_F32SYS
#define AUDIO_F32SYS
Definition: SDL_audio.h:130
SDL_RemoveAudioDevice
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:531
Uint16
uint16_t Uint16
Definition: SDL_stdinc.h:191
bufsize
GLenum GLuint GLsizei bufsize
Definition: SDL_opengl_glext.h:1765
SDL_assert.h
SDL_CalculateAudioSpec
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1686
SDL_min
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_AudioDriverImpl::DetectDevices
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
WASAPI_bootstrap
AudioBootStrap WASAPI_bootstrap
SDL_AudioStreamPut
#define SDL_AudioStreamPut
Definition: SDL_dynapi_overrides.h:644
SDL_AUDIO_BITSIZE
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
SDL_AudioDriverImpl::PlayDevice
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
WASAPI_RefDevice
void WASAPI_RefDevice(_THIS)
SDL_Delay
#define SDL_Delay
Definition: SDL_dynapi_overrides.h:486
WASAPI_RemoveDevice
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
SDL_AudioSpec::freq
int freq
Definition: SDL_audio.h:180
WIN_SetErrorFromHRESULT
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
SDL_AudioDriverImpl::BeginLoopIteration
void(* BeginLoopIteration)(_THIS)
Definition: SDL_sysaudio.h:71
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_AudioSpec::silence
Uint8 silence
Definition: SDL_audio.h:183
WASAPI_PlatformDeinit
void WASAPI_PlatformDeinit(void)
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_AudioDriverImpl::ThreadInit
void(* ThreadInit)(_THIS)
Definition: SDL_sysaudio.h:69
spec
SDL_AudioSpec spec
Definition: loopwave.c:31
WASAPI_DefaultPlaybackGeneration
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
SDL_AudioDriverImpl::CaptureFromDevice
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
SDL_AudioSpec::size
Uint32 size
Definition: SDL_audio.h:186
frames
static Uint32 frames
Definition: testsprite2.c:40
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_PrivateAudioData::devid
WCHAR * devid
Definition: SDL_wasapi.h:42
WASAPI_DefaultCaptureGeneration
SDL_atomic_t WASAPI_DefaultCaptureGeneration
SDL_AudioStreamClear
#define SDL_AudioStreamClear
Definition: SDL_dynapi_overrides.h:646
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
S_OK
#define S_OK
Definition: SDL_directx.h:47
WIN_SetError
int WIN_SetError(const char *prefix)
AUDIO_S32SYS
#define AUDIO_S32SYS
Definition: SDL_audio.h:129
SDL_NewAudioStream
#define SDL_NewAudioStream
Definition: SDL_dynapi_overrides.h:643
SDL_AudioDriverImpl::GetDeviceBuf
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
SDL_atomic_t
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
FAILED
#define FAILED(x)
Definition: SDL_directx.h:54
WASAPI_PlatformThreadInit
void WASAPI_PlatformThreadInit(_THIS)
enabled
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: SDL_opengl_glext.h:2482
WASAPI_ActivateDevice
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
WASAPI_EnumerateEndpoints
void WASAPI_EnumerateEndpoints(void)
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_FreeAudioStream
#define SDL_FreeAudioStream
Definition: SDL_dynapi_overrides.h:648
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
flags
GLbitfield flags
Definition: SDL_opengl_glext.h:1483
ptr
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
Definition: pixman-arm-simd-asm.h:171
SDL_AudioDriverImpl::CloseDevice
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
SDL_AddAudioDevice
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:469
WASAPI_PrepDevice
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
i
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
FALSE
#define FALSE
Definition: edid-parse.c:34
SDL_AudioDriverImpl::Deinitialize
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
WASAPI_BeginLoopIteration
void WASAPI_BeginLoopIteration(_THIS)
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179