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