src/audio/SDL_audio.c
author Sam Lantinga
Sat, 05 Oct 2002 16:50:56 +0000
changeset 509 dad72daf44b3
parent 398 d219b0e02f5f
child 630 550bccdf04bd
permissions -rw-r--r--
Added initial support for Dreamcast (thanks HERO!)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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@libsdl.org
    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 #ifdef OPENBSD_AUDIO_SUPPORT
    44 	&OPENBSD_AUDIO_bootstrap,
    45 #endif
    46 #ifdef OSS_SUPPORT
    47 	&DSP_bootstrap,
    48 	&DMA_bootstrap,
    49 #endif
    50 #ifdef ALSA_SUPPORT
    51 	&ALSA_bootstrap,
    52 #endif
    53 #ifdef SUNAUDIO_SUPPORT
    54 	&SUNAUDIO_bootstrap,
    55 #endif
    56 #ifdef DMEDIA_SUPPORT
    57 	&DMEDIA_bootstrap,
    58 #endif
    59 #ifdef ARTSC_SUPPORT
    60 	&ARTSC_bootstrap,
    61 #endif
    62 #ifdef ESD_SUPPORT
    63 	&ESD_bootstrap,
    64 #endif
    65 #ifdef NAS_SUPPORT
    66 	&NAS_bootstrap,
    67 #endif
    68 #ifdef ENABLE_DIRECTX
    69 	&DSOUND_bootstrap,
    70 #endif
    71 #ifdef ENABLE_WINDIB
    72 	&WAVEOUT_bootstrap,
    73 #endif
    74 #ifdef __BEOS__
    75 	&BAUDIO_bootstrap,
    76 #endif
    77 #if defined(macintosh) || TARGET_API_MAC_CARBON
    78 	&SNDMGR_bootstrap,
    79 #endif
    80 #ifdef _AIX
    81 	&Paud_bootstrap,
    82 #endif
    83 #ifdef ENABLE_AHI
    84 	&AHI_bootstrap,
    85 #endif
    86 #ifdef MINTAUDIO_SUPPORT
    87 	&MINTAUDIO_bootstrap,
    88 #endif
    89 #ifdef DISKAUD_SUPPORT
    90 	&DISKAUD_bootstrap,
    91 #endif
    92 #ifdef ENABLE_DC
    93 	&DCAUD_bootstrap,
    94 #endif
    95 	NULL
    96 };
    97 SDL_AudioDevice *current_audio = NULL;
    98 
    99 /* Various local functions */
   100 int SDL_AudioInit(const char *driver_name);
   101 void SDL_AudioQuit(void);
   102 
   103 #ifdef ENABLE_AHI
   104 static int audio_configured = 0;
   105 #endif
   106 
   107 /* The general mixing thread function */
   108 int SDL_RunAudio(void *audiop)
   109 {
   110 	SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
   111 	Uint8 *stream;
   112 	int    stream_len;
   113 	void  *udata;
   114 	void (*fill)(void *userdata,Uint8 *stream, int len);
   115 	int    silence;
   116 #ifdef ENABLE_AHI
   117 	int started = 0;
   118 
   119 /* AmigaOS NEEDS that the audio driver is opened in the thread that uses it! */
   120 
   121 	D(bug("Task audio started audio struct:<%lx>...\n",audiop));
   122 
   123 	D(bug("Before Openaudio..."));
   124 	if(audio->OpenAudio(audio, &audio->spec)==-1)
   125 	{
   126 		D(bug("Open audio failed...\n"));
   127 		return(-1);
   128 	}
   129 	D(bug("OpenAudio...OK\n"));
   130 #endif
   131 
   132 	/* Perform any thread setup */
   133 	if ( audio->ThreadInit ) {
   134 		audio->ThreadInit(audio);
   135 	}
   136 	audio->threadid = SDL_ThreadID();
   137 
   138 	/* Set up the mixing function */
   139 	fill  = audio->spec.callback;
   140 	udata = audio->spec.userdata;
   141 
   142 #ifdef ENABLE_AHI
   143 	audio_configured = 1;
   144 
   145 	D(bug("Audio configured... Checking for conversion\n"));
   146 	SDL_mutexP(audio->mixer_lock);
   147 	D(bug("Semaphore obtained...\n"));
   148 #endif
   149 
   150 	if ( audio->convert.needed ) {
   151 		if ( audio->convert.src_format == AUDIO_U8 ) {
   152 			silence = 0x80;
   153 		} else {
   154 			silence = 0;
   155 		}
   156 		stream_len = audio->convert.len;
   157 	} else {
   158 		silence = audio->spec.silence;
   159 		stream_len = audio->spec.size;
   160 	}
   161 	stream = audio->fake_stream;
   162 
   163 #ifdef ENABLE_AHI
   164 	SDL_mutexV(audio->mixer_lock);
   165 	D(bug("Entering audio loop...\n"));
   166 #endif
   167 
   168 
   169 	/* Loop, filling the audio buffers */
   170 	while ( audio->enabled ) {
   171 
   172 		/* Wait for new current buffer to finish playing */
   173 		if ( stream == audio->fake_stream ) {
   174 			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
   175 		} else {
   176 #ifdef ENABLE_AHI
   177 			if ( started > 1 )
   178 #endif
   179 			audio->WaitAudio(audio);
   180 		}
   181 
   182 		/* Fill the current buffer with sound */
   183 		if ( audio->convert.needed ) {
   184 			if ( audio->convert.buf ) {
   185 				stream = audio->convert.buf;
   186 			} else {
   187 				continue;
   188 			}
   189 		} else {
   190 			stream = audio->GetAudioBuf(audio);
   191 			if ( stream == NULL ) {
   192 				stream = audio->fake_stream;
   193 			}
   194 		}
   195 		memset(stream, silence, stream_len);
   196 
   197 		if ( ! audio->paused ) {
   198 			SDL_mutexP(audio->mixer_lock);
   199 			(*fill)(udata, stream, stream_len);
   200 			SDL_mutexV(audio->mixer_lock);
   201 		}
   202 
   203 		/* Convert the audio if necessary */
   204 		if ( audio->convert.needed ) {
   205 			SDL_ConvertAudio(&audio->convert);
   206 			stream = audio->GetAudioBuf(audio);
   207 			if ( stream == NULL ) {
   208 				stream = audio->fake_stream;
   209 			}
   210 			memcpy(stream, audio->convert.buf,
   211 			               audio->convert.len_cvt);
   212 		}
   213 
   214 		/* Ready current buffer for play and change current buffer */
   215 		if ( stream != audio->fake_stream ) {
   216 			audio->PlayAudio(audio);
   217 #ifdef ENABLE_AHI
   218 /* AmigaOS don't have to wait the first time audio is played! */
   219 			started++;
   220 #endif
   221 		}
   222 	}
   223 	/* Wait for the audio to drain.. */
   224 	if ( audio->WaitDone ) {
   225 		audio->WaitDone(audio);
   226 	}
   227 
   228 #ifdef ENABLE_AHI
   229 	D(bug("WaitAudio...Done\n"));
   230 
   231 	audio->CloseAudio(audio);
   232 
   233 	D(bug("CloseAudio..Done, subtask exiting...\n"));
   234 	audio_configured = 0;
   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 int SDL_AudioInit(const char *driver_name)
   256 {
   257 	SDL_AudioDevice *audio;
   258 	int i = 0, idx;
   259 
   260 	/* Check to make sure we don't overwrite 'current_audio' */
   261 	if ( current_audio != NULL ) {
   262 		SDL_AudioQuit();
   263 	}
   264 
   265 	/* Select the proper audio driver */
   266 	audio = NULL;
   267 	idx = 0;
   268 #ifdef unix
   269 	if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
   270 		/* Ahem, we know that if ESPEAKER is set, user probably wants
   271 		   to use ESD, but don't start it if it's not already running.
   272 		   This probably isn't the place to do this, but... Shh! :)
   273 		 */
   274 		for ( i=0; bootstrap[i]; ++i ) {
   275 			if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
   276 				const char *esd_no_spawn;
   277 
   278 				/* Don't start ESD if it's not running */
   279 				esd_no_spawn = getenv("ESD_NO_SPAWN");
   280 				if ( esd_no_spawn == NULL ) {
   281 					putenv("ESD_NO_SPAWN=1");
   282 				}
   283 				if ( bootstrap[i]->available() ) {
   284 					audio = bootstrap[i]->create(0);
   285 					break;
   286 				}
   287 #ifdef linux	/* No unsetenv() on most platforms */
   288 				if ( esd_no_spawn == NULL ) {
   289 					unsetenv("ESD_NO_SPAWN");
   290 				}
   291 #endif
   292 			}
   293 		}
   294 	}
   295 #endif /* unix */
   296 	if ( audio == NULL ) {
   297 		if ( driver_name != NULL ) {
   298 #if 0	/* This will be replaced with a better driver selection API */
   299 			if ( strrchr(driver_name, ':') != NULL ) {
   300 				idx = atoi(strrchr(driver_name, ':')+1);
   301 			}
   302 #endif
   303 			for ( i=0; bootstrap[i]; ++i ) {
   304 				if (strncmp(bootstrap[i]->name, driver_name,
   305 				            strlen(bootstrap[i]->name)) == 0) {
   306 					if ( bootstrap[i]->available() ) {
   307 						audio=bootstrap[i]->create(idx);
   308 						break;
   309 					}
   310 				}
   311 			}
   312 		} else {
   313 			for ( i=0; bootstrap[i]; ++i ) {
   314 				if ( bootstrap[i]->available() ) {
   315 					audio = bootstrap[i]->create(idx);
   316 					if ( audio != NULL ) {
   317 						break;
   318 					}
   319 				}
   320 			}
   321 		}
   322 		if ( audio == NULL ) {
   323 			SDL_SetError("No available audio device");
   324 #if 0 /* Don't fail SDL_Init() if audio isn't available.
   325          SDL_OpenAudio() will handle it at that point.  *sigh*
   326        */
   327 			return(-1);
   328 #endif
   329 		}
   330 	}
   331 	current_audio = audio;
   332 	if ( current_audio ) {
   333 		current_audio->name = bootstrap[i]->name;
   334 		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
   335 			current_audio->LockAudio = SDL_LockAudio_Default;
   336 			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
   337 		}
   338 	}
   339 	return(0);
   340 }
   341 
   342 char *SDL_AudioDriverName(char *namebuf, int maxlen)
   343 {
   344 	if ( current_audio != NULL ) {
   345 		strncpy(namebuf, current_audio->name, maxlen-1);
   346 		namebuf[maxlen-1] = '\0';
   347 		return(namebuf);
   348 	}
   349 	return(NULL);
   350 }
   351 
   352 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
   353 {
   354 	SDL_AudioDevice *audio;
   355 
   356 	/* Start up the audio driver, if necessary */
   357 	if ( ! current_audio ) {
   358 		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
   359 		     (current_audio == NULL) ) {
   360 			return(-1);
   361 		}
   362 	}
   363 	audio = current_audio;
   364 
   365 	if (audio->opened) {
   366 		SDL_SetError("Audio device is already opened");
   367 		return(-1);
   368 	}
   369 
   370 	/* Verify some parameters */
   371 	if ( desired->callback == NULL ) {
   372 		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   373 		return(-1);
   374 	}
   375 	switch ( desired->channels ) {
   376 	    case 1:	/* Mono */
   377 	    case 2:	/* Stereo */
   378 		break;
   379 	    default:
   380 		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
   381 		return(-1);
   382 	}
   383 
   384 #ifdef macintosh
   385 	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
   386 #else
   387 #if defined(__MINT__) && !defined(ENABLE_THREADS)
   388 	/* Uses interrupt driven audio, without thread */
   389 #else
   390 	/* Create a semaphore for locking the sound buffers */
   391 	audio->mixer_lock = SDL_CreateMutex();
   392 	if ( audio->mixer_lock == NULL ) {
   393 		SDL_SetError("Couldn't create mixer lock");
   394 		SDL_CloseAudio();
   395 		return(-1);
   396 	}
   397 #endif /* __MINT__ */
   398 #endif /* macintosh */
   399 
   400 	/* Calculate the silence and size of the audio specification */
   401 	SDL_CalculateAudioSpec(desired);
   402 
   403 	/* Open the audio subsystem */
   404 	memcpy(&audio->spec, desired, sizeof(audio->spec));
   405 	audio->convert.needed = 0;
   406 	audio->enabled = 1;
   407 	audio->paused  = 1;
   408 
   409 #ifndef ENABLE_AHI
   410 
   411 /* AmigaOS opens audio inside the main loop */
   412 	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
   413 
   414 	if ( ! audio->opened ) {
   415 		SDL_CloseAudio();
   416 		return(-1);
   417 	}
   418 #else
   419 	D(bug("Locking semaphore..."));
   420 	SDL_mutexP(audio->mixer_lock);
   421 
   422 	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   423 	D(bug("Created thread...\n"));
   424 
   425 	if ( audio->thread == NULL ) {
   426 		SDL_mutexV(audio->mixer_lock);
   427 		SDL_CloseAudio();
   428 		SDL_SetError("Couldn't create audio thread");
   429 		return(-1);
   430 	}
   431 
   432 	while(!audio_configured)
   433 		SDL_Delay(100);
   434 #endif
   435 
   436 	/* If the audio driver changes the buffer size, accept it */
   437 	if ( audio->spec.samples != desired->samples ) {
   438 		desired->samples = audio->spec.samples;
   439 		SDL_CalculateAudioSpec(desired);
   440 	}
   441 
   442 	/* Allocate a fake audio memory buffer */
   443 	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
   444 	if ( audio->fake_stream == NULL ) {
   445 		SDL_CloseAudio();
   446 		SDL_OutOfMemory();
   447 		return(-1);
   448 	}
   449 
   450 	/* See if we need to do any conversion */
   451 	if ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
   452 		/* Just copy over the desired audio specification */
   453 		if ( obtained != NULL ) {
   454 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   455 		}
   456 	} else {
   457 		/* Copy over the audio specification if possible */
   458 		if ( obtained != NULL ) {
   459 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   460 		} else {
   461 			/* Build an audio conversion block */
   462 			if ( SDL_BuildAudioCVT(&audio->convert,
   463 				desired->format, desired->channels,
   464 						desired->freq,
   465 				audio->spec.format, audio->spec.channels,
   466 						audio->spec.freq) < 0 ) {
   467 				SDL_CloseAudio();
   468 				return(-1);
   469 			}
   470 			if ( audio->convert.needed ) {
   471 				audio->convert.len = desired->size;
   472 				audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   473 				   audio->convert.len*audio->convert.len_mult);
   474 				if ( audio->convert.buf == NULL ) {
   475 					SDL_CloseAudio();
   476 					SDL_OutOfMemory();
   477 					return(-1);
   478 				}
   479 			}
   480 		}
   481 	}
   482 
   483 #ifndef ENABLE_AHI
   484 	/* Start the audio thread if necessary */
   485 	switch (audio->opened) {
   486 		case  1:
   487 			/* Start the audio thread */
   488 			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   489 			if ( audio->thread == NULL ) {
   490 				SDL_CloseAudio();
   491 				SDL_SetError("Couldn't create audio thread");
   492 				return(-1);
   493 			}
   494 			break;
   495 
   496 		default:
   497 			/* The audio is now playing */
   498 			break;
   499 	}
   500 #else
   501 	SDL_mutexV(audio->mixer_lock);
   502 	D(bug("SDL_OpenAudio USCITA...\n"));
   503 
   504 #endif
   505 
   506 	return(0);
   507 }
   508 
   509 SDL_audiostatus SDL_GetAudioStatus(void)
   510 {
   511 	SDL_AudioDevice *audio = current_audio;
   512 	SDL_audiostatus status;
   513 
   514 	status = SDL_AUDIO_STOPPED;
   515 	if ( audio && audio->enabled ) {
   516 		if ( audio->paused ) {
   517 			status = SDL_AUDIO_PAUSED;
   518 		} else {
   519 			status = SDL_AUDIO_PLAYING;
   520 		}
   521 	}
   522 	return(status);
   523 }
   524 
   525 void SDL_PauseAudio (int pause_on)
   526 {
   527 	SDL_AudioDevice *audio = current_audio;
   528 
   529 	if ( audio ) {
   530 		audio->paused = pause_on;
   531 	}
   532 }
   533 
   534 void SDL_LockAudio (void)
   535 {
   536 	SDL_AudioDevice *audio = current_audio;
   537 
   538 	/* Obtain a lock on the mixing buffers */
   539 	if ( audio && audio->LockAudio ) {
   540 		audio->LockAudio(audio);
   541 	}
   542 }
   543 
   544 void SDL_UnlockAudio (void)
   545 {
   546 	SDL_AudioDevice *audio = current_audio;
   547 
   548 	/* Release lock on the mixing buffers */
   549 	if ( audio && audio->UnlockAudio ) {
   550 		audio->UnlockAudio(audio);
   551 	}
   552 }
   553 
   554 void SDL_CloseAudio (void)
   555 {
   556 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
   557 }
   558 
   559 void SDL_AudioQuit(void)
   560 {
   561 	SDL_AudioDevice *audio = current_audio;
   562 
   563 	if ( audio ) {
   564 		audio->enabled = 0;
   565 		if ( audio->thread != NULL ) {
   566 			SDL_WaitThread(audio->thread, NULL);
   567 		}
   568 		if ( audio->mixer_lock != NULL ) {
   569 			SDL_DestroyMutex(audio->mixer_lock);
   570 		}
   571 		if ( audio->fake_stream != NULL ) {
   572 			SDL_FreeAudioMem(audio->fake_stream);
   573 		}
   574 		if ( audio->convert.needed ) {
   575 			SDL_FreeAudioMem(audio->convert.buf);
   576 
   577 		}
   578 #ifndef ENABLE_AHI
   579 		if ( audio->opened ) {
   580 			audio->CloseAudio(audio);
   581 			audio->opened = 0;
   582 		}
   583 #endif
   584 		/* Free the driver data */
   585 		audio->free(audio);
   586 		current_audio = NULL;
   587 	}
   588 }
   589 
   590 #define NUM_FORMATS	6
   591 static int format_idx;
   592 static int format_idx_sub;
   593 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
   594  { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   595  { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   596  { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
   597  { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
   598  { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
   599  { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
   600 };
   601 
   602 Uint16 SDL_FirstAudioFormat(Uint16 format)
   603 {
   604 	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
   605 		if ( format_list[format_idx][0] == format ) {
   606 			break;
   607 		}
   608 	}
   609 	format_idx_sub = 0;
   610 	return(SDL_NextAudioFormat());
   611 }
   612 
   613 Uint16 SDL_NextAudioFormat(void)
   614 {
   615 	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
   616 		return(0);
   617 	}
   618 	return(format_list[format_idx][format_idx_sub++]);
   619 }
   620 
   621 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
   622 {
   623 	switch (spec->format) {
   624 		case AUDIO_U8:
   625 			spec->silence = 0x80;
   626 			break;
   627 		default:
   628 			spec->silence = 0x00;
   629 			break;
   630 	}
   631 	spec->size = (spec->format&0xFF)/8;
   632 	spec->size *= spec->channels;
   633 	spec->size *= spec->samples;
   634 }