Atlas - SDL_wasapi.c

Home / ext / SDL2 / src / audio / wasapi Lines: 1 | Size: 26405 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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. */ 46SDL_atomic_t WASAPI_DefaultPlaybackGeneration; 47SDL_atomic_t WASAPI_DefaultCaptureGeneration; 48 49/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */ 50typedef struct DevIdList 51{ 52 WCHAR *str; 53 struct DevIdList *next; 54} DevIdList; 55 56static DevIdList *deviceid_list = NULL; 57 58/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */ 59static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; 60static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; 61static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; 62static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; 63 64static SDL_bool 65WStrEqual(const WCHAR *a, const WCHAR *b) 66{ 67 while (*a) { 68 if (*a != *b) { 69 return SDL_FALSE; 70 } 71 a++; 72 b++; 73 } 74 return *b == 0; 75} 76 77static size_t 78WStrLen(const WCHAR *wstr) 79{ 80 size_t retval = 0; 81 if (wstr) { 82 while (*(wstr++)) { 83 retval++; 84 } 85 } 86 return retval; 87} 88 89static WCHAR * 90WStrDupe(const WCHAR *wstr) 91{ 92 const size_t len = (WStrLen(wstr) + 1) * sizeof (WCHAR); 93 WCHAR *retval = (WCHAR *) SDL_malloc(len); 94 if (retval) { 95 SDL_memcpy(retval, wstr, len); 96 } 97 return retval; 98} 99 100 101void 102WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid) 103{ 104 DevIdList *i; 105 DevIdList *next; 106 DevIdList *prev = NULL; 107 for (i = deviceid_list; i; i = next) { 108 next = i->next; 109 if (WStrEqual(i->str, devid)) { 110 if (prev) { 111 prev->next = next; 112 } else { 113 deviceid_list = next; 114 } 115 SDL_RemoveAudioDevice(iscapture, i->str); 116 SDL_free(i->str); 117 SDL_free(i); 118 } 119 prev = i; 120 } 121} 122 123void 124WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid) 125{ 126 DevIdList *devidlist; 127 128 /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever). 129 In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for 130 phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be 131 available and switch automatically. (!!! FIXME...?) */ 132 133 /* see if we already have this one. */ 134 for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) { 135 if (WStrEqual(devidlist->str, devid)) { 136 return; /* we already have this. */ 137 } 138 } 139 140 devidlist = (DevIdList *) SDL_malloc(sizeof (*devidlist)); 141 if (!devidlist) { 142 return; /* oh well. */ 143 } 144 145 devid = WStrDupe(devid); 146 if (!devid) { 147 SDL_free(devidlist); 148 return; /* oh well. */ 149 } 150 151 devidlist->str = (WCHAR *) devid; 152 devidlist->next = deviceid_list; 153 deviceid_list = devidlist; 154 155 SDL_AddAudioDevice(iscapture, devname, (void *) devid); 156} 157 158static void 159WASAPI_DetectDevices(void) 160{ 161 WASAPI_EnumerateEndpoints(); 162} 163 164static int 165WASAPI_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 179static SDL_INLINE SDL_bool 180WasapiFailed(_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); 190 SDL_OpenedAudioDeviceDisconnected(this); 191 SDL_assert(!SDL_AtomicGet(&this->enabled)); 192 } 193 194 return SDL_TRUE; 195} 196 197static int 198UpdateAudioStream(_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! */ 210 SDL_FreeAudioStream(this->stream); 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 */ 218 SDL_FreeAudioStream(this->stream); 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 251static void ReleaseWasapiDevice(_THIS); 252 253static SDL_bool 254RecoverWasapiDevice(_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) { 268 SDL_OpenedAudioDeviceDisconnected(this); 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 277static SDL_bool 278RecoverWasapiIfLost(_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 301static Uint8 * 302WASAPI_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 317static void 318WASAPI_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 326static void 327WASAPI_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); 343 SDL_OpenedAudioDeviceDisconnected(this); 344 } 345 } 346} 347 348static int 349WASAPI_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 413static void 414WASAPI_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 438static void 439ReleaseWasapiDevice(_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 479static void 480WASAPI_CloseDevice(_THIS) 481{ 482 WASAPI_UnrefDevice(this); 483} 484 485void 486WASAPI_RefDevice(_THIS) 487{ 488 SDL_AtomicIncRef(&this->hidden->refcount); 489} 490 491void 492WASAPI_UnrefDevice(_THIS) 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. */ 509int 510WASAPI_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 */ 630 SDL_CalculateAudioSpec(&this->spec); 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 677static int 678WASAPI_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 716static void 717WASAPI_ThreadInit(_THIS) 718{ 719 WASAPI_PlatformThreadInit(this); 720} 721 722static void 723WASAPI_ThreadDeinit(_THIS) 724{ 725 WASAPI_PlatformThreadDeinit(this); 726} 727 728static void 729WASAPI_Deinitialize(void) 730{ 731 DevIdList *devidlist; 732 DevIdList *next; 733 734 WASAPI_PlatformDeinit(); 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 744static int 745WASAPI_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; 758 impl->BeginLoopIteration = WASAPI_BeginLoopIteration; 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 773AudioBootStrap WASAPI_bootstrap = { 774 "wasapi", "WASAPI", WASAPI_Init, 0 775}; 776 777#endif /* SDL_AUDIO_DRIVER_WASAPI */ 778 779/* vi: set ts=4 sw=4 expandtab: */ 780
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.