src/audio/SDL_audio.c
author Sam Lantinga
Sat, 30 Mar 2002 19:48:56 +0000
changeset 322 fd93a09655e3
parent 297 f6ffac90895c
child 398 d219b0e02f5f
permissions -rw-r--r--
The audio lock and unlock functions are now a part of the driver.
The MacOS audio locking has been implemented, courtesy of Ryan Gordon
     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 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 static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
   234 {
   235 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   236 		return;
   237 	}
   238 	SDL_mutexP(audio->mixer_lock);
   239 }
   240 
   241 static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
   242 {
   243 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   244 		return;
   245 	}
   246 	SDL_mutexV(audio->mixer_lock);
   247 }
   248 
   249 int SDL_AudioInit(const char *driver_name)
   250 {
   251 	SDL_AudioDevice *audio;
   252 	int i = 0, idx;
   253 
   254 	/* Check to make sure we don't overwrite 'current_audio' */
   255 	if ( current_audio != NULL ) {
   256 		SDL_AudioQuit();
   257 	}
   258 
   259 	/* Select the proper audio driver */
   260 	audio = NULL;
   261 	idx = 0;
   262 #ifdef unix
   263 	if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
   264 		/* Ahem, we know that if ESPEAKER is set, user probably wants
   265 		   to use ESD, but don't start it if it's not already running.
   266 		   This probably isn't the place to do this, but... Shh! :)
   267 		 */
   268 		for ( i=0; bootstrap[i]; ++i ) {
   269 			if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
   270 				const char *esd_no_spawn;
   271 
   272 				/* Don't start ESD if it's not running */
   273 				esd_no_spawn = getenv("ESD_NO_SPAWN");
   274 				if ( esd_no_spawn == NULL ) {
   275 					putenv("ESD_NO_SPAWN=1");
   276 				}
   277 				if ( bootstrap[i]->available() ) {
   278 					audio = bootstrap[i]->create(0);
   279 					break;
   280 				}
   281 #ifdef linux	/* No unsetenv() on most platforms */
   282 				if ( esd_no_spawn == NULL ) {
   283 					unsetenv("ESD_NO_SPAWN");
   284 				}
   285 #endif
   286 			}
   287 		}
   288 	}
   289 #endif /* unix */
   290 	if ( audio == NULL ) {
   291 		if ( driver_name != NULL ) {
   292 #if 0	/* This will be replaced with a better driver selection API */
   293 			if ( strrchr(driver_name, ':') != NULL ) {
   294 				idx = atoi(strrchr(driver_name, ':')+1);
   295 			}
   296 #endif
   297 			for ( i=0; bootstrap[i]; ++i ) {
   298 				if (strncmp(bootstrap[i]->name, driver_name,
   299 				            strlen(bootstrap[i]->name)) == 0) {
   300 					if ( bootstrap[i]->available() ) {
   301 						audio=bootstrap[i]->create(idx);
   302 						break;
   303 					}
   304 				}
   305 			}
   306 		} else {
   307 			for ( i=0; bootstrap[i]; ++i ) {
   308 				if ( bootstrap[i]->available() ) {
   309 					audio = bootstrap[i]->create(idx);
   310 					if ( audio != NULL ) {
   311 						break;
   312 					}
   313 				}
   314 			}
   315 		}
   316 		if ( audio == NULL ) {
   317 			SDL_SetError("No available audio device");
   318 #if 0 /* Don't fail SDL_Init() if audio isn't available.
   319          SDL_OpenAudio() will handle it at that point.  *sigh*
   320        */
   321 			return(-1);
   322 #endif
   323 		}
   324 	}
   325 	current_audio = audio;
   326 	if ( current_audio ) {
   327 		current_audio->name = bootstrap[i]->name;
   328 		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
   329 			current_audio->LockAudio = SDL_LockAudio_Default;
   330 			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
   331 		}
   332 	}
   333 	return(0);
   334 }
   335 
   336 char *SDL_AudioDriverName(char *namebuf, int maxlen)
   337 {
   338 	if ( current_audio != NULL ) {
   339 		strncpy(namebuf, current_audio->name, maxlen-1);
   340 		namebuf[maxlen-1] = '\0';
   341 		return(namebuf);
   342 	}
   343 	return(NULL);
   344 }
   345 
   346 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
   347 {
   348 	SDL_AudioDevice *audio;
   349 
   350 	/* Start up the audio driver, if necessary */
   351 	if ( ! current_audio ) {
   352 		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
   353 		     (current_audio == NULL) ) {
   354 			return(-1);
   355 		}
   356 	}
   357 	audio = current_audio;
   358 
   359 	if (audio->opened) {
   360 		SDL_SetError("Audio device is already opened");
   361 		return(-1);
   362 	}
   363 
   364 	/* Verify some parameters */
   365 	if ( desired->callback == NULL ) {
   366 		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   367 		return(-1);
   368 	}
   369 	switch ( desired->channels ) {
   370 	    case 1:	/* Mono */
   371 	    case 2:	/* Stereo */
   372 		break;
   373 	    default:
   374 		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
   375 		return(-1);
   376 	}
   377 
   378 #ifdef macintosh
   379 	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
   380 #else
   381 	/* Create a semaphore for locking the sound buffers */
   382 	audio->mixer_lock = SDL_CreateMutex();
   383 	if ( audio->mixer_lock == NULL ) {
   384 		SDL_SetError("Couldn't create mixer lock");
   385 		SDL_CloseAudio();
   386 		return(-1);
   387 	}
   388 #endif
   389 
   390 	/* Calculate the silence and size of the audio specification */
   391 	SDL_CalculateAudioSpec(desired);
   392 
   393 	/* Open the audio subsystem */
   394 	memcpy(&audio->spec, desired, sizeof(audio->spec));
   395 	audio->convert.needed = 0;
   396 	audio->enabled = 1;
   397 	audio->paused  = 1;
   398 
   399 #ifndef ENABLE_AHI
   400 
   401 /* AmigaOS opens audio inside the main loop */
   402 	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
   403 
   404 	if ( ! audio->opened ) {
   405 		SDL_CloseAudio();
   406 		return(-1);
   407 	}
   408 #else
   409 	D(bug("Locking semaphore..."));
   410 	SDL_mutexP(audio->mixer_lock);
   411 
   412 	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   413 	D(bug("Created thread...\n"));
   414 
   415 	if ( audio->thread == NULL ) {
   416 		SDL_mutexV(audio->mixer_lock);
   417 		SDL_CloseAudio();
   418 		SDL_SetError("Couldn't create audio thread");
   419 		return(-1);
   420 	}
   421 
   422 	while(!audio_configured)
   423 		SDL_Delay(100);
   424 #endif
   425 
   426 	/* If the audio driver changes the buffer size, accept it */
   427 	if ( audio->spec.samples != desired->samples ) {
   428 		desired->samples = audio->spec.samples;
   429 		SDL_CalculateAudioSpec(desired);
   430 	}
   431 
   432 	/* Allocate a fake audio memory buffer */
   433 	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
   434 	if ( audio->fake_stream == NULL ) {
   435 		SDL_CloseAudio();
   436 		SDL_OutOfMemory();
   437 		return(-1);
   438 	}
   439 
   440 	/* See if we need to do any conversion */
   441 	if ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
   442 		/* Just copy over the desired audio specification */
   443 		if ( obtained != NULL ) {
   444 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   445 		}
   446 	} else {
   447 		/* Copy over the audio specification if possible */
   448 		if ( obtained != NULL ) {
   449 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   450 		} else {
   451 			/* Build an audio conversion block */
   452 			if ( SDL_BuildAudioCVT(&audio->convert,
   453 				desired->format, desired->channels,
   454 						desired->freq,
   455 				audio->spec.format, audio->spec.channels,
   456 						audio->spec.freq) < 0 ) {
   457 				SDL_CloseAudio();
   458 				return(-1);
   459 			}
   460 			if ( audio->convert.needed ) {
   461 				audio->convert.len = desired->size;
   462 				audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   463 				   audio->convert.len*audio->convert.len_mult);
   464 				if ( audio->convert.buf == NULL ) {
   465 					SDL_CloseAudio();
   466 					SDL_OutOfMemory();
   467 					return(-1);
   468 				}
   469 			}
   470 		}
   471 	}
   472 
   473 #ifndef ENABLE_AHI
   474 	/* Start the audio thread if necessary */
   475 	switch (audio->opened) {
   476 		case  1:
   477 			/* Start the audio thread */
   478 			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   479 			if ( audio->thread == NULL ) {
   480 				SDL_CloseAudio();
   481 				SDL_SetError("Couldn't create audio thread");
   482 				return(-1);
   483 			}
   484 			break;
   485 
   486 		default:
   487 			/* The audio is now playing */
   488 			break;
   489 	}
   490 #else
   491 	SDL_mutexV(audio->mixer_lock);
   492 	D(bug("SDL_OpenAudio USCITA...\n"));
   493 
   494 #endif
   495 
   496 	return(0);
   497 }
   498 
   499 SDL_audiostatus SDL_GetAudioStatus(void)
   500 {
   501 	SDL_AudioDevice *audio = current_audio;
   502 	SDL_audiostatus status;
   503 
   504 	status = SDL_AUDIO_STOPPED;
   505 	if ( audio && audio->enabled ) {
   506 		if ( audio->paused ) {
   507 			status = SDL_AUDIO_PAUSED;
   508 		} else {
   509 			status = SDL_AUDIO_PLAYING;
   510 		}
   511 	}
   512 	return(status);
   513 }
   514 
   515 void SDL_PauseAudio (int pause_on)
   516 {
   517 	SDL_AudioDevice *audio = current_audio;
   518 
   519 	if ( audio ) {
   520 		audio->paused = pause_on;
   521 	}
   522 }
   523 
   524 void SDL_LockAudio (void)
   525 {
   526 	SDL_AudioDevice *audio = current_audio;
   527 
   528 	/* Obtain a lock on the mixing buffers */
   529 	if ( audio && audio->LockAudio ) {
   530 		audio->LockAudio(audio);
   531 	}
   532 }
   533 
   534 void SDL_UnlockAudio (void)
   535 {
   536 	SDL_AudioDevice *audio = current_audio;
   537 
   538 	/* Release lock on the mixing buffers */
   539 	if ( audio && audio->UnlockAudio ) {
   540 		audio->UnlockAudio(audio);
   541 	}
   542 }
   543 
   544 void SDL_CloseAudio (void)
   545 {
   546 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
   547 }
   548 
   549 void SDL_AudioQuit(void)
   550 {
   551 	SDL_AudioDevice *audio = current_audio;
   552 
   553 	if ( audio ) {
   554 		audio->enabled = 0;
   555 		if ( audio->thread != NULL ) {
   556 			SDL_WaitThread(audio->thread, NULL);
   557 		}
   558 		if ( audio->mixer_lock != NULL ) {
   559 			SDL_DestroyMutex(audio->mixer_lock);
   560 		}
   561 		if ( audio->fake_stream != NULL ) {
   562 			SDL_FreeAudioMem(audio->fake_stream);
   563 		}
   564 		if ( audio->convert.needed ) {
   565 			SDL_FreeAudioMem(audio->convert.buf);
   566 
   567 		}
   568 #ifndef ENABLE_AHI
   569 		if ( audio->opened ) {
   570 			audio->CloseAudio(audio);
   571 			audio->opened = 0;
   572 		}
   573 #endif
   574 		/* Free the driver data */
   575 		audio->free(audio);
   576 		current_audio = NULL;
   577 	}
   578 }
   579 
   580 #define NUM_FORMATS	6
   581 static int format_idx;
   582 static int format_idx_sub;
   583 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
   584  { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   585  { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   586  { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
   587  { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
   588  { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
   589  { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
   590 };
   591 
   592 Uint16 SDL_FirstAudioFormat(Uint16 format)
   593 {
   594 	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
   595 		if ( format_list[format_idx][0] == format ) {
   596 			break;
   597 		}
   598 	}
   599 	format_idx_sub = 0;
   600 	return(SDL_NextAudioFormat());
   601 }
   602 
   603 Uint16 SDL_NextAudioFormat(void)
   604 {
   605 	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
   606 		return(0);
   607 	}
   608 	return(format_list[format_idx][format_idx_sub++]);
   609 }
   610 
   611 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
   612 {
   613 	switch (spec->format) {
   614 		case AUDIO_U8:
   615 			spec->silence = 0x80;
   616 			break;
   617 		default:
   618 			spec->silence = 0x00;
   619 			break;
   620 	}
   621 	spec->size = (spec->format&0xFF)/8;
   622 	spec->size *= spec->channels;
   623 	spec->size *= spec->samples;
   624 }