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