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