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 #include "../../SDL_internal.h"
22 
23 #if SDL_AUDIO_DRIVER_WASAPI
24 
25 #include "../../core/windows/SDL_windows.h"
26 #include "SDL_audio.h"
27 #include "SDL_timer.h"
28 #include "../SDL_audio_c.h"
29 #include "../SDL_sysaudio.h"
30 
31 #define COBJMACROS
32 #include <mmdeviceapi.h>
33 #include <audioclient.h>
34 
35 #include "SDL_wasapi.h"
36 
37 /* This constant isn't available on MinGW-w64 */
38 #ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
39 #define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
40 #endif
41 
42 /* these increment as default devices change. Opened default devices pick up changes in their threads. */
45 
46 /* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
47 typedef struct DevIdList
48 {
49  WCHAR *str;
50  struct DevIdList *next;
51 } DevIdList;
52 
53 static DevIdList *deviceid_list = NULL;
54 
55 /* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
56 static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
57 static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
58 static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
59 static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
60 
61 static SDL_bool
62 WStrEqual(const WCHAR *a, const WCHAR *b)
63 {
64  while (*a) {
65  if (*a != *b) {
66  return SDL_FALSE;
67  }
68  a++;
69  b++;
70  }
71  return *b == 0;
72 }
73 
74 static size_t
75 WStrLen(const WCHAR *wstr)
76 {
77  size_t retval = 0;
78  if (wstr) {
79  while (*(wstr++)) {
80  retval++;
81  }
82  }
83  return retval;
84 }
85 
86 static WCHAR *
87 WStrDupe(const WCHAR *wstr)
88 {
89  const size_t len = (WStrLen(wstr) + 1) * sizeof (WCHAR);
90  WCHAR *retval = (WCHAR *) SDL_malloc(len);
91  if (retval) {
92  SDL_memcpy(retval, wstr, len);
93  }
94  return retval;
95 }
96 
97 
98 void
99 WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
100 {
101  DevIdList *i;
102  DevIdList *next;
103  DevIdList *prev = NULL;
104  for (i = deviceid_list; i; i = next) {
105  next = i->next;
106  if (WStrEqual(i->str, devid)) {
107  if (prev) {
108  prev->next = next;
109  } else {
110  deviceid_list = next;
111  }
112  SDL_RemoveAudioDevice(iscapture, i->str);
113  SDL_free(i->str);
114  SDL_free(i);
115  }
116  prev = i;
117  }
118 }
119 
120 void
121 WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
122 {
123  DevIdList *devidlist;
124 
125  /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
126  In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
127  phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
128  available and switch automatically. (!!! FIXME...?) */
129 
130  /* see if we already have this one. */
131  for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
132  if (WStrEqual(devidlist->str, devid)) {
133  return; /* we already have this. */
134  }
135  }
136 
137  devidlist = (DevIdList *) SDL_malloc(sizeof (*devidlist));
138  if (!devidlist) {
139  return; /* oh well. */
140  }
141 
142  devid = WStrDupe(devid);
143  if (!devid) {
144  SDL_free(devidlist);
145  return; /* oh well. */
146  }
147 
148  devidlist->str = (WCHAR *) devid;
149  devidlist->next = deviceid_list;
150  deviceid_list = devidlist;
151 
152  SDL_AddAudioDevice(iscapture, devname, (void *) devid);
153 }
154 
155 static void
156 WASAPI_DetectDevices(void)
157 {
159 }
160 
161 static SDL_INLINE SDL_bool
162 WasapiFailed(_THIS, const HRESULT err)
163 {
164  if (err == S_OK) {
165  return SDL_FALSE;
166  }
167 
168  if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
169  this->hidden->device_lost = SDL_TRUE;
170  } else if (SDL_AtomicGet(&this->enabled)) {
171  IAudioClient_Stop(this->hidden->client);
173  SDL_assert(!SDL_AtomicGet(&this->enabled));
174  }
175 
176  return SDL_TRUE;
177 }
178 
179 static int
180 UpdateAudioStream(_THIS, const SDL_AudioSpec *oldspec)
181 {
182  /* Since WASAPI requires us to handle all audio conversion, and our
183  device format might have changed, we might have to add/remove/change
184  the audio stream that the higher level uses to convert data, so
185  SDL keeps firing the callback as if nothing happened here. */
186 
187  if ( (this->callbackspec.channels == this->spec.channels) &&
188  (this->callbackspec.format == this->spec.format) &&
189  (this->callbackspec.freq == this->spec.freq) &&
190  (this->callbackspec.samples == this->spec.samples) ) {
191  /* no need to buffer/convert in an AudioStream! */
193  this->stream = NULL;
194  } else if ( (oldspec->channels == this->spec.channels) &&
195  (oldspec->format == this->spec.format) &&
196  (oldspec->freq == this->spec.freq) ) {
197  /* The existing audio stream is okay to keep using. */
198  } else {
199  /* replace the audiostream for new format */
201  if (this->iscapture) {
202  this->stream = SDL_NewAudioStream(this->spec.format,
203  this->spec.channels, this->spec.freq,
204  this->callbackspec.format,
205  this->callbackspec.channels,
206  this->callbackspec.freq);
207  } else {
208  this->stream = SDL_NewAudioStream(this->callbackspec.format,
209  this->callbackspec.channels,
210  this->callbackspec.freq, this->spec.format,
211  this->spec.channels, this->spec.freq);
212  }
213 
214  if (!this->stream) {
215  return -1;
216  }
217  }
218 
219  /* make sure our scratch buffer can cover the new device spec. */
220  if (this->spec.size > this->work_buffer_len) {
221  Uint8 *ptr = (Uint8 *) SDL_realloc(this->work_buffer, this->spec.size);
222  if (ptr == NULL) {
223  return SDL_OutOfMemory();
224  }
225  this->work_buffer = ptr;
226  this->work_buffer_len = this->spec.size;
227  }
228 
229  return 0;
230 }
231 
232 
233 static void ReleaseWasapiDevice(_THIS);
234 
235 static SDL_bool
236 RecoverWasapiDevice(_THIS)
237 {
238  ReleaseWasapiDevice(this); /* dump the lost device's handles. */
239 
240  if (this->hidden->default_device_generation) {
241  this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
242  }
243 
244  /* this can fail for lots of reasons, but the most likely is we had a
245  non-default device that was disconnected, so we can't recover. Default
246  devices try to reinitialize whatever the new default is, so it's more
247  likely to carry on here, but this handles a non-default device that
248  simply had its format changed in the Windows Control Panel. */
249  if (WASAPI_ActivateDevice(this, SDL_TRUE) == -1) {
251  return SDL_FALSE;
252  }
253 
254  this->hidden->device_lost = SDL_FALSE;
255 
256  return SDL_TRUE; /* okay, carry on with new device details! */
257 }
258 
259 static SDL_bool
260 RecoverWasapiIfLost(_THIS)
261 {
262  const int generation = this->hidden->default_device_generation;
263  SDL_bool lost = this->hidden->device_lost;
264 
265  if (!SDL_AtomicGet(&this->enabled)) {
266  return SDL_FALSE; /* already failed. */
267  }
268 
269  if (!this->hidden->client) {
270  return SDL_TRUE; /* still waiting for activation. */
271  }
272 
273  if (!lost && (generation > 0)) { /* is a default device? */
274  const int newgen = SDL_AtomicGet(this->iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
275  if (generation != newgen) { /* the desired default device was changed, jump over to it. */
276  lost = SDL_TRUE;
277  }
278  }
279 
280  return lost ? RecoverWasapiDevice(this) : SDL_TRUE;
281 }
282 
283 static Uint8 *
284 WASAPI_GetDeviceBuf(_THIS)
285 {
286  /* get an endpoint buffer from WASAPI. */
287  BYTE *buffer = NULL;
288 
289  while (RecoverWasapiIfLost(this) && this->hidden->render) {
290  if (!WasapiFailed(this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
291  return (Uint8 *) buffer;
292  }
293  SDL_assert(buffer == NULL);
294  }
295 
296  return (Uint8 *) buffer;
297 }
298 
299 static void
300 WASAPI_PlayDevice(_THIS)
301 {
302  if (this->hidden->render != NULL) { /* definitely activated? */
303  /* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
304  WasapiFailed(this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
305  }
306 }
307 
308 static void
309 WASAPI_WaitDevice(_THIS)
310 {
311  while (RecoverWasapiIfLost(this) && this->hidden->client && this->hidden->event) {
312  DWORD waitResult = WaitForSingleObjectEx(this->hidden->event, 200, FALSE);
313  if (waitResult == WAIT_OBJECT_0) {
314  const UINT32 maxpadding = this->spec.samples;
315  UINT32 padding = 0;
316  if (!WasapiFailed(this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
317  /*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
318  if (padding <= maxpadding) {
319  break;
320  }
321  }
322  } else if (waitResult != WAIT_TIMEOUT) {
323  /*SDL_Log("WASAPI FAILED EVENT!");*/
324  IAudioClient_Stop(this->hidden->client);
326  }
327  }
328 }
329 
330 static int
331 WASAPI_CaptureFromDevice(_THIS, void *buffer, int buflen)
332 {
333  SDL_AudioStream *stream = this->hidden->capturestream;
334  const int avail = SDL_AudioStreamAvailable(stream);
335  if (avail > 0) {
336  const int cpy = SDL_min(buflen, avail);
338  return cpy;
339  }
340 
341  while (RecoverWasapiIfLost(this)) {
342  HRESULT ret;
343  BYTE *ptr = NULL;
344  UINT32 frames = 0;
345  DWORD flags = 0;
346 
347  /* uhoh, client isn't activated yet, just return silence. */
348  if (!this->hidden->capture) {
349  /* Delay so we run at about the speed that audio would be arriving. */
350  SDL_Delay(((this->spec.samples * 1000) / this->spec.freq));
351  SDL_memset(buffer, this->spec.silence, buflen);
352  return buflen;
353  }
354 
355  ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
356  if (ret != AUDCLNT_S_BUFFER_EMPTY) {
357  WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
358  }
359 
360  if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
361  WASAPI_WaitDevice(this);
362  } else if (ret == S_OK) {
363  const int total = ((int) frames) * this->hidden->framesize;
364  const int cpy = SDL_min(buflen, total);
365  const int leftover = total - cpy;
366  const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
367 
368  if (silent) {
369  SDL_memset(buffer, this->spec.silence, cpy);
370  } else {
371  SDL_memcpy(buffer, ptr, cpy);
372  }
373 
374  if (leftover > 0) {
375  ptr += cpy;
376  if (silent) {
377  SDL_memset(ptr, this->spec.silence, leftover); /* I guess this is safe? */
378  }
379 
380  if (SDL_AudioStreamPut(stream, ptr, leftover) == -1) {
381  return -1; /* uhoh, out of memory, etc. Kill device. :( */
382  }
383  }
384 
385  ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
386  WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
387 
388  return cpy;
389  }
390  }
391 
392  return -1; /* unrecoverable error. */
393 }
394 
395 static void
396 WASAPI_FlushCapture(_THIS)
397 {
398  BYTE *ptr = NULL;
399  UINT32 frames = 0;
400  DWORD flags = 0;
401 
402  if (!this->hidden->capture) {
403  return; /* not activated yet? */
404  }
405 
406  /* just read until we stop getting packets, throwing them away. */
407  while (SDL_TRUE) {
408  const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
409  if (ret == AUDCLNT_S_BUFFER_EMPTY) {
410  break; /* no more buffered data; we're done. */
411  } else if (WasapiFailed(this, ret)) {
412  break; /* failed for some other reason, abort. */
413  } else if (WasapiFailed(this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
414  break; /* something broke. */
415  }
416  }
417  SDL_AudioStreamClear(this->hidden->capturestream);
418 }
419 
420 static void
421 ReleaseWasapiDevice(_THIS)
422 {
423  if (this->hidden->client) {
424  IAudioClient_Stop(this->hidden->client);
425  IAudioClient_SetEventHandle(this->hidden->client, NULL);
426  IAudioClient_Release(this->hidden->client);
427  this->hidden->client = NULL;
428  }
429 
430  if (this->hidden->render) {
431  IAudioRenderClient_Release(this->hidden->render);
432  this->hidden->render = NULL;
433  }
434 
435  if (this->hidden->capture) {
436  IAudioCaptureClient_Release(this->hidden->capture);
437  this->hidden->capture = NULL;
438  }
439 
440  if (this->hidden->waveformat) {
441  CoTaskMemFree(this->hidden->waveformat);
442  this->hidden->waveformat = NULL;
443  }
444 
445  if (this->hidden->capturestream) {
446  SDL_FreeAudioStream(this->hidden->capturestream);
447  this->hidden->capturestream = NULL;
448  }
449 
450  if (this->hidden->activation_handler) {
451  WASAPI_PlatformDeleteActivationHandler(this->hidden->activation_handler);
452  this->hidden->activation_handler = NULL;
453  }
454 
455  if (this->hidden->event) {
456  CloseHandle(this->hidden->event);
457  this->hidden->event = NULL;
458  }
459 }
460 
461 static void
462 WASAPI_CloseDevice(_THIS)
463 {
464  WASAPI_UnrefDevice(this);
465 }
466 
467 void
469 {
470  SDL_AtomicIncRef(&this->hidden->refcount);
471 }
472 
473 void
475 {
476  if (!SDL_AtomicDecRef(&this->hidden->refcount)) {
477  return;
478  }
479 
480  /* actual closing happens here. */
481 
482  /* don't touch this->hidden->task in here; it has to be reverted from
483  our callback thread. We do that in WASAPI_ThreadDeinit().
484  (likewise for this->hidden->coinitialized). */
485  ReleaseWasapiDevice(this);
486  SDL_free(this->hidden->devid);
487  SDL_free(this->hidden);
488 }
489 
490 /* This is called once a device is activated, possibly asynchronously. */
491 int
492 WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
493 {
494  /* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
495  !!! it will write into the kernel's audio buffer directly instead of
496  !!! shared memory that a user-mode mixer then writes to the kernel with
497  !!! everything else. Doing this means any other sound using this device will
498  !!! stop playing, including the user's MP3 player and system notification
499  !!! sounds. You'd probably need to release the device when the app isn't in
500  !!! the foreground, to be a good citizen of the system. It's doable, but it's
501  !!! more work and causes some annoyances, and I don't know what the latency
502  !!! wins actually look like. Maybe add a hint to force exclusive mode at
503  !!! some point. To be sure, defaulting to shared mode is the right thing to
504  !!! do in any case. */
505  const SDL_AudioSpec oldspec = this->spec;
506  const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
507  UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
508  REFERENCE_TIME default_period = 0;
509  IAudioClient *client = this->hidden->client;
510  IAudioRenderClient *render = NULL;
511  IAudioCaptureClient *capture = NULL;
512  WAVEFORMATEX *waveformat = NULL;
513  SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
514  SDL_AudioFormat wasapi_format = 0;
515  SDL_bool valid_format = SDL_FALSE;
516  HRESULT ret = S_OK;
517  DWORD streamflags = 0;
518 
519  SDL_assert(client != NULL);
520 
521 #ifdef __WINRT__ /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
522  this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
523 #else
524  this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
525 #endif
526 
527  if (this->hidden->event == NULL) {
528  return WIN_SetError("WASAPI can't create an event handle");
529  }
530 
531  ret = IAudioClient_GetMixFormat(client, &waveformat);
532  if (FAILED(ret)) {
533  return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
534  }
535 
536  SDL_assert(waveformat != NULL);
537  this->hidden->waveformat = waveformat;
538 
539  this->spec.channels = (Uint8) waveformat->nChannels;
540 
541  /* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
542  if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
543  wasapi_format = AUDIO_F32SYS;
544  } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
545  wasapi_format = AUDIO_S16SYS;
546  } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
547  wasapi_format = AUDIO_S32SYS;
548  } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
549  const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *) waveformat;
550  if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
551  wasapi_format = AUDIO_F32SYS;
552  } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
553  wasapi_format = AUDIO_S16SYS;
554  } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
555  wasapi_format = AUDIO_S32SYS;
556  }
557  }
558 
559  while ((!valid_format) && (test_format)) {
560  if (test_format == wasapi_format) {
561  this->spec.format = test_format;
562  valid_format = SDL_TRUE;
563  break;
564  }
565  test_format = SDL_NextAudioFormat();
566  }
567 
568  if (!valid_format) {
569  return SDL_SetError("WASAPI: Unsupported audio format");
570  }
571 
572  ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
573  if (FAILED(ret)) {
574  return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
575  }
576 
577  /* favor WASAPI's resampler over our own, in Win7+. */
578  if (this->spec.freq != waveformat->nSamplesPerSec) {
579  /* RATEADJUST only works with output devices in share mode, and is available in Win7 and later.*/
580  if (WIN_IsWindows7OrGreater() && !this->iscapture && (sharemode == AUDCLNT_SHAREMODE_SHARED)) {
581  streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
582  waveformat->nSamplesPerSec = this->spec.freq;
583  waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
584  }
585  else {
586  this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in. */
587  }
588  }
589 
590  streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
591  ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
592  if (FAILED(ret)) {
593  return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
594  }
595 
596  ret = IAudioClient_SetEventHandle(client, this->hidden->event);
597  if (FAILED(ret)) {
598  return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
599  }
600 
601  ret = IAudioClient_GetBufferSize(client, &bufsize);
602  if (FAILED(ret)) {
603  return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
604  }
605 
606  /* Match the callback size to the period size to cut down on the number of
607  interrupts waited for in each call to WaitDevice */
608  {
609  const float period_millis = default_period / 10000.0f;
610  const float period_frames = period_millis * this->spec.freq / 1000.0f;
611  this->spec.samples = (Uint16)SDL_ceilf(period_frames);
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: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:171
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:534
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:472
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1689
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1650
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:489
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1662
#define AUDIO_S16SYS
Definition: SDL_audio.h:123
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define AUDIO_S32SYS
Definition: SDL_audio.h:124
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define AUDIO_F32SYS
Definition: SDL_audio.h:125
#define S_OK
Definition: SDL_directx.h:47
#define FAILED(x)
Definition: SDL_directx.h:54
#define SDL_AtomicSet
#define SDL_SetError
#define SDL_memset
#define SDL_AudioStreamGet
#define SDL_AudioStreamClear
#define SDL_NewAudioStream
#define SDL_AudioStreamAvailable
#define SDL_FreeAudioStream
#define SDL_malloc
#define SDL_realloc
#define SDL_ceilf
#define SDL_free
#define SDL_AudioStreamPut
#define SDL_memcmp
#define SDL_Delay
#define SDL_AtomicGet
#define SDL_memcpy
#define SDL_OutOfMemory()
Definition: SDL_error.h:88
GLboolean GLboolean GLboolean b
GLuint GLuint stream
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum GLsizei len
GLboolean GLboolean GLboolean GLboolean a
GLuint buffer
GLbitfield flags
GLenum GLuint GLsizei bufsize
uint16_t Uint16
Definition: SDL_stdinc.h:197
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
uint8_t Uint8
Definition: SDL_stdinc.h:185
#define SDL_zerop(x)
Definition: SDL_stdinc.h:427
#define SDL_min(x, y)
Definition: SDL_stdinc.h:412
AudioBootStrap WASAPI_bootstrap
void WASAPI_PlatformDeleteActivationHandler(void *handler)
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
SDL_atomic_t WASAPI_DefaultPlaybackGeneration
void WASAPI_PlatformThreadDeinit(_THIS)
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
void WASAPI_RefDevice(_THIS)
void WASAPI_BeginLoopIteration(_THIS)
SDL_atomic_t WASAPI_DefaultCaptureGeneration
void WASAPI_PlatformThreadInit(_THIS)
int WASAPI_PlatformInit(void)
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
void WASAPI_EnumerateEndpoints(void)
void WASAPI_PlatformDeinit(void)
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
void WASAPI_UnrefDevice(_THIS)
BOOL WIN_IsWindows7OrGreater(void)
int WIN_SetError(const char *prefix)
int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:163
#define SDL_INLINE
Definition: begin_code.h:130
#define FALSE
Definition: edid-parse.c:34
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_AudioSpec spec
Definition: loopwave.c:31
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
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
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
void(* ThreadDeinit)(_THIS)
Definition: SDL_sysaudio.h:70
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
void(* BeginLoopIteration)(_THIS)
Definition: SDL_sysaudio.h:71
void(* ThreadInit)(_THIS)
Definition: SDL_sysaudio.h:69
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
Uint32 size
Definition: SDL_audio.h:186
Uint16 samples
Definition: SDL_audio.h:184
Uint8 channels
Definition: SDL_audio.h:182
Uint8 silence
Definition: SDL_audio.h:183
SDL_AudioFormat format
Definition: SDL_audio.h:181
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_bool retval
void render(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect texture_dimensions)
Definition: testshape.c:29
static Uint32 frames
Definition: testsprite2.c:40
typedef int(__stdcall *FARPROC)()