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