src/audio/SDL_audio.c
author Sam Lantinga
Sun, 04 Jan 2004 16:49:27 +0000
changeset 769 b8d311d90021
parent 663 8bedd6d61642
child 808 0defd90ef27c
permissions -rw-r--r--
Updated copyright information for 2004 (Happy New Year!)
     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 ( memcmp(desired, &audio->spec, sizeof(audio->spec)) == 0 ) {
   465 		/* Just copy over the desired audio specification */
   466 		if ( obtained != NULL ) {
   467 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   468 		}
   469 	} else {
   470 		/* Copy over the audio specification if possible */
   471 		if ( obtained != NULL ) {
   472 			memcpy(obtained, &audio->spec, sizeof(audio->spec));
   473 		} else {
   474 			/* Build an audio conversion block */
   475 			if ( SDL_BuildAudioCVT(&audio->convert,
   476 				desired->format, desired->channels,
   477 						desired->freq,
   478 				audio->spec.format, audio->spec.channels,
   479 						audio->spec.freq) < 0 ) {
   480 				SDL_CloseAudio();
   481 				return(-1);
   482 			}
   483 			if ( audio->convert.needed ) {
   484 				audio->convert.len = desired->size;
   485 				audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   486 				   audio->convert.len*audio->convert.len_mult);
   487 				if ( audio->convert.buf == NULL ) {
   488 					SDL_CloseAudio();
   489 					SDL_OutOfMemory();
   490 					return(-1);
   491 				}
   492 			}
   493 		}
   494 	}
   495 
   496 #ifndef ENABLE_AHI
   497 	/* Start the audio thread if necessary */
   498 	switch (audio->opened) {
   499 		case  1:
   500 			/* Start the audio thread */
   501 			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   502 			if ( audio->thread == NULL ) {
   503 				SDL_CloseAudio();
   504 				SDL_SetError("Couldn't create audio thread");
   505 				return(-1);
   506 			}
   507 			break;
   508 
   509 		default:
   510 			/* The audio is now playing */
   511 			break;
   512 	}
   513 #else
   514 	SDL_mutexV(audio->mixer_lock);
   515 	D(bug("SDL_OpenAudio USCITA...\n"));
   516 
   517 #endif
   518 
   519 	return(0);
   520 }
   521 
   522 SDL_audiostatus SDL_GetAudioStatus(void)
   523 {
   524 	SDL_AudioDevice *audio = current_audio;
   525 	SDL_audiostatus status;
   526 
   527 	status = SDL_AUDIO_STOPPED;
   528 	if ( audio && audio->enabled ) {
   529 		if ( audio->paused ) {
   530 			status = SDL_AUDIO_PAUSED;
   531 		} else {
   532 			status = SDL_AUDIO_PLAYING;
   533 		}
   534 	}
   535 	return(status);
   536 }
   537 
   538 void SDL_PauseAudio (int pause_on)
   539 {
   540 	SDL_AudioDevice *audio = current_audio;
   541 
   542 	if ( audio ) {
   543 		audio->paused = pause_on;
   544 	}
   545 }
   546 
   547 void SDL_LockAudio (void)
   548 {
   549 	SDL_AudioDevice *audio = current_audio;
   550 
   551 	/* Obtain a lock on the mixing buffers */
   552 	if ( audio && audio->LockAudio ) {
   553 		audio->LockAudio(audio);
   554 	}
   555 }
   556 
   557 void SDL_UnlockAudio (void)
   558 {
   559 	SDL_AudioDevice *audio = current_audio;
   560 
   561 	/* Release lock on the mixing buffers */
   562 	if ( audio && audio->UnlockAudio ) {
   563 		audio->UnlockAudio(audio);
   564 	}
   565 }
   566 
   567 void SDL_CloseAudio (void)
   568 {
   569 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
   570 }
   571 
   572 void SDL_AudioQuit(void)
   573 {
   574 	SDL_AudioDevice *audio = current_audio;
   575 
   576 	if ( audio ) {
   577 		audio->enabled = 0;
   578 		if ( audio->thread != NULL ) {
   579 			SDL_WaitThread(audio->thread, NULL);
   580 		}
   581 		if ( audio->mixer_lock != NULL ) {
   582 			SDL_DestroyMutex(audio->mixer_lock);
   583 		}
   584 		if ( audio->fake_stream != NULL ) {
   585 			SDL_FreeAudioMem(audio->fake_stream);
   586 		}
   587 		if ( audio->convert.needed ) {
   588 			SDL_FreeAudioMem(audio->convert.buf);
   589 
   590 		}
   591 #ifndef ENABLE_AHI
   592 		if ( audio->opened ) {
   593 			audio->CloseAudio(audio);
   594 			audio->opened = 0;
   595 		}
   596 #endif
   597 		/* Free the driver data */
   598 		audio->free(audio);
   599 		current_audio = NULL;
   600 	}
   601 }
   602 
   603 #define NUM_FORMATS	6
   604 static int format_idx;
   605 static int format_idx_sub;
   606 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
   607  { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   608  { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   609  { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
   610  { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
   611  { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
   612  { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
   613 };
   614 
   615 Uint16 SDL_FirstAudioFormat(Uint16 format)
   616 {
   617 	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
   618 		if ( format_list[format_idx][0] == format ) {
   619 			break;
   620 		}
   621 	}
   622 	format_idx_sub = 0;
   623 	return(SDL_NextAudioFormat());
   624 }
   625 
   626 Uint16 SDL_NextAudioFormat(void)
   627 {
   628 	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
   629 		return(0);
   630 	}
   631 	return(format_list[format_idx][format_idx_sub++]);
   632 }
   633 
   634 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
   635 {
   636 	switch (spec->format) {
   637 		case AUDIO_U8:
   638 			spec->silence = 0x80;
   639 			break;
   640 		default:
   641 			spec->silence = 0x00;
   642 			break;
   643 	}
   644 	spec->size = (spec->format&0xFF)/8;
   645 	spec->size *= spec->channels;
   646 	spec->size *= spec->samples;
   647 }