comparison src/audio/SDL_audio.c @ 3796:b19680c84cdf SDL-ryan-multiple-audio-device

Bunch of 1.3 audio cleanups to remove FIXMEs, get driver specific crap out of the core and into the drivers where it belongs, and push generic responsibilities out of the drivers and into the core where they belong.
author Ryan C. Gordon <icculus@icculus.org>
date Wed, 04 Oct 2006 19:54:23 +0000
parents 589bc3d060cd
children c8b3d3d13ed1
comparison
equal deleted inserted replaced
3795:589bc3d060cd 3796:b19680c84cdf
32 /* We'll need the DosSetPriority() API! */ 32 /* We'll need the DosSetPriority() API! */
33 #define INCL_DOSPROCESS 33 #define INCL_DOSPROCESS
34 #include <os2.h> 34 #include <os2.h>
35 #endif 35 #endif
36 36
37 #define _THIS SDL_AudioDevice *this
38
37 static SDL_AudioDriver current_audio; 39 static SDL_AudioDriver current_audio;
38 40
39 /* !!! FIXME: don't use a static array, but it's Good Enough For Now... */ 41 /* !!! FIXME: don't use a static array, but it's Good Enough For Now... */
40 static SDL_AudioDevice *open_devices[16]; 42 static SDL_AudioDevice *open_devices[16];
43
44 /* !!! FIXME: These are wordy and unlocalized... */
45 #define DEFAULT_OUTPUT_DEVNAME "System audio output device"
46 #define DEFAULT_INPUT_DEVNAME "System audio capture device"
47
48
49 /*
50 * Not all of these will be compiled and linked in, but it's convenient
51 * to have a complete list here and saves yet-another block of #ifdefs...
52 * Please see bootstrap[], below, for the actual #ifdef mess.
53 */
54 extern AudioBootStrap BSD_AUDIO_bootstrap;
55 extern AudioBootStrap DSP_bootstrap;
56 extern AudioBootStrap DMA_bootstrap;
57 extern AudioBootStrap ALSA_bootstrap;
58 extern AudioBootStrap QNXNTOAUDIO_bootstrap;
59 extern AudioBootStrap SUNAUDIO_bootstrap;
60 extern AudioBootStrap DMEDIA_bootstrap;
61 extern AudioBootStrap ARTS_bootstrap;
62 extern AudioBootStrap ESD_bootstrap;
63 extern AudioBootStrap NAS_bootstrap;
64 extern AudioBootStrap DSOUND_bootstrap;
65 extern AudioBootStrap WAVEOUT_bootstrap;
66 extern AudioBootStrap Paud_bootstrap;
67 extern AudioBootStrap BAUDIO_bootstrap;
68 extern AudioBootStrap COREAUDIO_bootstrap;
69 extern AudioBootStrap SNDMGR_bootstrap;
70 extern AudioBootStrap AHI_bootstrap;
71 extern AudioBootStrap MINTAUDIO_GSXB_bootstrap;
72 extern AudioBootStrap MINTAUDIO_MCSN_bootstrap;
73 extern AudioBootStrap MINTAUDIO_STFA_bootstrap;
74 extern AudioBootStrap MINTAUDIO_XBIOS_bootstrap;
75 extern AudioBootStrap MINTAUDIO_DMA8_bootstrap;
76 extern AudioBootStrap DISKAUD_bootstrap;
77 extern AudioBootStrap DUMMYAUD_bootstrap;
78 extern AudioBootStrap DCAUD_bootstrap;
79 extern AudioBootStrap MMEAUDIO_bootstrap;
80 extern AudioBootStrap DART_bootstrap;
41 81
42 82
43 /* Available audio drivers */ 83 /* Available audio drivers */
44 static AudioBootStrap *bootstrap[] = { 84 static AudioBootStrap *bootstrap[] = {
45 #if SDL_AUDIO_DRIVER_BSD 85 #if SDL_AUDIO_DRIVER_BSD
131 return open_devices[id]; 171 return open_devices[id];
132 } 172 }
133 173
134 174
135 /* stubs for audio drivers that don't need a specific entry point... */ 175 /* stubs for audio drivers that don't need a specific entry point... */
136 /* !!! FIXME: fill in more of these. */ 176 static int SDL_AudioDetectDevices_Default(int iscapture) { return -1; }
137 177 static void SDL_AudioThreadInit_Default(_THIS) { /* no-op. */ }
138 static void SDL_DeinitializeAudio_Default(void) 178 static void SDL_AudioWaitDevice_Default(_THIS) { /* no-op. */ }
139 { 179 static void SDL_AudioPlayDevice_Default(_THIS) { /* no-op. */ }
140 /* no-op. */ 180 static Uint8 *SDL_AudioGetDeviceBuf_Default(_THIS) { return NULL; }
141 } 181 static void SDL_AudioWaitDone_Default(_THIS) { /* no-op. */ }
142 182 static void SDL_AudioCloseDevice_Default(_THIS) { /* no-op. */ }
183 static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
184
185 static int
186 SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
187 {
188 return 0;
189 }
190
191 static const char *SDL_AudioGetDeviceName_Default(int index, int iscapture)
192 {
193 SDL_SetError("No such device");
194 return NULL;
195 }
196
197 static void
198 SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
199 {
200 if (device->thread && (SDL_ThreadID() == device->threadid)) {
201 return;
202 }
203 SDL_mutexP(device->mixer_lock);
204 }
205
206 static void
207 SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
208 {
209 if (device->thread && (SDL_ThreadID() == device->threadid)) {
210 return;
211 }
212 SDL_mutexV(device->mixer_lock);
213 }
214
215
216 static void finalize_audio_entry_points(void)
217 {
218 /*
219 * Fill in stub functions for unused driver entry points. This lets us
220 * blindly call them without having to check for validity first.
221 */
222
223 #define FILL_STUB(x) \
224 if (current_audio.impl.x == NULL) { \
225 current_audio.impl.x = SDL_Audio##x##_Default; \
226 }
227 FILL_STUB(DetectDevices);
228 FILL_STUB(GetDeviceName);
229 FILL_STUB(OpenDevice);
230 FILL_STUB(ThreadInit);
231 FILL_STUB(WaitDevice);
232 FILL_STUB(PlayDevice);
233 FILL_STUB(GetDeviceBuf);
234 FILL_STUB(WaitDone);
235 FILL_STUB(CloseDevice);
236 FILL_STUB(LockDevice);
237 FILL_STUB(UnlockDevice);
238 FILL_STUB(Deinitialize);
239 #undef FILL_STUB
240 }
143 241
144 242
145 /* The general mixing thread function */ 243 /* The general mixing thread function */
146 int SDLCALL 244 int SDLCALL
147 SDL_RunAudio(void *devicep) 245 SDL_RunAudio(void *devicep)
170 } 268 }
171 D(bug("OpenAudio...OK\n")); 269 D(bug("OpenAudio...OK\n"));
172 #endif 270 #endif
173 271
174 /* Perform any thread setup */ 272 /* Perform any thread setup */
175 if (current_audio.impl.ThreadInit != NULL) {
176 current_audio.impl.ThreadInit(device);
177 }
178 device->threadid = SDL_ThreadID(); 273 device->threadid = SDL_ThreadID();
274 current_audio.impl.ThreadInit(device);
179 275
180 /* Set up the mixing function */ 276 /* Set up the mixing function */
181 fill = device->spec.callback; 277 fill = device->spec.callback;
182 udata = device->spec.userdata; 278 udata = device->spec.userdata;
183 279
202 } 298 }
203 299
204 #if SDL_AUDIO_DRIVER_AHI 300 #if SDL_AUDIO_DRIVER_AHI
205 SDL_mutexV(device->mixer_lock); 301 SDL_mutexV(device->mixer_lock);
206 D(bug("Entering audio loop...\n")); 302 D(bug("Entering audio loop...\n"));
207 #endif
208
209 /* !!! FIXME: push this out of core. */
210 #ifdef __OS2__
211 /* Increase the priority of this thread to make sure that
212 the audio will be continuous all the time! */
213 #ifdef USE_DOSSETPRIORITY
214 if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO")) {
215 #ifdef DEBUG_BUILD
216 printf
217 ("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n",
218 SDL_ThreadID());
219 #endif
220 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
221 } else {
222 #ifdef DEBUG_BUILD
223 printf
224 ("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n",
225 SDL_ThreadID());
226 #endif
227 DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
228 }
229 #endif
230 #endif 303 #endif
231 304
232 /* Loop, filling the audio buffers */ 305 /* Loop, filling the audio buffers */
233 while (device->enabled) { 306 while (device->enabled) {
234 307
279 current_audio.impl.WaitDevice(device); 352 current_audio.impl.WaitDevice(device);
280 } 353 }
281 } 354 }
282 355
283 /* Wait for the audio to drain.. */ 356 /* Wait for the audio to drain.. */
284 if (current_audio.impl.WaitDone) { 357 current_audio.impl.WaitDone(device);
285 current_audio.impl.WaitDone(device); 358
286 } 359 /* !!! FIXME: get this out of core. */
287 #if SDL_AUDIO_DRIVER_AHI 360 #if SDL_AUDIO_DRIVER_AHI
288 D(bug("WaitDevice...Done\n")); 361 D(bug("WaitDevice...Done\n"));
289 362 current_audio.impl.CloseDevice(device);
290 audio->CloseDevice(audio); 363 device->opened = 0;
291
292 D(bug("CloseDevice..Done, subtask exiting...\n")); 364 D(bug("CloseDevice..Done, subtask exiting...\n"));
293 audio_configured = 0; 365 audio_configured = 0;
294 #endif 366 #endif
295 #ifdef __OS2__ 367
296 #ifdef DEBUG_BUILD
297 printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID());
298 #endif
299 #endif
300 return (0); 368 return (0);
301 } 369 }
302 370
303 static void
304 SDL_LockDevice_Default(SDL_AudioDevice * audio)
305 {
306 if (audio->thread && (SDL_ThreadID() == audio->threadid)) {
307 return;
308 }
309 SDL_mutexP(audio->mixer_lock);
310 }
311
312 static void
313 SDL_UnlockDevice_Default(SDL_AudioDevice * audio)
314 {
315 if (audio->thread && (SDL_ThreadID() == audio->threadid)) {
316 return;
317 }
318 SDL_mutexV(audio->mixer_lock);
319 }
320 371
321 static SDL_AudioFormat 372 static SDL_AudioFormat
322 SDL_ParseAudioFormat(const char *string) 373 SDL_ParseAudioFormat(const char *string)
323 { 374 {
324 #define CHECK_FMT_STRING(x) if (strcmp(string, #x) == 0) return AUDIO_##x 375 #define CHECK_FMT_STRING(x) if (strcmp(string, #x) == 0) return AUDIO_##x
446 SDL_memset(&current_audio, 0, sizeof (current_audio)); 497 SDL_memset(&current_audio, 0, sizeof (current_audio));
447 return (-1); /* No driver was available, so fail. */ 498 return (-1); /* No driver was available, so fail. */
448 } 499 }
449 } 500 }
450 501
451 if (!current_audio.impl.LockDevice && !current_audio.impl.UnlockDevice) { 502 finalize_audio_entry_points();
452 current_audio.impl.LockDevice = SDL_LockDevice_Default;
453 current_audio.impl.UnlockDevice = SDL_UnlockDevice_Default;
454 }
455
456 if (!current_audio.impl.Deinitialize) {
457 current_audio.impl.Deinitialize = SDL_DeinitializeAudio_Default;
458 }
459 503
460 return (0); 504 return (0);
461 } 505 }
462 506
463 /* 507 /*
471 515
472 516
473 int 517 int
474 SDL_GetNumAudioDevices(int iscapture) 518 SDL_GetNumAudioDevices(int iscapture)
475 { 519 {
476 if (!SDL_WasInit(SDL_INIT_AUDIO) || !current_audio.impl.DetectDevices) { 520 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
477 return -1; 521 return -1;
478 } 522 }
523 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
524 return 0;
525 }
526
527 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
528 return 1;
529 }
530
531 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
532 return 1;
533 }
534
479 return current_audio.impl.DetectDevices(iscapture); 535 return current_audio.impl.DetectDevices(iscapture);
480 } 536 }
481 537
482 538
483 const char * 539 const char *
486 if (!SDL_WasInit(SDL_INIT_AUDIO)) { 542 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
487 SDL_SetError("Audio subsystem is not initialized"); 543 SDL_SetError("Audio subsystem is not initialized");
488 return NULL; 544 return NULL;
489 } 545 }
490 546
491 if ((index < 0) && (!current_audio.impl.GetDeviceName)) { 547 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
548 SDL_SetError("No capture support");
549 return NULL;
550 }
551
552 if (index < 0) {
492 SDL_SetError("No such device"); 553 SDL_SetError("No such device");
493 return NULL; 554 return NULL;
555 }
556
557 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
558 return DEFAULT_INPUT_DEVNAME;
559 }
560
561 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
562 return DEFAULT_OUTPUT_DEVNAME;
494 } 563 }
495 564
496 return current_audio.impl.GetDeviceName(index, iscapture); 565 return current_audio.impl.GetDeviceName(index, iscapture);
497 } 566 }
498 567
511 SDL_FreeAudioMem(device->fake_stream); 580 SDL_FreeAudioMem(device->fake_stream);
512 } 581 }
513 if (device->convert.needed) { 582 if (device->convert.needed) {
514 SDL_FreeAudioMem(device->convert.buf); 583 SDL_FreeAudioMem(device->convert.buf);
515 } 584 }
516 #if !SDL_AUDIO_DRIVER_AHI /* !!! FIXME: get rid of this #if. */
517 if (device->opened) { 585 if (device->opened) {
518 current_audio.impl.CloseDevice(device); 586 current_audio.impl.CloseDevice(device);
519 device->opened = 0; 587 device->opened = 0;
520 } 588 }
521 #endif
522 SDL_FreeAudioMem(device); 589 SDL_FreeAudioMem(device);
523 } 590 }
524 591
525 592
526 /* 593 /*
594 static SDL_AudioDeviceID 661 static SDL_AudioDeviceID
595 open_audio_device(const char *devname, int iscapture, 662 open_audio_device(const char *devname, int iscapture,
596 const SDL_AudioSpec *_desired, SDL_AudioSpec *obtained, 663 const SDL_AudioSpec *_desired, SDL_AudioSpec *obtained,
597 int min_id) 664 int min_id)
598 { 665 {
599 int i = 0;
600 SDL_AudioDeviceID id = 0; 666 SDL_AudioDeviceID id = 0;
601 SDL_AudioSpec desired; 667 SDL_AudioSpec desired;
602 SDL_AudioDevice *device; 668 SDL_AudioDevice *device;
603 669 int i = 0;
604 if (iscapture) {
605 SDL_SetError("Audio capture support not implemented yet!");
606 return 0; /* !!! FIXME */
607 }
608 670
609 if (!SDL_WasInit(SDL_INIT_AUDIO)) { 671 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
610 SDL_SetError("Audio subsystem is not initialized"); 672 SDL_SetError("Audio subsystem is not initialized");
611 return 0; 673 return 0;
612 } 674 }
613 675
676 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
677 SDL_SetError("No capture support");
678 return 0;
679 }
680
614 if (!prepare_audiospec(_desired, &desired)) { 681 if (!prepare_audiospec(_desired, &desired)) {
615 return 0; 682 return 0;
616 } 683 }
617 684
618 /* If app doesn't care about a specific device, let the user override. */ 685 /* If app doesn't care about a specific device, let the user override. */
619 if (devname == NULL) { 686 if (devname == NULL) {
620 devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME"); 687 devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
688 }
689
690 /*
691 * Catch device names at the high level for the simple case...
692 * This lets us have a basic "device enumeration" for systems that
693 * don't have multiple devices, but makes sure the device name is
694 * always NULL when it hits the low level.
695 *
696 * Also make sure that the simple case prevents multiple simultaneous
697 * opens of the default system device.
698 */
699
700 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
701 if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
702 SDL_SetError("No such device");
703 return 0;
704 }
705 devname = NULL;
706
707 for (i = 0; i < SDL_arraysize(open_devices); i++) {
708 if ((open_devices[i]) && (open_devices[i]->iscapture)) {
709 SDL_SetError("Audio device already open");
710 return;
711 }
712 }
713 }
714
715 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
716 if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
717 SDL_SetError("No such device");
718 return 0;
719 }
720 devname = NULL;
721
722 for (i = 0; i < SDL_arraysize(open_devices); i++) {
723 if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
724 SDL_SetError("Audio device already open");
725 return;
726 }
727 }
621 } 728 }
622 729
623 device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof (SDL_AudioDevice)); 730 device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof (SDL_AudioDevice));
624 if (device == NULL) { 731 if (device == NULL) {
625 SDL_OutOfMemory(); 732 SDL_OutOfMemory();
626 return 0; 733 return 0;
627 } 734 }
628 SDL_memset(device, '\0', sizeof (SDL_AudioDevice)); 735 SDL_memset(device, '\0', sizeof (SDL_AudioDevice));
629 SDL_memcpy(&device->spec, &desired, sizeof (SDL_AudioSpec)); 736 SDL_memcpy(&device->spec, &desired, sizeof (SDL_AudioSpec));
630 device->driver = &current_audio; /* !!! FIXME: unused... */
631 device->enabled = 1; 737 device->enabled = 1;
632 device->paused = 1; 738 device->paused = 1;
633 739 device->iscapture = iscapture;
634 /* !!! FIXME: Get this out of the core. */ 740
635 #if defined(__MINT__) && SDL_THREADS_DISABLED
636 /* Uses interrupt driven audio, without thread */
637 #else
638 /* Create a semaphore for locking the sound buffers */ 741 /* Create a semaphore for locking the sound buffers */
639 device->mixer_lock = SDL_CreateMutex(); 742 if (!current_audio.impl.SkipMixerLock) {
640 if (device->mixer_lock == NULL) { 743 device->mixer_lock = SDL_CreateMutex();
641 SDL_SetError("Couldn't create mixer lock"); 744 if (device->mixer_lock == NULL) {
642 return 0; 745 close_audio_device(device);
643 } 746 SDL_SetError("Couldn't create mixer lock");
644 #endif /* __MINT__ */ 747 return 0;
748 }
749 }
645 750
646 /* !!! FIXME: Get this #if out of the core. */ 751 /* !!! FIXME: Get this #if out of the core. */
647 /* AmigaOS opens audio inside the main loop */ 752 /* AmigaOS opens audio inside the main loop */
648 #if !SDL_AUDIO_DRIVER_AHI 753 #if !SDL_AUDIO_DRIVER_AHI
649 if (!current_audio.impl.OpenDevice(device, devname, iscapture)) { 754 if (!current_audio.impl.OpenDevice(device, devname, iscapture)) {
650 close_audio_device(device); 755 close_audio_device(device);
651 return 0; 756 return 0;
652 } 757 }
653 device->opened = 2; /* !!! FIXME */ 758 device->opened = 1;
654 #else 759 #else
655 # error needs to be fixed for new internal API. Email Ryan for details. 760 # error needs to be fixed for new internal API. Email Ryan for details.
656 761
657 D(bug("Locking semaphore...")); 762 D(bug("Locking semaphore..."));
658 SDL_mutexP(audio->mixer_lock); 763 SDL_mutexP(audio->mixer_lock);
727 SDL_SetError("Too many open audio devices"); 832 SDL_SetError("Too many open audio devices");
728 close_audio_device(device); 833 close_audio_device(device);
729 return 0; 834 return 0;
730 } 835 }
731 836
837 /* !!! FIXME: get this out of core. */
732 #if !SDL_AUDIO_DRIVER_AHI 838 #if !SDL_AUDIO_DRIVER_AHI
733 /* Start the audio thread if necessary */ 839 /* Start the audio thread if necessary */
734 switch (device->opened) { /* !!! FIXME: what is this?! */ 840 if (!current_audio.impl.ProvidesOwnCallbackThread) {
735 case 1:
736 /* Start the audio thread */ 841 /* Start the audio thread */
737 /* !!! FIXME: this is nasty. */ 842 /* !!! FIXME: this is nasty. */
738 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) 843 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
739 #undef SDL_CreateThread 844 #undef SDL_CreateThread
740 device->thread = SDL_CreateThread(SDL_RunAudio, device, NULL, NULL); 845 device->thread = SDL_CreateThread(SDL_RunAudio, device, NULL, NULL);
744 if (device->thread == NULL) { 849 if (device->thread == NULL) {
745 SDL_CloseAudioDevice(id+1); 850 SDL_CloseAudioDevice(id+1);
746 SDL_SetError("Couldn't create audio thread"); 851 SDL_SetError("Couldn't create audio thread");
747 return 0; 852 return 0;
748 } 853 }
749 break; 854 }
750 855
751 default:
752 /* The audio is now playing */
753 break;
754 }
755 #else 856 #else
756 SDL_mutexV(audio->mixer_lock); 857 SDL_mutexV(audio->mixer_lock);
757 D(bug("SDL_OpenAudio USCITA...\n")); 858 D(bug("SDL_OpenAudio USCITA...\n"));
758 859
759 #endif 860 #endif
836 937
837 938
838 void 939 void
839 SDL_LockAudioDevice(SDL_AudioDeviceID devid) 940 SDL_LockAudioDevice(SDL_AudioDeviceID devid)
840 { 941 {
841 if (current_audio.impl.LockDevice != NULL) { 942 /* Obtain a lock on the mixing buffers */
842 SDL_AudioDevice *device = get_audio_device(devid); 943 SDL_AudioDevice *device = get_audio_device(devid);
843 /* Obtain a lock on the mixing buffers */ 944 if (device) {
844 if (device) { 945 current_audio.impl.LockDevice(device);
845 current_audio.impl.LockDevice(device);
846 }
847 } 946 }
848 } 947 }
849 948
850 void 949 void
851 SDL_LockAudio(void) 950 SDL_LockAudio(void)
854 } 953 }
855 954
856 void 955 void
857 SDL_UnlockAudioDevice(SDL_AudioDeviceID devid) 956 SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
858 { 957 {
859 if (current_audio.impl.UnlockDevice != NULL) { 958 /* Obtain a lock on the mixing buffers */
860 SDL_AudioDevice *device = get_audio_device(devid); 959 SDL_AudioDevice *device = get_audio_device(devid);
861 /* Obtain a lock on the mixing buffers */ 960 if (device) {
862 if (device) { 961 current_audio.impl.UnlockDevice(device);
863 current_audio.impl.UnlockDevice(device);
864 }
865 } 962 }
866 } 963 }
867 964
868 void 965 void
869 SDL_UnlockAudio(void) 966 SDL_UnlockAudio(void)