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 <signal.h>
|
|
32 #include <string.h>
|
|
33
|
|
34 #include "SDL.h"
|
|
35 #include "SDL_audio.h"
|
|
36 #include "SDL_timer.h"
|
|
37 #include "SDL_error.h"
|
|
38 #include "SDL_audio_c.h"
|
|
39 #include "SDL_audiomem.h"
|
|
40 #include "SDL_sysaudio.h"
|
|
41
|
|
42
|
|
43 /* Available audio drivers */
|
|
44 static AudioBootStrap *bootstrap[] = {
|
|
45 #ifdef unix
|
|
46 &AUDIO_bootstrap,
|
|
47 #endif
|
|
48 #ifdef linux
|
|
49 &DMA_bootstrap,
|
|
50 #endif
|
|
51 #ifdef ESD_SUPPORT
|
|
52 &ESD_bootstrap,
|
|
53 #endif
|
|
54 #ifdef ENABLE_DIRECTX
|
|
55 &DSOUND_bootstrap,
|
|
56 #endif
|
|
57 #ifdef ENABLE_WINDIB
|
|
58 &WAVEOUT_bootstrap,
|
|
59 #endif
|
|
60 #ifdef __BEOS__
|
|
61 &BAUDIO_bootstrap,
|
|
62 #endif
|
|
63 #ifdef macintosh
|
|
64 &AUDIO_bootstrap,
|
|
65 #endif
|
|
66 #ifdef _AIX
|
|
67 &Paud_bootstrap,
|
|
68 #endif
|
|
69 #ifdef ENABLE_CYBERGRAPHICS
|
|
70 &AHI_bootstrap,
|
|
71 #endif
|
|
72 NULL
|
|
73 };
|
|
74 SDL_AudioDevice *current_audio = NULL;
|
|
75
|
|
76 /* Various local functions */
|
|
77 int SDL_AudioInit(const char *driver_name);
|
|
78 void SDL_AudioQuit(void);
|
|
79
|
|
80 struct SignalSemaphore AudioSem;
|
|
81
|
|
82 /* The general mixing thread function */
|
|
83 int RunAudio(void *audiop)
|
|
84 {
|
|
85 SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
|
|
86 Uint8 *stream;
|
|
87 int stream_len;
|
|
88 void *udata;
|
|
89 void (*fill)(void *userdata,Uint8 *stream, int len);
|
|
90 int silence,started=0;
|
|
91
|
|
92 D(bug("Task audio started audio struct:<%lx>...\n",audiop));
|
|
93
|
|
94 D(bug("Before Openaudio..."));
|
|
95 if(audio->OpenAudio(audio, &audio->spec)==-1)
|
|
96 {
|
|
97 return(-1);
|
|
98 }
|
|
99
|
|
100 D(bug("OpenAudio...OK\n"));
|
|
101
|
|
102 /* Perform any thread setup */
|
|
103 if ( audio->ThreadInit ) {
|
|
104 audio->ThreadInit(audio);
|
|
105 }
|
|
106 audio->threadid = SDL_ThreadID();
|
|
107
|
|
108 /* Set up the mixing function */
|
|
109 fill = audio->spec.callback;
|
|
110 udata = audio->spec.userdata;
|
|
111 if ( audio->convert.needed ) {
|
|
112 if ( audio->convert.src_format == AUDIO_U8 ) {
|
|
113 silence = 0x80;
|
|
114 D(bug("*** Silence 0x80 ***\n"));
|
|
115 } else {
|
|
116 silence = 0;
|
|
117 }
|
|
118 stream_len = audio->convert.len;
|
|
119 } else {
|
|
120 silence = audio->spec.silence;
|
|
121 stream_len = audio->spec.size;
|
|
122 }
|
|
123 stream = audio->fake_stream;
|
|
124
|
|
125 ObtainSemaphore(&AudioSem);
|
|
126 ReleaseSemaphore(&AudioSem);
|
|
127
|
|
128 D(bug("Enering audio loop...\n"));
|
|
129
|
|
130 D(if(audio->convert.needed)bug("*** Conversion needed.\n"));
|
|
131
|
|
132 /* Loop, filling the audio buffers */
|
|
133 while ( audio->enabled )
|
|
134 {
|
|
135 /* Wait for new current buffer to finish playing */
|
|
136
|
|
137 if ( stream == audio->fake_stream )
|
|
138 SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
|
|
139 else
|
|
140 {
|
|
141 if(started>1)
|
|
142 {
|
|
143 // D(bug("Waiting audio...\n"));
|
|
144 audio->WaitAudio(audio);
|
|
145 }
|
|
146 }
|
|
147
|
|
148 ObtainSemaphore(&AudioSem);
|
|
149
|
|
150 /* Fill the current buffer with sound */
|
|
151 if ( audio->convert.needed ) {
|
|
152 stream = audio->convert.buf;
|
|
153 } else {
|
|
154 stream = audio->GetAudioBuf(audio);
|
|
155 }
|
|
156
|
|
157 if(stream!=audio->fake_stream)
|
|
158 memset(stream, silence, stream_len);
|
|
159
|
|
160 if ( ! audio->paused ) {
|
|
161 ObtainSemaphore(&audio->mixer_lock);
|
|
162 (*fill)(udata, stream, stream_len);
|
|
163 ReleaseSemaphore(&audio->mixer_lock);
|
|
164 }
|
|
165
|
|
166 /* Convert the audio if necessary */
|
|
167 if ( audio->convert.needed ) {
|
|
168 SDL_ConvertAudio(&audio->convert);
|
|
169 stream = audio->GetAudioBuf(audio);
|
|
170 memcpy(stream, audio->convert.buf,audio->convert.len_cvt);
|
|
171 }
|
|
172
|
|
173 if(stream!=audio->fake_stream)
|
|
174 {
|
|
175 // D(bug("Playing stream at %lx\n",stream));
|
|
176
|
|
177 audio->PlayAudio(audio);
|
|
178 started++;
|
|
179 }
|
|
180 ReleaseSemaphore(&AudioSem);
|
|
181
|
|
182 }
|
|
183 D(bug("Out of subtask loop...\n"));
|
|
184
|
|
185 /* Wait for the audio to drain.. */
|
|
186 if ( audio->WaitDone ) {
|
|
187 audio->WaitDone(audio);
|
|
188 }
|
|
189
|
|
190 D(bug("WaitAudio...Done\n"));
|
|
191
|
|
192 audio->CloseAudio(audio);
|
|
193
|
|
194 D(bug("CloseAudio..Done, subtask exiting...\n"));
|
|
195
|
|
196 return(0);
|
|
197 }
|
|
198
|
|
199 int SDL_AudioInit(const char *driver_name)
|
|
200 {
|
|
201 SDL_AudioDevice *audio;
|
|
202 int i = 0, idx;
|
|
203
|
|
204 /* Check to make sure we don't overwrite 'current_audio' */
|
|
205 if ( current_audio != NULL ) {
|
|
206 SDL_AudioQuit();
|
|
207 }
|
|
208
|
|
209 /* Select the proper audio driver */
|
|
210 audio = NULL;
|
|
211 idx = 0;
|
|
212
|
|
213 InitSemaphore(&AudioSem);
|
|
214
|
|
215 if ( audio == NULL ) {
|
|
216 if ( driver_name != NULL ) {
|
|
217 if ( strrchr(driver_name, ':') != NULL ) {
|
|
218 idx = atoi(strrchr(driver_name, ':')+1);
|
|
219 }
|
|
220 for ( i=0; bootstrap[i]; ++i ) {
|
|
221 if (strncmp(bootstrap[i]->name, driver_name,
|
|
222 strlen(bootstrap[i]->name)) == 0) {
|
|
223 if ( bootstrap[i]->available() ) {
|
|
224 audio=bootstrap[i]->create(idx);
|
|
225 break;
|
|
226 }
|
|
227 }
|
|
228 }
|
|
229 } else {
|
|
230 for ( i=0; bootstrap[i]; ++i ) {
|
|
231 if ( bootstrap[i]->available() ) {
|
|
232 audio = bootstrap[i]->create(idx);
|
|
233 if ( audio != NULL ) {
|
|
234 break;
|
|
235 }
|
|
236 }
|
|
237 }
|
|
238 }
|
|
239 if ( audio == NULL ) {
|
|
240 SDL_SetError("No available audio device");
|
|
241 #if 0 /* Don't fail SDL_Init() if audio isn't available.
|
|
242 SDL_OpenAudio() will handle it at that point. *sigh*
|
|
243 */
|
|
244 return(-1);
|
|
245 #endif
|
|
246 }
|
|
247 }
|
|
248 current_audio = audio;
|
|
249 if ( current_audio ) {
|
|
250 current_audio->name = bootstrap[i]->name;
|
|
251 }
|
|
252 return(0);
|
|
253 }
|
|
254
|
|
255 char *SDL_AudioDriverName(char *namebuf, int maxlen)
|
|
256 {
|
|
257 if ( current_audio != NULL ) {
|
|
258 strncpy(namebuf, current_audio->name, maxlen-1);
|
|
259 namebuf[maxlen-1] = '\0';
|
|
260 return(namebuf);
|
|
261 }
|
|
262 return(NULL);
|
|
263 }
|
|
264
|
|
265 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
|
|
266 {
|
|
267 SDL_AudioDevice *audio;
|
|
268
|
|
269 /* Start up the audio driver, if necessary */
|
|
270 if ( ! current_audio ) {
|
|
271 if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
|
|
272 (current_audio == NULL) ) {
|
|
273 return(-1);
|
|
274 }
|
|
275 }
|
|
276 audio = current_audio;
|
|
277
|
|
278 D(bug("Chiamata SDL_OpenAudio...\n"));
|
|
279
|
|
280 /* Verify some parameters */
|
|
281 if ( desired->callback == NULL ) {
|
|
282 SDL_SetError("SDL_OpenAudio() passed a NULL callback");
|
|
283 return(-1);
|
|
284 }
|
|
285 switch ( desired->channels ) {
|
|
286 case 1: /* Mono */
|
|
287 case 2: /* Stereo */
|
|
288 break;
|
|
289 default:
|
|
290 SDL_SetError("1 (mono) and 2 (stereo) channels supported");
|
|
291 return(-1);
|
|
292 }
|
|
293
|
|
294 /* Create a semaphore for locking the sound buffers */
|
|
295 InitSemaphore(&audio->mixer_lock);
|
|
296
|
|
297 /* Calculate the silence and size of the audio specification */
|
|
298 SDL_CalculateAudioSpec(desired);
|
|
299
|
|
300 /* Open the audio subsystem */
|
|
301 memcpy(&audio->spec, desired, sizeof(audio->spec));
|
|
302 audio->convert.needed = 0;
|
|
303 audio->enabled = 1;
|
|
304 audio->paused = 1;
|
|
305
|
|
306 ObtainSemaphore(&AudioSem);
|
|
307
|
|
308 audio->thread = SDL_CreateThread(RunAudio, audio);
|
|
309
|
|
310 if ( audio->thread == NULL ) {
|
|
311 ReleaseSemaphore(&AudioSem);
|
|
312 SDL_CloseAudio();
|
|
313 SDL_SetError("Couldn't create audio thread");
|
|
314 return(-1);
|
|
315 }
|
|
316
|
|
317 /* If the audio driver changes the buffer size, accept it */
|
|
318 if ( audio->spec.samples != desired->samples ) {
|
|
319 desired->samples = audio->spec.samples;
|
|
320 SDL_CalculateAudioSpec(desired);
|
|
321 }
|
|
322
|
|
323 /* Allocate a fake audio memory buffer */
|
|
324 audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
|
|
325 if ( audio->fake_stream == NULL ) {
|
|
326 ReleaseSemaphore(&AudioSem);
|
|
327 SDL_CloseAudio();
|
|
328 SDL_OutOfMemory();
|
|
329 return(-1);
|
|
330 }
|
|
331
|
|
332 /* See if we need to do any conversion */
|
|
333 if ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
|
|
334 /* Just copy over the desired audio specification */
|
|
335 if ( obtained != NULL ) {
|
|
336 memcpy(obtained, &audio->spec, sizeof(audio->spec));
|
|
337 }
|
|
338 } else {
|
|
339 /* Copy over the audio specification if possible */
|
|
340 if ( obtained != NULL ) {
|
|
341 memcpy(obtained, &audio->spec, sizeof(audio->spec));
|
|
342 } else {
|
|
343 /* Build an audio conversion block */
|
|
344 D(bug("Need conversion:\n desired: C:%ld F:%ld T:%lx\navailable: C:%ld F:%ld T:%lx\n",
|
|
345 desired->channels, desired->freq, desired->format,
|
|
346 audio->spec.channels,audio->spec.freq,audio->spec.format));
|
|
347
|
|
348 Forbid();
|
|
349
|
|
350 // Magari poi lo sostiutisco con un semaforo.
|
|
351
|
|
352 if ( SDL_BuildAudioCVT(&audio->convert,
|
|
353 desired->format, desired->channels,
|
|
354 desired->freq,
|
|
355 audio->spec.format, audio->spec.channels,
|
|
356 audio->spec.freq) < 0 ) {
|
|
357 ReleaseSemaphore(&AudioSem);
|
|
358 SDL_CloseAudio();
|
|
359 return(-1);
|
|
360 }
|
|
361 if ( audio->convert.needed ) {
|
|
362 audio->convert.len = desired->size;
|
|
363 audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
|
|
364 audio->convert.len*audio->convert.len_mult);
|
|
365 if ( audio->convert.buf == NULL ) {
|
|
366 ReleaseSemaphore(&AudioSem);
|
|
367 SDL_CloseAudio();
|
|
368 SDL_OutOfMemory();
|
|
369 return(-1);
|
|
370 }
|
|
371 }
|
|
372 }
|
|
373 }
|
|
374
|
|
375 ReleaseSemaphore(&AudioSem);
|
|
376
|
|
377 D(bug("SDL_OpenAudio USCITA...\n"));
|
|
378
|
|
379 return(0);
|
|
380 }
|
|
381
|
|
382 SDL_audiostatus SDL_GetAudioStatus(void)
|
|
383 {
|
|
384 SDL_AudioDevice *audio = current_audio;
|
|
385 SDL_audiostatus status;
|
|
386
|
|
387 status = SDL_AUDIO_STOPPED;
|
|
388 if ( audio && audio->enabled ) {
|
|
389 if ( audio->paused ) {
|
|
390 status = SDL_AUDIO_PAUSED;
|
|
391 } else {
|
|
392 status = SDL_AUDIO_PLAYING;
|
|
393 }
|
|
394 }
|
|
395 return(status);
|
|
396 }
|
|
397
|
|
398 void SDL_PauseAudio (int pause_on)
|
|
399 {
|
|
400 SDL_AudioDevice *audio = current_audio;
|
|
401
|
|
402 if ( audio ) {
|
|
403 audio->paused = pause_on;
|
|
404 }
|
|
405 }
|
|
406
|
|
407 void SDL_LockAudio (void)
|
|
408 {
|
|
409 SDL_AudioDevice *audio = current_audio;
|
|
410
|
|
411 /* Obtain a lock on the mixing buffers */
|
|
412 if ( audio ) {
|
|
413 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
|
|
414 return;
|
|
415 }
|
|
416 ObtainSemaphore(&audio->mixer_lock);
|
|
417 }
|
|
418 }
|
|
419
|
|
420 void SDL_UnlockAudio (void)
|
|
421 {
|
|
422 SDL_AudioDevice *audio = current_audio;
|
|
423
|
|
424 /* Release lock on the mixing buffers */
|
|
425 if ( audio ) {
|
|
426 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
|
|
427 return;
|
|
428 }
|
|
429 ReleaseSemaphore(&audio->mixer_lock);
|
|
430 }
|
|
431 }
|
|
432
|
|
433 void SDL_CloseAudio (void)
|
|
434 {
|
|
435 SDL_AudioDevice *audio = current_audio;
|
|
436
|
|
437 if ( audio ) {
|
|
438 if(audio->enabled)
|
|
439 {
|
|
440 audio->enabled = 0;
|
|
441
|
|
442 if ( audio->thread != NULL ) {
|
|
443 D(bug("Waiting audio thread...\n"));
|
|
444 SDL_WaitThread(audio->thread, NULL);
|
|
445 D(bug("...audio replied\n"));
|
|
446 }
|
|
447 }
|
|
448
|
|
449 if ( audio->fake_stream != NULL ) {
|
|
450 SDL_FreeAudioMem(audio->fake_stream);
|
|
451 audio->fake_stream=NULL;
|
|
452 }
|
|
453 if ( audio->convert.needed && current_audio->convert.buf!=NULL) {
|
|
454 SDL_FreeAudioMem(audio->convert.buf);
|
|
455 current_audio->convert.buf=NULL;
|
|
456 }
|
|
457 }
|
|
458 SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
459 }
|
|
460
|
|
461 void SDL_AudioQuit(void)
|
|
462 {
|
|
463 if ( current_audio ) {
|
|
464 if(current_audio->enabled)
|
|
465 {
|
|
466 D(bug("Closing audio in AudioQuit...\n"));
|
|
467 current_audio->enabled = 0;
|
|
468
|
|
469 if ( current_audio->thread != NULL ) {
|
|
470 D(bug("Waiting audio thread...\n"));
|
|
471 SDL_WaitThread(current_audio->thread, NULL);
|
|
472 D(bug("...audio replied\n"));
|
|
473 }
|
|
474 }
|
|
475 if ( current_audio->fake_stream != NULL ) {
|
|
476 SDL_FreeAudioMem(current_audio->fake_stream);
|
|
477 }
|
|
478 if ( current_audio->convert.needed &&
|
|
479 current_audio->convert.buf) {
|
|
480 SDL_FreeAudioMem(current_audio->convert.buf);
|
|
481 }
|
|
482
|
|
483 current_audio->free(current_audio);
|
|
484 current_audio = NULL;
|
|
485 }
|
|
486 }
|
|
487
|
|
488 #define NUM_FORMATS 6
|
|
489 static int format_idx;
|
|
490 static int format_idx_sub;
|
|
491 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
|
|
492 { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
|
|
493 { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
|
|
494 { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
|
|
495 { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
|
|
496 { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
|
|
497 { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
|
|
498 };
|
|
499
|
|
500 Uint16 SDL_FirstAudioFormat(Uint16 format)
|
|
501 {
|
|
502 for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
|
|
503 if ( format_list[format_idx][0] == format ) {
|
|
504 break;
|
|
505 }
|
|
506 }
|
|
507 format_idx_sub = 0;
|
|
508 return(SDL_NextAudioFormat());
|
|
509 }
|
|
510
|
|
511 Uint16 SDL_NextAudioFormat(void)
|
|
512 {
|
|
513 if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
|
|
514 return(0);
|
|
515 }
|
|
516 return(format_list[format_idx][format_idx_sub++]);
|
|
517 }
|
|
518
|
|
519 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
|
|
520 {
|
|
521 switch (spec->format) {
|
|
522 case AUDIO_U8:
|
|
523 spec->silence = 0x80;
|
|
524 break;
|
|
525 default:
|
|
526 spec->silence = 0x00;
|
|
527 break;
|
|
528 }
|
|
529 spec->size = (spec->format&0xFF)/8;
|
|
530 spec->size *= spec->channels;
|
|
531 spec->size *= spec->samples;
|
|
532 }
|