e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 Sam Lantinga |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with this library; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | |
19 | Sam Lantinga |
20 | slouken@libsdl.org |
21 | */ |
22 | #include "SDL_config.h" |
23 | |
24 | /* Allow access to a raw mixing buffer */ |
25 | |
26 | #include "SDL.h" |
27 | #include "SDL_audio_c.h" |
28 | #include "SDL_audiomem.h" |
29 | #include "SDL_sysaudio.h" |
30 | |
31 | #ifdef __OS2__ |
32 | /* We'll need the DosSetPriority() API! */ |
33 | #define INCL_DOSPROCESS |
34 | #include <os2.h> |
35 | #endif |
36 | |
37 | /* Available audio drivers */ |
38 | static AudioBootStrap *bootstrap[] = { |
39 | #if SDL_AUDIO_DRIVER_BSD |
40 | &BSD_AUDIO_bootstrap, |
41 | #endif |
42 | #if SDL_AUDIO_DRIVER_PULSE |
43 | &PULSE_bootstrap, |
44 | #endif |
45 | #if SDL_AUDIO_DRIVER_ALSA |
46 | &ALSA_bootstrap, |
47 | #endif |
48 | #if SDL_AUDIO_DRIVER_OSS |
49 | &DSP_bootstrap, |
50 | &DMA_bootstrap, |
51 | #endif |
52 | #if SDL_AUDIO_DRIVER_QNXNTO |
53 | &QNXNTOAUDIO_bootstrap, |
54 | #endif |
55 | #if SDL_AUDIO_DRIVER_SUNAUDIO |
56 | &SUNAUDIO_bootstrap, |
57 | #endif |
58 | #if SDL_AUDIO_DRIVER_DMEDIA |
59 | &DMEDIA_bootstrap, |
60 | #endif |
61 | #if SDL_AUDIO_DRIVER_ARTS |
62 | &ARTS_bootstrap, |
63 | #endif |
64 | #if SDL_AUDIO_DRIVER_ESD |
65 | &ESD_bootstrap, |
66 | #endif |
67 | #if SDL_AUDIO_DRIVER_NAS |
68 | &NAS_bootstrap, |
69 | #endif |
70 | #if SDL_AUDIO_DRIVER_DSOUND |
71 | &DSOUND_bootstrap, |
72 | #endif |
73 | #if SDL_AUDIO_DRIVER_WAVEOUT |
74 | &WAVEOUT_bootstrap, |
75 | #endif |
76 | #if SDL_AUDIO_DRIVER_PAUD |
77 | &Paud_bootstrap, |
78 | #endif |
79 | #if SDL_AUDIO_DRIVER_BAUDIO |
80 | &BAUDIO_bootstrap, |
81 | #endif |
82 | #if SDL_AUDIO_DRIVER_COREAUDIO |
83 | &COREAUDIO_bootstrap, |
84 | #endif |
85 | #if SDL_AUDIO_DRIVER_SNDMGR |
86 | &SNDMGR_bootstrap, |
87 | #endif |
88 | #if SDL_AUDIO_DRIVER_MINT |
89 | &MINTAUDIO_GSXB_bootstrap, |
90 | &MINTAUDIO_MCSN_bootstrap, |
91 | &MINTAUDIO_STFA_bootstrap, |
92 | &MINTAUDIO_XBIOS_bootstrap, |
93 | &MINTAUDIO_DMA8_bootstrap, |
94 | #endif |
95 | #if SDL_AUDIO_DRIVER_DISK |
96 | &DISKAUD_bootstrap, |
97 | #endif |
98 | #if SDL_AUDIO_DRIVER_DUMMY |
99 | &DUMMYAUD_bootstrap, |
100 | #endif |
101 | #if SDL_AUDIO_DRIVER_DC |
102 | &DCAUD_bootstrap, |
103 | #endif |
104 | #if SDL_AUDIO_DRIVER_NDS |
105 | &NDSAUD_bootstrap, |
106 | #endif |
107 | #if SDL_AUDIO_DRIVER_MMEAUDIO |
108 | &MMEAUDIO_bootstrap, |
109 | #endif |
110 | #if SDL_AUDIO_DRIVER_DART |
111 | &DART_bootstrap, |
112 | #endif |
113 | #if SDL_AUDIO_DRIVER_EPOCAUDIO |
114 | &EPOCAudio_bootstrap, |
115 | #endif |
116 | NULL |
117 | }; |
118 | SDL_AudioDevice *current_audio = NULL; |
119 | |
120 | /* Various local functions */ |
121 | int SDL_AudioInit(const char *driver_name); |
122 | void SDL_AudioQuit(void); |
123 | |
124 | /* The general mixing thread function */ |
125 | int SDLCALL SDL_RunAudio(void *audiop) |
126 | { |
127 | SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop; |
128 | Uint8 *stream; |
129 | int stream_len; |
130 | void *udata; |
131 | void (SDLCALL *fill)(void *userdata,Uint8 *stream, int len); |
132 | int silence; |
133 | |
134 | /* Perform any thread setup */ |
135 | if ( audio->ThreadInit ) { |
136 | audio->ThreadInit(audio); |
137 | } |
138 | audio->threadid = SDL_ThreadID(); |
139 | |
140 | /* Set up the mixing function */ |
141 | fill = audio->spec.callback; |
142 | udata = audio->spec.userdata; |
143 | |
144 | if ( audio->convert.needed ) { |
145 | if ( audio->convert.src_format == AUDIO_U8 ) { |
146 | silence = 0x80; |
147 | } else { |
148 | silence = 0; |
149 | } |
150 | stream_len = audio->convert.len; |
151 | } else { |
152 | silence = audio->spec.silence; |
153 | stream_len = audio->spec.size; |
154 | } |
155 | |
156 | #ifdef __OS2__ |
157 | /* Increase the priority of this thread to make sure that |
158 | the audio will be continuous all the time! */ |
159 | #ifdef USE_DOSSETPRIORITY |
160 | if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO")) |
161 | { |
162 | #ifdef DEBUG_BUILD |
163 | printf("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n", SDL_ThreadID()); |
164 | #endif |
165 | DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0); |
166 | } |
167 | else |
168 | { |
169 | #ifdef DEBUG_BUILD |
170 | printf("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n", SDL_ThreadID()); |
171 | #endif |
172 | DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0); |
173 | } |
174 | #endif |
175 | #endif |
176 | |
177 | /* Loop, filling the audio buffers */ |
178 | while ( audio->enabled ) { |
179 | |
180 | /* Fill the current buffer with sound */ |
181 | if ( audio->convert.needed ) { |
182 | if ( audio->convert.buf ) { |
183 | stream = audio->convert.buf; |
184 | } else { |
185 | continue; |
186 | } |
187 | } else { |
188 | stream = audio->GetAudioBuf(audio); |
189 | if ( stream == NULL ) { |
190 | stream = audio->fake_stream; |
191 | } |
192 | } |
193 | |
194 | SDL_memset(stream, silence, stream_len); |
195 | |
196 | if ( ! audio->paused ) { |
197 | SDL_mutexP(audio->mixer_lock); |
198 | (*fill)(udata, stream, stream_len); |
199 | SDL_mutexV(audio->mixer_lock); |
200 | } |
201 | |
202 | /* Convert the audio if necessary */ |
203 | if ( audio->convert.needed ) { |
204 | SDL_ConvertAudio(&audio->convert); |
205 | stream = audio->GetAudioBuf(audio); |
206 | if ( stream == NULL ) { |
207 | stream = audio->fake_stream; |
208 | } |
209 | SDL_memcpy(stream, audio->convert.buf, |
210 | audio->convert.len_cvt); |
211 | } |
212 | |
213 | /* Ready current buffer for play and change current buffer */ |
214 | if ( stream != audio->fake_stream ) { |
215 | audio->PlayAudio(audio); |
216 | } |
217 | |
218 | /* Wait for an audio buffer to become available */ |
219 | if ( stream == audio->fake_stream ) { |
220 | SDL_Delay((audio->spec.samples*1000)/audio->spec.freq); |
221 | } else { |
222 | audio->WaitAudio(audio); |
223 | } |
224 | } |
225 | |
226 | /* Wait for the audio to drain.. */ |
227 | if ( audio->WaitDone ) { |
228 | audio->WaitDone(audio); |
229 | } |
230 | |
231 | #ifdef __OS2__ |
232 | #ifdef DEBUG_BUILD |
233 | printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID()); |
234 | #endif |
235 | #endif |
236 | return(0); |
237 | } |
238 | |
239 | static void SDL_LockAudio_Default(SDL_AudioDevice *audio) |
240 | { |
241 | if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) { |
242 | return; |
243 | } |
244 | SDL_mutexP(audio->mixer_lock); |
245 | } |
246 | |
247 | static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio) |
248 | { |
249 | if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) { |
250 | return; |
251 | } |
252 | SDL_mutexV(audio->mixer_lock); |
253 | } |
254 | |
255 | static Uint16 SDL_ParseAudioFormat(const char *string) |
256 | { |
257 | Uint16 format = 0; |
258 | |
259 | switch (*string) { |
260 | case 'U': |
261 | ++string; |
262 | format |= 0x0000; |
263 | break; |
264 | case 'S': |
265 | ++string; |
266 | format |= 0x8000; |
267 | break; |
268 | default: |
269 | return 0; |
270 | } |
271 | switch (SDL_atoi(string)) { |
272 | case 8: |
273 | string += 1; |
274 | format |= 8; |
275 | break; |
276 | case 16: |
277 | string += 2; |
278 | format |= 16; |
279 | if ( SDL_strcmp(string, "LSB") == 0 |
280 | #if SDL_BYTEORDER == SDL_LIL_ENDIAN |
281 | || SDL_strcmp(string, "SYS") == 0 |
282 | #endif |
283 | ) { |
284 | format |= 0x0000; |
285 | } |
286 | if ( SDL_strcmp(string, "MSB") == 0 |
287 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN |
288 | || SDL_strcmp(string, "SYS") == 0 |
289 | #endif |
290 | ) { |
291 | format |= 0x1000; |
292 | } |
293 | break; |
294 | default: |
295 | return 0; |
296 | } |
297 | return format; |
298 | } |
299 | |
300 | int SDL_AudioInit(const char *driver_name) |
301 | { |
302 | SDL_AudioDevice *audio; |
303 | int i = 0, idx; |
304 | |
305 | /* Check to make sure we don't overwrite 'current_audio' */ |
306 | if ( current_audio != NULL ) { |
307 | SDL_AudioQuit(); |
308 | } |
309 | |
310 | /* Select the proper audio driver */ |
311 | audio = NULL; |
312 | idx = 0; |
313 | #if SDL_AUDIO_DRIVER_ESD |
314 | if ( (driver_name == NULL) && (SDL_getenv("ESPEAKER") != NULL) ) { |
315 | /* Ahem, we know that if ESPEAKER is set, user probably wants |
316 | to use ESD, but don't start it if it's not already running. |
317 | This probably isn't the place to do this, but... Shh! :) |
318 | */ |
319 | for ( i=0; bootstrap[i]; ++i ) { |
320 | if ( SDL_strcasecmp(bootstrap[i]->name, "esd") == 0 ) { |
321 | #ifdef HAVE_PUTENV |
322 | const char *esd_no_spawn; |
323 | |
324 | /* Don't start ESD if it's not running */ |
325 | esd_no_spawn = getenv("ESD_NO_SPAWN"); |
326 | if ( esd_no_spawn == NULL ) { |
327 | putenv("ESD_NO_SPAWN=1"); |
328 | } |
329 | #endif |
330 | if ( bootstrap[i]->available() ) { |
331 | audio = bootstrap[i]->create(0); |
332 | break; |
333 | } |
334 | #ifdef HAVE_UNSETENV |
335 | if ( esd_no_spawn == NULL ) { |
336 | unsetenv("ESD_NO_SPAWN"); |
337 | } |
338 | #endif |
339 | } |
340 | } |
341 | } |
342 | #endif /* SDL_AUDIO_DRIVER_ESD */ |
343 | if ( audio == NULL ) { |
344 | if ( driver_name != NULL ) { |
345 | #if 0 /* This will be replaced with a better driver selection API */ |
346 | if ( SDL_strrchr(driver_name, ':') != NULL ) { |
347 | idx = atoi(SDL_strrchr(driver_name, ':')+1); |
348 | } |
349 | #endif |
350 | for ( i=0; bootstrap[i]; ++i ) { |
351 | if (SDL_strcasecmp(bootstrap[i]->name, driver_name) == 0) { |
352 | if ( bootstrap[i]->available() ) { |
353 | audio=bootstrap[i]->create(idx); |
354 | break; |
355 | } |
356 | } |
357 | } |
358 | } else { |
359 | for ( i=0; bootstrap[i]; ++i ) { |
360 | if ( bootstrap[i]->available() ) { |
361 | audio = bootstrap[i]->create(idx); |
362 | if ( audio != NULL ) { |
363 | break; |
364 | } |
365 | } |
366 | } |
367 | } |
368 | if ( audio == NULL ) { |
369 | SDL_SetError("No available audio device"); |
370 | #if 0 /* Don't fail SDL_Init() if audio isn't available. |
371 | SDL_OpenAudio() will handle it at that point. *sigh* |
372 | */ |
373 | return(-1); |
374 | #endif |
375 | } |
376 | } |
377 | current_audio = audio; |
378 | if ( current_audio ) { |
379 | current_audio->name = bootstrap[i]->name; |
380 | if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) { |
381 | current_audio->LockAudio = SDL_LockAudio_Default; |
382 | current_audio->UnlockAudio = SDL_UnlockAudio_Default; |
383 | } |
384 | } |
385 | return(0); |
386 | } |
387 | |
388 | char *SDL_AudioDriverName(char *namebuf, int maxlen) |
389 | { |
390 | if ( current_audio != NULL ) { |
391 | SDL_strlcpy(namebuf, current_audio->name, maxlen); |
392 | return(namebuf); |
393 | } |
394 | return(NULL); |
395 | } |
396 | |
397 | int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained) |
398 | { |
399 | SDL_AudioDevice *audio; |
400 | const char *env; |
401 | |
402 | /* Start up the audio driver, if necessary */ |
403 | if ( ! current_audio ) { |
404 | if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) || |
405 | (current_audio == NULL) ) { |
406 | return(-1); |
407 | } |
408 | } |
409 | audio = current_audio; |
410 | |
411 | if (audio->opened) { |
412 | SDL_SetError("Audio device is already opened"); |
413 | return(-1); |
414 | } |
415 | |
416 | /* Verify some parameters */ |
417 | if ( desired->freq == 0 ) { |
418 | env = SDL_getenv("SDL_AUDIO_FREQUENCY"); |
419 | if ( env ) { |
420 | desired->freq = SDL_atoi(env); |
421 | } |
422 | } |
423 | if ( desired->freq == 0 ) { |
424 | /* Pick some default audio frequency */ |
425 | desired->freq = 22050; |
426 | } |
427 | if ( desired->format == 0 ) { |
428 | env = SDL_getenv("SDL_AUDIO_FORMAT"); |
429 | if ( env ) { |
430 | desired->format = SDL_ParseAudioFormat(env); |
431 | } |
432 | } |
433 | if ( desired->format == 0 ) { |
434 | /* Pick some default audio format */ |
435 | desired->format = AUDIO_S16; |
436 | } |
437 | if ( desired->channels == 0 ) { |
438 | env = SDL_getenv("SDL_AUDIO_CHANNELS"); |
439 | if ( env ) { |
440 | desired->channels = (Uint8)SDL_atoi(env); |
441 | } |
442 | } |
443 | if ( desired->channels == 0 ) { |
444 | /* Pick a default number of channels */ |
445 | desired->channels = 2; |
446 | } |
447 | switch ( desired->channels ) { |
448 | case 1: /* Mono */ |
449 | case 2: /* Stereo */ |
450 | case 4: /* surround */ |
451 | case 6: /* surround with center and lfe */ |
452 | break; |
453 | default: |
454 | SDL_SetError("1 (mono) and 2 (stereo) channels supported"); |
455 | return(-1); |
456 | } |
457 | if ( desired->samples == 0 ) { |
458 | env = SDL_getenv("SDL_AUDIO_SAMPLES"); |
459 | if ( env ) { |
460 | desired->samples = (Uint16)SDL_atoi(env); |
461 | } |
462 | } |
463 | if ( desired->samples == 0 ) { |
464 | /* Pick a default of ~46 ms at desired frequency */ |
465 | int samples = (desired->freq / 1000) * 46; |
466 | int power2 = 1; |
467 | while ( power2 < samples ) { |
468 | power2 *= 2; |
469 | } |
470 | desired->samples = power2; |
471 | } |
472 | if ( desired->callback == NULL ) { |
473 | SDL_SetError("SDL_OpenAudio() passed a NULL callback"); |
474 | return(-1); |
475 | } |
476 | |
477 | #if SDL_THREADS_DISABLED |
478 | /* Uses interrupt driven audio, without thread */ |
479 | #else |
480 | /* Create a semaphore for locking the sound buffers */ |
481 | audio->mixer_lock = SDL_CreateMutex(); |
482 | if ( audio->mixer_lock == NULL ) { |
483 | SDL_SetError("Couldn't create mixer lock"); |
484 | SDL_CloseAudio(); |
485 | return(-1); |
486 | } |
487 | #endif /* SDL_THREADS_DISABLED */ |
488 | |
489 | /* Calculate the silence and size of the audio specification */ |
490 | SDL_CalculateAudioSpec(desired); |
491 | |
492 | /* Open the audio subsystem */ |
493 | SDL_memcpy(&audio->spec, desired, sizeof(audio->spec)); |
494 | audio->convert.needed = 0; |
495 | audio->enabled = 1; |
496 | audio->paused = 1; |
497 | |
498 | audio->opened = audio->OpenAudio(audio, &audio->spec)+1; |
499 | |
500 | if ( ! audio->opened ) { |
501 | SDL_CloseAudio(); |
502 | return(-1); |
503 | } |
504 | |
505 | /* If the audio driver changes the buffer size, accept it */ |
506 | if ( audio->spec.samples != desired->samples ) { |
507 | desired->samples = audio->spec.samples; |
508 | SDL_CalculateAudioSpec(desired); |
509 | } |
510 | |
511 | /* Allocate a fake audio memory buffer */ |
512 | audio->fake_stream = SDL_AllocAudioMem(audio->spec.size); |
513 | if ( audio->fake_stream == NULL ) { |
514 | SDL_CloseAudio(); |
515 | SDL_OutOfMemory(); |
516 | return(-1); |
517 | } |
518 | |
519 | /* See if we need to do any conversion */ |
520 | if ( obtained != NULL ) { |
521 | SDL_memcpy(obtained, &audio->spec, sizeof(audio->spec)); |
522 | } else if ( desired->freq != audio->spec.freq || |
523 | desired->format != audio->spec.format || |
524 | desired->channels != audio->spec.channels ) { |
525 | /* Build an audio conversion block */ |
526 | if ( SDL_BuildAudioCVT(&audio->convert, |
527 | desired->format, desired->channels, |
528 | desired->freq, |
529 | audio->spec.format, audio->spec.channels, |
530 | audio->spec.freq) < 0 ) { |
531 | SDL_CloseAudio(); |
532 | return(-1); |
533 | } |
534 | if ( audio->convert.needed ) { |
535 | audio->convert.len = (int) ( ((double) audio->spec.size) / |
536 | audio->convert.len_ratio ); |
537 | audio->convert.buf =(Uint8 *)SDL_AllocAudioMem( |
538 | audio->convert.len*audio->convert.len_mult); |
539 | if ( audio->convert.buf == NULL ) { |
540 | SDL_CloseAudio(); |
541 | SDL_OutOfMemory(); |
542 | return(-1); |
543 | } |
544 | } |
545 | } |
546 | |
547 | /* Start the audio thread if necessary */ |
548 | switch (audio->opened) { |
549 | case 1: |
550 | /* Start the audio thread */ |
551 | #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) && !defined(__SYMBIAN32__) |
552 | #undef SDL_CreateThread |
553 | audio->thread = SDL_CreateThread(SDL_RunAudio, audio, NULL, NULL); |
554 | #else |
555 | audio->thread = SDL_CreateThread(SDL_RunAudio, audio); |
556 | #endif |
557 | if ( audio->thread == NULL ) { |
558 | SDL_CloseAudio(); |
559 | SDL_SetError("Couldn't create audio thread"); |
560 | return(-1); |
561 | } |
562 | break; |
563 | |
564 | default: |
565 | /* The audio is now playing */ |
566 | break; |
567 | } |
568 | |
569 | return(0); |
570 | } |
571 | |
572 | SDL_audiostatus SDL_GetAudioStatus(void) |
573 | { |
574 | SDL_AudioDevice *audio = current_audio; |
575 | SDL_audiostatus status; |
576 | |
577 | status = SDL_AUDIO_STOPPED; |
578 | if ( audio && audio->enabled ) { |
579 | if ( audio->paused ) { |
580 | status = SDL_AUDIO_PAUSED; |
581 | } else { |
582 | status = SDL_AUDIO_PLAYING; |
583 | } |
584 | } |
585 | return(status); |
586 | } |
587 | |
588 | void SDL_PauseAudio (int pause_on) |
589 | { |
590 | SDL_AudioDevice *audio = current_audio; |
591 | |
592 | if ( audio ) { |
593 | audio->paused = pause_on; |
594 | } |
595 | } |
596 | |
597 | void SDL_LockAudio (void) |
598 | { |
599 | SDL_AudioDevice *audio = current_audio; |
600 | |
601 | /* Obtain a lock on the mixing buffers */ |
602 | if ( audio && audio->LockAudio ) { |
603 | audio->LockAudio(audio); |
604 | } |
605 | } |
606 | |
607 | void SDL_UnlockAudio (void) |
608 | { |
609 | SDL_AudioDevice *audio = current_audio; |
610 | |
611 | /* Release lock on the mixing buffers */ |
612 | if ( audio && audio->UnlockAudio ) { |
613 | audio->UnlockAudio(audio); |
614 | } |
615 | } |
616 | |
617 | void SDL_CloseAudio (void) |
618 | { |
619 | SDL_QuitSubSystem(SDL_INIT_AUDIO); |
620 | } |
621 | |
622 | void SDL_AudioQuit(void) |
623 | { |
624 | SDL_AudioDevice *audio = current_audio; |
625 | |
626 | if ( audio ) { |
627 | audio->enabled = 0; |
628 | if ( audio->thread != NULL ) { |
629 | SDL_WaitThread(audio->thread, NULL); |
630 | } |
631 | if ( audio->mixer_lock != NULL ) { |
632 | SDL_DestroyMutex(audio->mixer_lock); |
633 | } |
634 | if ( audio->fake_stream != NULL ) { |
635 | SDL_FreeAudioMem(audio->fake_stream); |
636 | } |
637 | if ( audio->convert.needed ) { |
638 | SDL_FreeAudioMem(audio->convert.buf); |
639 | |
640 | } |
641 | if ( audio->opened ) { |
642 | audio->CloseAudio(audio); |
643 | audio->opened = 0; |
644 | } |
645 | /* Free the driver data */ |
646 | audio->free(audio); |
647 | current_audio = NULL; |
648 | } |
649 | } |
650 | |
651 | #define NUM_FORMATS 6 |
652 | static int format_idx; |
653 | static int format_idx_sub; |
654 | static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = { |
655 | { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB }, |
656 | { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB }, |
657 | { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 }, |
658 | { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 }, |
659 | { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 }, |
660 | { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 }, |
661 | }; |
662 | |
663 | Uint16 SDL_FirstAudioFormat(Uint16 format) |
664 | { |
665 | for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) { |
666 | if ( format_list[format_idx][0] == format ) { |
667 | break; |
668 | } |
669 | } |
670 | format_idx_sub = 0; |
671 | return(SDL_NextAudioFormat()); |
672 | } |
673 | |
674 | Uint16 SDL_NextAudioFormat(void) |
675 | { |
676 | if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) { |
677 | return(0); |
678 | } |
679 | return(format_list[format_idx][format_idx_sub++]); |
680 | } |
681 | |
682 | void SDL_CalculateAudioSpec(SDL_AudioSpec *spec) |
683 | { |
684 | switch (spec->format) { |
685 | case AUDIO_U8: |
686 | spec->silence = 0x80; |
687 | break; |
688 | default: |
689 | spec->silence = 0x00; |
690 | break; |
691 | } |
692 | spec->size = (spec->format&0xFF)/8; |
693 | spec->size *= spec->channels; |
694 | spec->size *= spec->samples; |
695 | } |