0
|
1 /*
|
|
2 SDL - Simple DirectMedia Layer
|
|
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
|
|
4
|
|
5 This library is free software; you can redistribute it and/or
|
|
6 modify it under the terms of the GNU Library General Public
|
|
7 License as published by the Free Software Foundation; either
|
|
8 version 2 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 Library General Public License for more details.
|
|
14
|
|
15 You should have received a copy of the GNU Library General Public
|
|
16 License along with this library; if not, write to the Free
|
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
18
|
|
19 Sam Lantinga
|
|
20 slouken@devolution.com
|
|
21 */
|
|
22
|
|
23 #ifdef SAVE_RCSID
|
|
24 static char rcsid =
|
|
25 "@(#) $Id$";
|
|
26 #endif
|
|
27
|
|
28 /* Allow access to a raw mixing buffer */
|
|
29 #include <stdlib.h>
|
|
30 #include <stdio.h>
|
|
31 #include <string.h>
|
|
32
|
|
33 #include "SDL.h"
|
|
34 #include "SDL_audio.h"
|
|
35 #include "SDL_timer.h"
|
|
36 #include "SDL_error.h"
|
|
37 #include "SDL_audio_c.h"
|
|
38 #include "SDL_audiomem.h"
|
|
39 #include "SDL_sysaudio.h"
|
|
40
|
|
41 /* Available audio drivers */
|
|
42 static AudioBootStrap *bootstrap[] = {
|
|
43 #if defined(unix) && \
|
|
44 !defined(linux) && !defined(__FreeBSD__) && !defined(__CYGWIN32__) \
|
|
45 && !defined(__bsdi__)
|
|
46 &AUDIO_bootstrap,
|
|
47 #endif
|
|
48 #ifdef OSS_SUPPORT
|
|
49 &DSP_bootstrap,
|
|
50 &DMA_bootstrap,
|
|
51 #endif
|
|
52 #ifdef ALSA_SUPPORT
|
|
53 &ALSA_bootstrap,
|
|
54 #endif
|
|
55 #ifdef ARTSC_SUPPORT
|
|
56 &ARTSC_bootstrap,
|
|
57 #endif
|
|
58 #ifdef ESD_SUPPORT
|
|
59 &ESD_bootstrap,
|
|
60 #endif
|
|
61 #ifdef NAS_SUPPORT
|
|
62 &NAS_bootstrap,
|
|
63 #endif
|
|
64 #ifdef ENABLE_DIRECTX
|
|
65 &DSOUND_bootstrap,
|
|
66 #endif
|
|
67 #ifdef ENABLE_WINDIB
|
|
68 &WAVEOUT_bootstrap,
|
|
69 #endif
|
|
70 #ifdef __BEOS__
|
|
71 &BAUDIO_bootstrap,
|
|
72 #endif
|
|
73 #if defined(macintosh) || TARGET_API_MAC_CARBON
|
|
74 &SNDMGR_bootstrap,
|
|
75 #endif
|
|
76 #ifdef _AIX
|
|
77 &Paud_bootstrap,
|
|
78 #endif
|
|
79 NULL
|
|
80 };
|
|
81 SDL_AudioDevice *current_audio = NULL;
|
|
82
|
|
83 /* Various local functions */
|
|
84 int SDL_AudioInit(const char *driver_name);
|
|
85 void SDL_AudioQuit(void);
|
|
86
|
|
87
|
|
88 /* The general mixing thread function */
|
|
89 int SDL_RunAudio(void *audiop)
|
|
90 {
|
|
91 SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
|
|
92 Uint8 *stream;
|
|
93 int stream_len;
|
|
94 void *udata;
|
|
95 void (*fill)(void *userdata,Uint8 *stream, int len);
|
|
96 int silence;
|
|
97
|
|
98 /* Perform any thread setup */
|
|
99 if ( audio->ThreadInit ) {
|
|
100 audio->ThreadInit(audio);
|
|
101 }
|
|
102 audio->threadid = SDL_ThreadID();
|
|
103
|
|
104 /* Set up the mixing function */
|
|
105 fill = audio->spec.callback;
|
|
106 udata = audio->spec.userdata;
|
|
107 if ( audio->convert.needed ) {
|
|
108 if ( audio->convert.src_format == AUDIO_U8 ) {
|
|
109 silence = 0x80;
|
|
110 } else {
|
|
111 silence = 0;
|
|
112 }
|
|
113 stream_len = audio->convert.len;
|
|
114 } else {
|
|
115 silence = audio->spec.silence;
|
|
116 stream_len = audio->spec.size;
|
|
117 }
|
|
118 stream = audio->fake_stream;
|
|
119
|
|
120 /* Loop, filling the audio buffers */
|
|
121 while ( audio->enabled ) {
|
|
122
|
|
123 /* Wait for new current buffer to finish playing */
|
|
124 if ( stream == audio->fake_stream ) {
|
|
125 SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
|
|
126 } else {
|
|
127 audio->WaitAudio(audio);
|
|
128 }
|
|
129
|
|
130 /* Fill the current buffer with sound */
|
|
131 if ( audio->convert.needed ) {
|
|
132 /* The buffer may not be allocated yet */
|
|
133 if ( audio->convert.buf ) {
|
|
134 stream = audio->convert.buf;
|
|
135 } else {
|
|
136 continue;
|
|
137 }
|
|
138 } else {
|
|
139 stream = audio->GetAudioBuf(audio);
|
|
140 if ( stream == NULL ) {
|
|
141 stream = audio->fake_stream;
|
|
142 }
|
|
143 }
|
|
144 memset(stream, silence, stream_len);
|
|
145
|
|
146 if ( ! audio->paused ) {
|
|
147 SDL_mutexP(audio->mixer_lock);
|
|
148 (*fill)(udata, stream, stream_len);
|
|
149 SDL_mutexV(audio->mixer_lock);
|
|
150 }
|
|
151
|
|
152 /* Convert the audio if necessary */
|
|
153 if ( audio->convert.needed ) {
|
|
154 SDL_ConvertAudio(&audio->convert);
|
|
155 stream = audio->GetAudioBuf(audio);
|
|
156 if ( stream == NULL ) {
|
|
157 stream = audio->fake_stream;
|
|
158 }
|
|
159 memcpy(stream, audio->convert.buf,
|
|
160 audio->convert.len_cvt);
|
|
161 }
|
|
162
|
|
163 /* Ready current buffer for play and change current buffer */
|
|
164 if ( stream != audio->fake_stream ) {
|
|
165 audio->PlayAudio(audio);
|
|
166 }
|
|
167 }
|
|
168 /* Wait for the audio to drain.. */
|
|
169 if ( audio->WaitDone ) {
|
|
170 audio->WaitDone(audio);
|
|
171 }
|
|
172 return(0);
|
|
173 }
|
|
174
|
|
175 int SDL_AudioInit(const char *driver_name)
|
|
176 {
|
|
177 SDL_AudioDevice *audio;
|
|
178 int i = 0, idx;
|
|
179
|
|
180 /* Check to make sure we don't overwrite 'current_audio' */
|
|
181 if ( current_audio != NULL ) {
|
|
182 SDL_AudioQuit();
|
|
183 }
|
|
184
|
|
185 /* Select the proper audio driver */
|
|
186 audio = NULL;
|
|
187 idx = 0;
|
|
188 #ifdef unix
|
|
189 if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
|
|
190 /* Ahem, we know that if ESPEAKER is set, user probably wants
|
|
191 to use ESD, but don't start it if it's not already running.
|
|
192 This probably isn't the place to do this, but... Shh! :)
|
|
193 */
|
|
194 for ( i=0; bootstrap[i]; ++i ) {
|
|
195 if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
|
|
196 const char *esd_no_spawn;
|
|
197
|
|
198 /* Don't start ESD if it's not running */
|
|
199 esd_no_spawn = getenv("ESD_NO_SPAWN");
|
|
200 if ( esd_no_spawn == NULL ) {
|
|
201 putenv("ESD_NO_SPAWN=1");
|
|
202 }
|
|
203 if ( bootstrap[i]->available() ) {
|
|
204 audio = bootstrap[i]->create(0);
|
|
205 break;
|
|
206 }
|
|
207 #ifdef linux /* No unsetenv() on most platforms */
|
|
208 if ( esd_no_spawn == NULL ) {
|
|
209 unsetenv("ESD_NO_SPAWN");
|
|
210 }
|
|
211 #endif
|
|
212 }
|
|
213 }
|
|
214 }
|
|
215 #endif /* unix */
|
|
216 if ( audio == NULL ) {
|
|
217 if ( driver_name != NULL ) {
|
|
218 #if 0 /* This will be replaced with a better driver selection API */
|
|
219 if ( strrchr(driver_name, ':') != NULL ) {
|
|
220 idx = atoi(strrchr(driver_name, ':')+1);
|
|
221 }
|
|
222 #endif
|
|
223 for ( i=0; bootstrap[i]; ++i ) {
|
|
224 if (strncmp(bootstrap[i]->name, driver_name,
|
|
225 strlen(bootstrap[i]->name)) == 0) {
|
|
226 if ( bootstrap[i]->available() ) {
|
|
227 audio=bootstrap[i]->create(idx);
|
|
228 break;
|
|
229 }
|
|
230 }
|
|
231 }
|
|
232 } else {
|
|
233 for ( i=0; bootstrap[i]; ++i ) {
|
|
234 if ( bootstrap[i]->available() ) {
|
|
235 audio = bootstrap[i]->create(idx);
|
|
236 if ( audio != NULL ) {
|
|
237 break;
|
|
238 }
|
|
239 }
|
|
240 }
|
|
241 }
|
|
242 if ( audio == NULL ) {
|
|
243 SDL_SetError("No available audio device");
|
|
244 #if 0 /* Don't fail SDL_Init() if audio isn't available.
|
|
245 SDL_OpenAudio() will handle it at that point. *sigh*
|
|
246 */
|
|
247 return(-1);
|
|
248 #endif
|
|
249 }
|
|
250 }
|
|
251 current_audio = audio;
|
|
252 if ( current_audio ) {
|
|
253 current_audio->name = bootstrap[i]->name;
|
|
254 }
|
|
255 return(0);
|
|
256 }
|
|
257
|
|
258 char *SDL_AudioDriverName(char *namebuf, int maxlen)
|
|
259 {
|
|
260 if ( current_audio != NULL ) {
|
|
261 strncpy(namebuf, current_audio->name, maxlen-1);
|
|
262 namebuf[maxlen-1] = '\0';
|
|
263 return(namebuf);
|
|
264 }
|
|
265 return(NULL);
|
|
266 }
|
|
267
|
|
268 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
|
|
269 {
|
|
270 SDL_AudioDevice *audio;
|
|
271
|
|
272 /* Start up the audio driver, if necessary */
|
|
273 if ( ! current_audio ) {
|
|
274 if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
|
|
275 (current_audio == NULL) ) {
|
|
276 return(-1);
|
|
277 }
|
|
278 }
|
|
279 audio = current_audio;
|
|
280
|
|
281 /* Verify some parameters */
|
|
282 if ( desired->callback == NULL ) {
|
|
283 SDL_SetError("SDL_OpenAudio() passed a NULL callback");
|
|
284 return(-1);
|
|
285 }
|
|
286 switch ( desired->channels ) {
|
|
287 case 1: /* Mono */
|
|
288 case 2: /* Stereo */
|
|
289 break;
|
|
290 default:
|
|
291 SDL_SetError("1 (mono) and 2 (stereo) channels supported");
|
|
292 return(-1);
|
|
293 }
|
|
294
|
|
295 #ifdef macintosh
|
|
296 /* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
|
|
297 #else
|
|
298 /* Create a semaphore for locking the sound buffers */
|
|
299 audio->mixer_lock = SDL_CreateMutex();
|
|
300 if ( audio->mixer_lock == NULL ) {
|
|
301 SDL_SetError("Couldn't create mixer lock");
|
|
302 SDL_CloseAudio();
|
|
303 return(-1);
|
|
304 }
|
|
305 #endif
|
|
306
|
|
307 /* Calculate the silence and size of the audio specification */
|
|
308 SDL_CalculateAudioSpec(desired);
|
|
309
|
|
310 /* Open the audio subsystem */
|
|
311 memcpy(&audio->spec, desired, sizeof(audio->spec));
|
|
312 audio->convert.needed = 0;
|
|
313 audio->enabled = 1;
|
|
314 audio->paused = 1;
|
|
315 audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
|
|
316 if ( ! audio->opened ) {
|
|
317 SDL_CloseAudio();
|
|
318 return(-1);
|
|
319 }
|
|
320
|
|
321 /* If the audio driver changes the buffer size, accept it */
|
|
322 if ( audio->spec.samples != desired->samples ) {
|
|
323 desired->samples = audio->spec.samples;
|
|
324 SDL_CalculateAudioSpec(desired);
|
|
325 }
|
|
326
|
|
327 /* Allocate a fake audio memory buffer */
|
|
328 audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
|
|
329 if ( audio->fake_stream == NULL ) {
|
|
330 SDL_CloseAudio();
|
|
331 SDL_OutOfMemory();
|
|
332 return(-1);
|
|
333 }
|
|
334
|
|
335 /* See if we need to do any conversion */
|
|
336 if ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
|
|
337 /* Just copy over the desired audio specification */
|
|
338 if ( obtained != NULL ) {
|
|
339 memcpy(obtained, &audio->spec, sizeof(audio->spec));
|
|
340 }
|
|
341 } else {
|
|
342 /* Copy over the audio specification if possible */
|
|
343 if ( obtained != NULL ) {
|
|
344 memcpy(obtained, &audio->spec, sizeof(audio->spec));
|
|
345 } else {
|
|
346 /* Build an audio conversion block */
|
|
347 if ( SDL_BuildAudioCVT(&audio->convert,
|
|
348 desired->format, desired->channels,
|
|
349 desired->freq,
|
|
350 audio->spec.format, audio->spec.channels,
|
|
351 audio->spec.freq) < 0 ) {
|
|
352 SDL_CloseAudio();
|
|
353 return(-1);
|
|
354 }
|
|
355 if ( audio->convert.needed ) {
|
|
356 audio->convert.len = desired->size;
|
|
357 audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
|
|
358 audio->convert.len*audio->convert.len_mult);
|
|
359 if ( audio->convert.buf == NULL ) {
|
|
360 SDL_CloseAudio();
|
|
361 SDL_OutOfMemory();
|
|
362 return(-1);
|
|
363 }
|
|
364 }
|
|
365 }
|
|
366 }
|
|
367
|
|
368 /* Start the audio thread if necessary */
|
|
369 switch (audio->opened) {
|
|
370 case 1:
|
|
371 /* Start the audio thread */
|
|
372 audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
|
|
373 if ( audio->thread == NULL ) {
|
|
374 SDL_CloseAudio();
|
|
375 SDL_SetError("Couldn't create audio thread");
|
|
376 return(-1);
|
|
377 }
|
|
378 break;
|
|
379
|
|
380 default:
|
|
381 /* The audio is now playing */
|
|
382 break;
|
|
383 }
|
|
384 return(0);
|
|
385 }
|
|
386
|
|
387 SDL_audiostatus SDL_GetAudioStatus(void)
|
|
388 {
|
|
389 SDL_AudioDevice *audio = current_audio;
|
|
390 SDL_audiostatus status;
|
|
391
|
|
392 status = SDL_AUDIO_STOPPED;
|
|
393 if ( audio && audio->enabled ) {
|
|
394 if ( audio->paused ) {
|
|
395 status = SDL_AUDIO_PAUSED;
|
|
396 } else {
|
|
397 status = SDL_AUDIO_PLAYING;
|
|
398 }
|
|
399 }
|
|
400 return(status);
|
|
401 }
|
|
402
|
|
403 void SDL_PauseAudio (int pause_on)
|
|
404 {
|
|
405 SDL_AudioDevice *audio = current_audio;
|
|
406
|
|
407 if ( audio ) {
|
|
408 audio->paused = pause_on;
|
|
409 }
|
|
410 }
|
|
411
|
|
412 void SDL_LockAudio (void)
|
|
413 {
|
|
414 SDL_AudioDevice *audio = current_audio;
|
|
415
|
|
416 /* Obtain a lock on the mixing buffers */
|
|
417 if ( audio ) {
|
|
418 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
|
|
419 return;
|
|
420 }
|
|
421 SDL_mutexP(audio->mixer_lock);
|
|
422 }
|
|
423 }
|
|
424
|
|
425 void SDL_UnlockAudio (void)
|
|
426 {
|
|
427 SDL_AudioDevice *audio = current_audio;
|
|
428
|
|
429 /* Release lock on the mixing buffers */
|
|
430 if ( audio ) {
|
|
431 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
|
|
432 return;
|
|
433 }
|
|
434 SDL_mutexV(audio->mixer_lock);
|
|
435 }
|
|
436 }
|
|
437
|
|
438 void SDL_CloseAudio (void)
|
|
439 {
|
|
440 SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
441 }
|
|
442
|
|
443 void SDL_AudioQuit(void)
|
|
444 {
|
|
445 SDL_AudioDevice *audio = current_audio;
|
|
446
|
|
447 if ( audio ) {
|
|
448 audio->enabled = 0;
|
|
449 if ( audio->thread != NULL ) {
|
|
450 SDL_WaitThread(audio->thread, NULL);
|
|
451 }
|
|
452 if ( audio->mixer_lock != NULL ) {
|
|
453 SDL_DestroyMutex(audio->mixer_lock);
|
|
454 }
|
|
455 if ( audio->fake_stream != NULL ) {
|
|
456 SDL_FreeAudioMem(audio->fake_stream);
|
|
457 }
|
|
458 if ( audio->convert.needed ) {
|
|
459 SDL_FreeAudioMem(audio->convert.buf);
|
|
460 }
|
|
461 if ( audio->opened ) {
|
|
462 audio->CloseAudio(audio);
|
|
463 audio->opened = 0;
|
|
464 }
|
|
465
|
|
466 /* Free the driver data */
|
|
467 audio->free(audio);
|
|
468 current_audio = NULL;
|
|
469 }
|
|
470 }
|
|
471
|
|
472 #define NUM_FORMATS 6
|
|
473 static int format_idx;
|
|
474 static int format_idx_sub;
|
|
475 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
|
|
476 { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
|
|
477 { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
|
|
478 { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
|
|
479 { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
|
|
480 { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
|
|
481 { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
|
|
482 };
|
|
483
|
|
484 Uint16 SDL_FirstAudioFormat(Uint16 format)
|
|
485 {
|
|
486 for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
|
|
487 if ( format_list[format_idx][0] == format ) {
|
|
488 break;
|
|
489 }
|
|
490 }
|
|
491 format_idx_sub = 0;
|
|
492 return(SDL_NextAudioFormat());
|
|
493 }
|
|
494
|
|
495 Uint16 SDL_NextAudioFormat(void)
|
|
496 {
|
|
497 if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
|
|
498 return(0);
|
|
499 }
|
|
500 return(format_list[format_idx][format_idx_sub++]);
|
|
501 }
|
|
502
|
|
503 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
|
|
504 {
|
|
505 switch (spec->format) {
|
|
506 case AUDIO_U8:
|
|
507 spec->silence = 0x80;
|
|
508 break;
|
|
509 default:
|
|
510 spec->silence = 0x00;
|
|
511 break;
|
|
512 }
|
|
513 spec->size = (spec->format&0xFF)/8;
|
|
514 spec->size *= spec->channels;
|
|
515 spec->size *= spec->samples;
|
|
516 }
|