src/audio/SDL_audio.c
author Sam Lantinga
Wed, 09 Jan 2002 16:04:58 +0000
changeset 262 ba5363e21df8
parent 252 e8157fcb3114
child 297 f6ffac90895c
permissions -rw-r--r--
Don't allow multiple audio opens to succeed (until SDL 1.3)
     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 	if (audio->opened) {
   340 		SDL_SetError("Audio device is already opened");
   341 		return(-1);
   342 	}
   343 
   344 	/* Verify some parameters */
   345 	if ( desired->callback == NULL ) {
   346 		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   347 		return(-1);
   348 	}
   349 	switch ( desired->channels ) {
   350 	    case 1:	/* Mono */
   351 	    case 2:	/* Stereo */
   352 		break;
   353 	    default:
   354 		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
   355 		return(-1);
   356 	}
   357 
   358 #ifdef macintosh
   359 	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
   360 #else
   361 	/* Create a semaphore for locking the sound buffers */
   362 	audio->mixer_lock = SDL_CreateMutex();
   363 	if ( audio->mixer_lock == NULL ) {
   364 		SDL_SetError("Couldn't create mixer lock");
   365 		SDL_CloseAudio();
   366 		return(-1);
   367 	}
   368 #endif
   369 
   370 	/* Calculate the silence and size of the audio specification */
   371 	SDL_CalculateAudioSpec(desired);
   372 
   373 	/* Open the audio subsystem */
   374 	memcpy(&audio->spec, desired, sizeof(audio->spec));
   375 	audio->convert.needed = 0;
   376 	audio->enabled = 1;
   377 	audio->paused  = 1;
   378 
   379 #ifndef ENABLE_AHI
   380 
   381 /* AmigaOS opens audio inside the main loop */
   382 	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
   383 
   384 	if ( ! audio->opened ) {
   385 		SDL_CloseAudio();
   386 		return(-1);
   387 	}
   388 #else
   389 	D(bug("Locking semaphore..."));
   390 	SDL_mutexP(audio->mixer_lock);
   391 
   392 	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   393 	D(bug("Created thread...\n"));
   394 
   395 	if ( audio->thread == NULL ) {
   396 		SDL_mutexV(audio->mixer_lock);
   397 		SDL_CloseAudio();
   398 		SDL_SetError("Couldn't create audio thread");
   399 		return(-1);
   400 	}
   401 
   402 	while(!audio_configured)
   403 		SDL_Delay(100);
   404 #endif
   405 
   406 	/* If the audio driver changes the buffer size, accept it */
   407 	if ( audio->spec.samples != desired->samples ) {
   408 		desired->samples = audio->spec.samples;
   409 		SDL_CalculateAudioSpec(desired);
   410 	}
   411 
   412 	/* Allocate a fake audio memory buffer */
   413 	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
   414 	if ( audio->fake_stream == NULL ) {
   415 		SDL_CloseAudio();
   416 		SDL_OutOfMemory();
   417 		return(-1);
   418 	}
   419 
   420 	/* See if we need to do any conversion */
   421 	if ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
   422 		/* Just copy over the desired audio specification */
   423 		if ( obtained != NULL ) {
   424 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   425 		}
   426 	} else {
   427 		/* Copy over the audio specification if possible */
   428 		if ( obtained != NULL ) {
   429 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   430 		} else {
   431 			/* Build an audio conversion block */
   432 			if ( SDL_BuildAudioCVT(&audio->convert,
   433 				desired->format, desired->channels,
   434 						desired->freq,
   435 				audio->spec.format, audio->spec.channels,
   436 						audio->spec.freq) < 0 ) {
   437 				SDL_CloseAudio();
   438 				return(-1);
   439 			}
   440 			if ( audio->convert.needed ) {
   441 				audio->convert.len = desired->size;
   442 				audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   443 				   audio->convert.len*audio->convert.len_mult);
   444 				if ( audio->convert.buf == NULL ) {
   445 					SDL_CloseAudio();
   446 					SDL_OutOfMemory();
   447 					return(-1);
   448 				}
   449 			}
   450 		}
   451 	}
   452 
   453 #ifndef ENABLE_AHI
   454 	/* Start the audio thread if necessary */
   455 	switch (audio->opened) {
   456 		case  1:
   457 			/* Start the audio thread */
   458 			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   459 			if ( audio->thread == NULL ) {
   460 				SDL_CloseAudio();
   461 				SDL_SetError("Couldn't create audio thread");
   462 				return(-1);
   463 			}
   464 			break;
   465 
   466 		default:
   467 			/* The audio is now playing */
   468 			break;
   469 	}
   470 #else
   471 	SDL_mutexV(audio->mixer_lock);
   472 	D(bug("SDL_OpenAudio USCITA...\n"));
   473 
   474 #endif
   475 
   476 	return(0);
   477 }
   478 
   479 SDL_audiostatus SDL_GetAudioStatus(void)
   480 {
   481 	SDL_AudioDevice *audio = current_audio;
   482 	SDL_audiostatus status;
   483 
   484 	status = SDL_AUDIO_STOPPED;
   485 	if ( audio && audio->enabled ) {
   486 		if ( audio->paused ) {
   487 			status = SDL_AUDIO_PAUSED;
   488 		} else {
   489 			status = SDL_AUDIO_PLAYING;
   490 		}
   491 	}
   492 	return(status);
   493 }
   494 
   495 void SDL_PauseAudio (int pause_on)
   496 {
   497 	SDL_AudioDevice *audio = current_audio;
   498 
   499 	if ( audio ) {
   500 		audio->paused = pause_on;
   501 	}
   502 }
   503 
   504 void SDL_LockAudio (void)
   505 {
   506 	SDL_AudioDevice *audio = current_audio;
   507 
   508 	/* Obtain a lock on the mixing buffers */
   509 	if ( audio ) {
   510 		if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   511 			return;
   512 		}
   513 		SDL_mutexP(audio->mixer_lock);
   514 	}
   515 }
   516 
   517 void SDL_UnlockAudio (void)
   518 {
   519 	SDL_AudioDevice *audio = current_audio;
   520 
   521 	/* Release lock on the mixing buffers */
   522 	if ( audio ) {
   523 		if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   524 			return;
   525 		}
   526 		SDL_mutexV(audio->mixer_lock);
   527 	}
   528 }
   529 
   530 void SDL_CloseAudio (void)
   531 {
   532 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
   533 }
   534 
   535 void SDL_AudioQuit(void)
   536 {
   537 	SDL_AudioDevice *audio = current_audio;
   538 
   539 	if ( audio ) {
   540 		audio->enabled = 0;
   541 		if ( audio->thread != NULL ) {
   542 			SDL_WaitThread(audio->thread, NULL);
   543 		}
   544 		if ( audio->mixer_lock != NULL ) {
   545 			SDL_DestroyMutex(audio->mixer_lock);
   546 		}
   547 		if ( audio->fake_stream != NULL ) {
   548 			SDL_FreeAudioMem(audio->fake_stream);
   549 		}
   550 		if ( audio->convert.needed ) {
   551 			SDL_FreeAudioMem(audio->convert.buf);
   552 
   553 		}
   554 #ifndef ENABLE_AHI
   555 		if ( audio->opened ) {
   556 			audio->CloseAudio(audio);
   557 			audio->opened = 0;
   558 		}
   559 #endif
   560 		/* Free the driver data */
   561 		audio->free(audio);
   562 		current_audio = NULL;
   563 	}
   564 }
   565 
   566 #define NUM_FORMATS	6
   567 static int format_idx;
   568 static int format_idx_sub;
   569 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
   570  { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   571  { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   572  { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
   573  { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
   574  { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
   575  { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
   576 };
   577 
   578 Uint16 SDL_FirstAudioFormat(Uint16 format)
   579 {
   580 	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
   581 		if ( format_list[format_idx][0] == format ) {
   582 			break;
   583 		}
   584 	}
   585 	format_idx_sub = 0;
   586 	return(SDL_NextAudioFormat());
   587 }
   588 
   589 Uint16 SDL_NextAudioFormat(void)
   590 {
   591 	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
   592 		return(0);
   593 	}
   594 	return(format_list[format_idx][format_idx_sub++]);
   595 }
   596 
   597 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
   598 {
   599 	switch (spec->format) {
   600 		case AUDIO_U8:
   601 			spec->silence = 0x80;
   602 			break;
   603 		default:
   604 			spec->silence = 0x00;
   605 			break;
   606 	}
   607 	spec->size = (spec->format&0xFF)/8;
   608 	spec->size *= spec->channels;
   609 	spec->size *= spec->samples;
   610 }