src/audio/SDL_audio.c
author Sam Lantinga
Thu, 26 Apr 2001 16:45:43 +0000
changeset 0 74212992fb08
child 21 75a95f82bc1f
permissions -rw-r--r--
Initial revision
     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 }