comparison src/audio/SDL_audio.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children 75a95f82bc1f
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
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 }