Atlas - SDL_directsound.c

Home / ext / SDL2 / src / audio / directsound Lines: 2 | Size: 19502 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#include "../../SDL_internal.h" 22 23#if SDL_AUDIO_DRIVER_DSOUND 24 25/* Allow access to a raw mixing buffer */ 26 27#include "SDL_assert.h" 28#include "SDL_timer.h" 29#include "SDL_loadso.h" 30#include "SDL_audio.h" 31#include "../SDL_audio_c.h" 32#include "SDL_directsound.h" 33 34#ifndef WAVE_FORMAT_IEEE_FLOAT 35#define WAVE_FORMAT_IEEE_FLOAT 0x0003 36#endif 37 38/* DirectX function pointers for audio */ 39static void* DSoundDLL = NULL; 40typedef HRESULT (WINAPI *fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN); 41typedef HRESULT (WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); 42typedef HRESULT (WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID,LPDIRECTSOUNDCAPTURE8 *,LPUNKNOWN); 43typedef HRESULT (WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID); 44static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL; 45static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL; 46static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL; 47static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL; 48 49static void 50DSOUND_Unload(void) 51{ 52 pDirectSoundCreate8 = NULL; 53 pDirectSoundEnumerateW = NULL; 54 pDirectSoundCaptureCreate8 = NULL; 55 pDirectSoundCaptureEnumerateW = NULL; 56 57 if (DSoundDLL != NULL) { 58 SDL_UnloadObject(DSoundDLL); 59 DSoundDLL = NULL; 60 } 61} 62 63 64static int 65DSOUND_Load(void) 66{ 67 int loaded = 0; 68 69 DSOUND_Unload(); 70 71 DSoundDLL = SDL_LoadObject("DSOUND.DLL"); 72 if (DSoundDLL == NULL) { 73 SDL_SetError("DirectSound: failed to load DSOUND.DLL"); 74 } else { 75 /* Now make sure we have DirectX 8 or better... */ 76 #define DSOUNDLOAD(f) { \ 77 p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \ 78 if (!p##f) loaded = 0; \ 79 } 80 loaded = 1; /* will reset if necessary. */ 81 DSOUNDLOAD(DirectSoundCreate8); 82 DSOUNDLOAD(DirectSoundEnumerateW); 83 DSOUNDLOAD(DirectSoundCaptureCreate8); 84 DSOUNDLOAD(DirectSoundCaptureEnumerateW); 85 #undef DSOUNDLOAD 86 87 if (!loaded) { 88 SDL_SetError("DirectSound: System doesn't appear to have DX8."); 89 } 90 } 91 92 if (!loaded) { 93 DSOUND_Unload(); 94 } 95 96 return loaded; 97} 98 99static int 100SetDSerror(const char *function, int code) 101{ 102 static const char *error; 103 static char errbuf[1024]; 104 105 errbuf[0] = 0; 106 switch (code) { 107 case E_NOINTERFACE: 108 error = "Unsupported interface -- Is DirectX 8.0 or later installed?"; 109 break; 110 case DSERR_ALLOCATED: 111 error = "Audio device in use"; 112 break; 113 case DSERR_BADFORMAT: 114 error = "Unsupported audio format"; 115 break; 116 case DSERR_BUFFERLOST: 117 error = "Mixing buffer was lost"; 118 break; 119 case DSERR_CONTROLUNAVAIL: 120 error = "Control requested is not available"; 121 break; 122 case DSERR_INVALIDCALL: 123 error = "Invalid call for the current state"; 124 break; 125 case DSERR_INVALIDPARAM: 126 error = "Invalid parameter"; 127 break; 128 case DSERR_NODRIVER: 129 error = "No audio device found"; 130 break; 131 case DSERR_OUTOFMEMORY: 132 error = "Out of memory"; 133 break; 134 case DSERR_PRIOLEVELNEEDED: 135 error = "Caller doesn't have priority"; 136 break; 137 case DSERR_UNSUPPORTED: 138 error = "Function not supported"; 139 break; 140 default: 141 SDL_snprintf(errbuf, SDL_arraysize(errbuf), 142 "%s: Unknown DirectSound error: 0x%x", function, code); 143 break; 144 } 145 if (!errbuf[0]) { 146 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, 147 error); 148 } 149 return SDL_SetError("%s", errbuf); 150} 151 152static void 153DSOUND_FreeDeviceHandle(void *handle) 154{ 155 SDL_free(handle); 156} 157 158static BOOL CALLBACK 159FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data) 160{ 161 const int iscapture = (int) ((size_t) data); 162 if (guid != NULL) { /* skip default device */ 163 char *str = WIN_LookupAudioDeviceName(desc, guid); 164 if (str != NULL) { 165 LPGUID cpyguid = (LPGUID) SDL_malloc(sizeof (GUID)); 166 SDL_memcpy(cpyguid, guid, sizeof (GUID)); 167 SDL_AddAudioDevice(iscapture, str, cpyguid); 168 SDL_free(str); /* addfn() makes a copy of this string. */ 169 } 170 } 171 return TRUE; /* keep enumerating. */ 172} 173 174static void 175DSOUND_DetectDevices(void) 176{ 177 pDirectSoundCaptureEnumerateW(FindAllDevs, (void *) ((size_t) 1)); 178 pDirectSoundEnumerateW(FindAllDevs, (void *) ((size_t) 0)); 179} 180 181 182static void 183DSOUND_WaitDevice(_THIS) 184{ 185 DWORD status = 0; 186 DWORD cursor = 0; 187 DWORD junk = 0; 188 HRESULT result = DS_OK; 189 190 /* Semi-busy wait, since we have no way of getting play notification 191 on a primary mixing buffer located in hardware (DirectX 5.0) 192 */ 193 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, 194 &junk, &cursor); 195 if (result != DS_OK) { 196 if (result == DSERR_BUFFERLOST) { 197 IDirectSoundBuffer_Restore(this->hidden->mixbuf); 198 } 199#ifdef DEBUG_SOUND 200 SetDSerror("DirectSound GetCurrentPosition", result); 201#endif 202 return; 203 } 204 205 while ((cursor / this->spec.size) == this->hidden->lastchunk) { 206 /* FIXME: find out how much time is left and sleep that long */ 207 SDL_Delay(1); 208 209 /* Try to restore a lost sound buffer */ 210 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); 211 if ((status & DSBSTATUS_BUFFERLOST)) { 212 IDirectSoundBuffer_Restore(this->hidden->mixbuf); 213 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); 214 if ((status & DSBSTATUS_BUFFERLOST)) { 215 break; 216 } 217 } 218 if (!(status & DSBSTATUS_PLAYING)) { 219 result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0, 220 DSBPLAY_LOOPING); 221 if (result == DS_OK) { 222 continue; 223 } 224#ifdef DEBUG_SOUND 225 SetDSerror("DirectSound Play", result); 226#endif 227 return; 228 } 229 230 /* Find out where we are playing */ 231 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, 232 &junk, &cursor); 233 if (result != DS_OK) { 234 SetDSerror("DirectSound GetCurrentPosition", result); 235 return; 236 } 237 } 238} 239 240static void 241DSOUND_PlayDevice(_THIS) 242{ 243 /* Unlock the buffer, allowing it to play */ 244 if (this->hidden->locked_buf) { 245 IDirectSoundBuffer_Unlock(this->hidden->mixbuf, 246 this->hidden->locked_buf, 247 this->spec.size, NULL, 0); 248 } 249} 250 251static Uint8 * 252DSOUND_GetDeviceBuf(_THIS) 253{ 254 DWORD cursor = 0; 255 DWORD junk = 0; 256 HRESULT result = DS_OK; 257 DWORD rawlen = 0; 258 259 /* Figure out which blocks to fill next */ 260 this->hidden->locked_buf = NULL; 261 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, 262 &junk, &cursor); 263 if (result == DSERR_BUFFERLOST) { 264 IDirectSoundBuffer_Restore(this->hidden->mixbuf); 265 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, 266 &junk, &cursor); 267 } 268 if (result != DS_OK) { 269 SetDSerror("DirectSound GetCurrentPosition", result); 270 return (NULL); 271 } 272 cursor /= this->spec.size; 273#ifdef DEBUG_SOUND 274 /* Detect audio dropouts */ 275 { 276 DWORD spot = cursor; 277 if (spot < this->hidden->lastchunk) { 278 spot += this->hidden->num_buffers; 279 } 280 if (spot > this->hidden->lastchunk + 1) { 281 fprintf(stderr, "Audio dropout, missed %d fragments\n", 282 (spot - (this->hidden->lastchunk + 1))); 283 } 284 } 285#endif 286 this->hidden->lastchunk = cursor; 287 cursor = (cursor + 1) % this->hidden->num_buffers; 288 cursor *= this->spec.size; 289 290 /* Lock the audio buffer */ 291 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, 292 this->spec.size, 293 (LPVOID *) & this->hidden->locked_buf, 294 &rawlen, NULL, &junk, 0); 295 if (result == DSERR_BUFFERLOST) { 296 IDirectSoundBuffer_Restore(this->hidden->mixbuf); 297 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, 298 this->spec.size, 299 (LPVOID *) & this-> 300 hidden->locked_buf, &rawlen, NULL, 301 &junk, 0); 302 } 303 if (result != DS_OK) { 304 SetDSerror("DirectSound Lock", result); 305 return (NULL); 306 } 307 return (this->hidden->locked_buf); 308} 309 310static int 311DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen) 312{ 313 struct SDL_PrivateAudioData *h = this->hidden; 314 DWORD junk, cursor, ptr1len, ptr2len; 315 VOID *ptr1, *ptr2; 316 317 SDL_assert(buflen == this->spec.size); 318 319 while (SDL_TRUE) { 320 if (SDL_AtomicGet(&this->shutdown)) { /* in case the buffer froze... */ 321 SDL_memset(buffer, this->spec.silence, buflen); 322 return buflen; 323 } 324 325 if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) { 326 return -1; 327 } 328 if ((cursor / this->spec.size) == h->lastchunk) { 329 SDL_Delay(1); /* FIXME: find out how much time is left and sleep that long */ 330 } else { 331 break; 332 } 333 } 334 335 if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * this->spec.size, this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) { 336 return -1; 337 } 338 339 SDL_assert(ptr1len == this->spec.size); 340 SDL_assert(ptr2 == NULL); 341 SDL_assert(ptr2len == 0); 342 343 SDL_memcpy(buffer, ptr1, ptr1len); 344 345 if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) { 346 return -1; 347 } 348 349 h->lastchunk = (h->lastchunk + 1) % h->num_buffers; 350 351 return ptr1len; 352} 353 354static void 355DSOUND_FlushCapture(_THIS) 356{ 357 struct SDL_PrivateAudioData *h = this->hidden; 358 DWORD junk, cursor; 359 if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) { 360 h->lastchunk = cursor / this->spec.size; 361 } 362} 363 364static void 365DSOUND_CloseDevice(_THIS) 366{ 367 if (this->hidden->mixbuf != NULL) { 368 IDirectSoundBuffer_Stop(this->hidden->mixbuf); 369 IDirectSoundBuffer_Release(this->hidden->mixbuf); 370 } 371 if (this->hidden->sound != NULL) { 372 IDirectSound_Release(this->hidden->sound); 373 } 374 if (this->hidden->capturebuf != NULL) { 375 IDirectSoundCaptureBuffer_Stop(this->hidden->capturebuf); 376 IDirectSoundCaptureBuffer_Release(this->hidden->capturebuf); 377 } 378 if (this->hidden->capture != NULL) { 379 IDirectSoundCapture_Release(this->hidden->capture); 380 } 381 SDL_free(this->hidden); 382} 383 384/* This function tries to create a secondary audio buffer, and returns the 385 number of audio chunks available in the created buffer. This is for 386 playback devices, not capture. 387*/ 388static int 389CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt) 390{ 391 LPDIRECTSOUND sndObj = this->hidden->sound; 392 LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf; 393 HRESULT result = DS_OK; 394 DSBUFFERDESC format; 395 LPVOID pvAudioPtr1, pvAudioPtr2; 396 DWORD dwAudioBytes1, dwAudioBytes2; 397 398 /* Try to create the secondary buffer */ 399 SDL_zero(format); 400 format.dwSize = sizeof(format); 401 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; 402 format.dwFlags |= DSBCAPS_GLOBALFOCUS; 403 format.dwBufferBytes = bufsize; 404 format.lpwfxFormat = wfmt; 405 result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); 406 if (result != DS_OK) { 407 return SetDSerror("DirectSound CreateSoundBuffer", result); 408 } 409 IDirectSoundBuffer_SetFormat(*sndbuf, wfmt); 410 411 /* Silence the initial audio buffer */ 412 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, 413 (LPVOID *) & pvAudioPtr1, &dwAudioBytes1, 414 (LPVOID *) & pvAudioPtr2, &dwAudioBytes2, 415 DSBLOCK_ENTIREBUFFER); 416 if (result == DS_OK) { 417 SDL_memset(pvAudioPtr1, this->spec.silence, dwAudioBytes1); 418 IDirectSoundBuffer_Unlock(*sndbuf, 419 (LPVOID) pvAudioPtr1, dwAudioBytes1, 420 (LPVOID) pvAudioPtr2, dwAudioBytes2); 421 } 422 423 /* We're ready to go */ 424 return 0; 425} 426 427/* This function tries to create a capture buffer, and returns the 428 number of audio chunks available in the created buffer. This is for 429 capture devices, not playback. 430*/ 431static int 432CreateCaptureBuffer(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt) 433{ 434 LPDIRECTSOUNDCAPTURE capture = this->hidden->capture; 435 LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf; 436 DSCBUFFERDESC format; 437 HRESULT result; 438 439 SDL_zero(format); 440 format.dwSize = sizeof (format); 441 format.dwFlags = DSCBCAPS_WAVEMAPPED; 442 format.dwBufferBytes = bufsize; 443 format.lpwfxFormat = wfmt; 444 445 result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL); 446 if (result != DS_OK) { 447 return SetDSerror("DirectSound CreateCaptureBuffer", result); 448 } 449 450 result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING); 451 if (result != DS_OK) { 452 IDirectSoundCaptureBuffer_Release(*capturebuf); 453 return SetDSerror("DirectSound Start", result); 454 } 455 456#if 0 457 /* presumably this starts at zero, but just in case... */ 458 result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor); 459 if (result != DS_OK) { 460 IDirectSoundCaptureBuffer_Stop(*capturebuf); 461 IDirectSoundCaptureBuffer_Release(*capturebuf); 462 return SetDSerror("DirectSound GetCurrentPosition", result); 463 } 464 465 this->hidden->lastchunk = cursor / this->spec.size; 466#endif 467 468 return 0; 469} 470 471static int 472DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 473{ 474 const DWORD numchunks = 8; 475 HRESULT result; 476 SDL_bool valid_format = SDL_FALSE; 477 SDL_bool tried_format = SDL_FALSE; 478 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); 479 LPGUID guid = (LPGUID) handle; 480 DWORD bufsize; 481 482 /* Initialize all variables that we clean on shutdown */ 483 this->hidden = (struct SDL_PrivateAudioData *) 484 SDL_malloc((sizeof *this->hidden)); 485 if (this->hidden == NULL) { 486 return SDL_OutOfMemory(); 487 } 488 SDL_zerop(this->hidden); 489 490 /* Open the audio device */ 491 if (iscapture) { 492 result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL); 493 if (result != DS_OK) { 494 return SetDSerror("DirectSoundCaptureCreate8", result); 495 } 496 } else { 497 result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL); 498 if (result != DS_OK) { 499 return SetDSerror("DirectSoundCreate8", result); 500 } 501 result = IDirectSound_SetCooperativeLevel(this->hidden->sound, 502 GetDesktopWindow(), 503 DSSCL_NORMAL); 504 if (result != DS_OK) { 505 return SetDSerror("DirectSound SetCooperativeLevel", result); 506 } 507 } 508 509 while ((!valid_format) && (test_format)) { 510 switch (test_format) { 511 case AUDIO_U8: 512 case AUDIO_S16: 513 case AUDIO_S32: 514 case AUDIO_F32: 515 tried_format = SDL_TRUE; 516 517 this->spec.format = test_format; 518 519 /* Update the fragment size as size in bytes */ 520 SDL_CalculateAudioSpec(&this->spec); 521 522 bufsize = numchunks * this->spec.size; 523 if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) { 524 SDL_SetError("Sound buffer size must be between %d and %d", 525 (int) ((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks), 526 (int) (DSBSIZE_MAX / numchunks)); 527 } else { 528 int rc; 529 WAVEFORMATEX wfmt; 530 SDL_zero(wfmt); 531 if (SDL_AUDIO_ISFLOAT(this->spec.format)) { 532 wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; 533 } else { 534 wfmt.wFormatTag = WAVE_FORMAT_PCM; 535 } 536 537 wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); 538 wfmt.nChannels = this->spec.channels; 539 wfmt.nSamplesPerSec = this->spec.freq; 540 wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8); 541 wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign; 542 543 rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt); 544 if (rc == 0) { 545 this->hidden->num_buffers = numchunks; 546 valid_format = SDL_TRUE; 547 } 548 } 549 break; 550 } 551 test_format = SDL_NextAudioFormat(); 552 } 553 554 if (!valid_format) { 555 if (tried_format) { 556 return -1; /* CreateSecondary() should have called SDL_SetError(). */ 557 } 558 return SDL_SetError("DirectSound: Unsupported audio format"); 559 } 560 561 /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */ 562 563 return 0; /* good to go. */ 564} 565 566 567static void 568DSOUND_Deinitialize(void) 569{ 570 DSOUND_Unload(); 571} 572 573 574static int 575DSOUND_Init(SDL_AudioDriverImpl * impl) 576{ 577 if (!DSOUND_Load()) { 578 return 0; 579 } 580 581 /* Set the function pointers */ 582 impl->DetectDevices = DSOUND_DetectDevices; 583 impl->OpenDevice = DSOUND_OpenDevice; 584 impl->PlayDevice = DSOUND_PlayDevice; 585 impl->WaitDevice = DSOUND_WaitDevice; 586 impl->GetDeviceBuf = DSOUND_GetDeviceBuf; 587 impl->CaptureFromDevice = DSOUND_CaptureFromDevice; 588 impl->FlushCapture = DSOUND_FlushCapture; 589 impl->CloseDevice = DSOUND_CloseDevice; 590 impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle; 591 impl->Deinitialize = DSOUND_Deinitialize; 592 593 impl->HasCaptureSupport = SDL_TRUE; 594 595 return 1; /* this audio target is available. */ 596} 597 598AudioBootStrap DSOUND_bootstrap = { 599 "directsound", "DirectSound", DSOUND_Init, 0 600}; 601 602#endif /* SDL_AUDIO_DRIVER_DSOUND */ 603 604/* vi: set ts=4 sw=4 expandtab: */ 605
[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.