src/audio/SDL_audio.c
author Sam Lantinga
Sat, 21 Aug 2004 03:21:44 +0000
changeset 936 84f930aebaeb
parent 935 f8d5ddc7aef1
child 942 41a59de7f2ed
permissions -rw-r--r--
CoreAudio driver works on Mac OSX 10.1
     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 #ifdef MACOSX
    81 	&COREAUDIO_bootstrap,
    82 #endif
    83 #if defined(macintosh) || TARGET_API_MAC_CARBON
    84 	&SNDMGR_bootstrap,
    85 #endif
    86 #ifdef _AIX
    87 	&Paud_bootstrap,
    88 #endif
    89 #ifdef ENABLE_AHI
    90 	&AHI_bootstrap,
    91 #endif
    92 #ifdef MMEAUDIO_SUPPORT
    93 	&MMEAUDIO_bootstrap,
    94 #endif
    95 #ifdef MINTAUDIO_SUPPORT
    96 	&MINTAUDIO_GSXB_bootstrap,
    97 	&MINTAUDIO_MCSN_bootstrap,
    98 	&MINTAUDIO_STFA_bootstrap,
    99 	&MINTAUDIO_XBIOS_bootstrap,
   100 	&MINTAUDIO_DMA8_bootstrap,
   101 #endif
   102 #ifdef DISKAUD_SUPPORT
   103 	&DISKAUD_bootstrap,
   104 #endif
   105 #ifdef ENABLE_DC
   106 	&DCAUD_bootstrap,
   107 #endif
   108 #ifdef DRENDERER_SUPPORT
   109 	&DRENDERER_bootstrap,
   110 #endif
   111 	NULL
   112 };
   113 SDL_AudioDevice *current_audio = NULL;
   114 
   115 /* Various local functions */
   116 int SDL_AudioInit(const char *driver_name);
   117 void SDL_AudioQuit(void);
   118 
   119 #ifdef ENABLE_AHI
   120 static int audio_configured = 0;
   121 #endif
   122 
   123 /* The general mixing thread function */
   124 int SDL_RunAudio(void *audiop)
   125 {
   126 	SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
   127 	Uint8 *stream;
   128 	int    stream_len;
   129 	void  *udata;
   130 	void (*fill)(void *userdata,Uint8 *stream, int len);
   131 	int    silence;
   132 #ifdef ENABLE_AHI
   133 	int started = 0;
   134 
   135 /* AmigaOS NEEDS that the audio driver is opened in the thread that uses it! */
   136 
   137 	D(bug("Task audio started audio struct:<%lx>...\n",audiop));
   138 
   139 	D(bug("Before Openaudio..."));
   140 	if(audio->OpenAudio(audio, &audio->spec)==-1)
   141 	{
   142 		D(bug("Open audio failed...\n"));
   143 		return(-1);
   144 	}
   145 	D(bug("OpenAudio...OK\n"));
   146 #endif
   147 
   148 	/* Perform any thread setup */
   149 	if ( audio->ThreadInit ) {
   150 		audio->ThreadInit(audio);
   151 	}
   152 	audio->threadid = SDL_ThreadID();
   153 
   154 	/* Set up the mixing function */
   155 	fill  = audio->spec.callback;
   156 	udata = audio->spec.userdata;
   157 
   158 #ifdef ENABLE_AHI
   159 	audio_configured = 1;
   160 
   161 	D(bug("Audio configured... Checking for conversion\n"));
   162 	SDL_mutexP(audio->mixer_lock);
   163 	D(bug("Semaphore obtained...\n"));
   164 #endif
   165 
   166 	if ( audio->convert.needed ) {
   167 		if ( audio->convert.src_format == AUDIO_U8 ) {
   168 			silence = 0x80;
   169 		} else {
   170 			silence = 0;
   171 		}
   172 		stream_len = audio->convert.len;
   173 	} else {
   174 		silence = audio->spec.silence;
   175 		stream_len = audio->spec.size;
   176 	}
   177 	stream = audio->fake_stream;
   178 
   179 #ifdef ENABLE_AHI
   180 	SDL_mutexV(audio->mixer_lock);
   181 	D(bug("Entering audio loop...\n"));
   182 #endif
   183 
   184 
   185 	/* Loop, filling the audio buffers */
   186 	while ( audio->enabled ) {
   187 
   188 		/* Wait for new current buffer to finish playing */
   189 		if ( stream == audio->fake_stream ) {
   190 			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
   191 		} else {
   192 #ifdef ENABLE_AHI
   193 			if ( started > 1 )
   194 #endif
   195 			audio->WaitAudio(audio);
   196 		}
   197 
   198 		/* Fill the current buffer with sound */
   199 		if ( audio->convert.needed ) {
   200 			if ( audio->convert.buf ) {
   201 				stream = audio->convert.buf;
   202 			} else {
   203 				continue;
   204 			}
   205 		} else {
   206 			stream = audio->GetAudioBuf(audio);
   207 			if ( stream == NULL ) {
   208 				stream = audio->fake_stream;
   209 			}
   210 		}
   211 		memset(stream, silence, stream_len);
   212 
   213 		if ( ! audio->paused ) {
   214 			SDL_mutexP(audio->mixer_lock);
   215 			(*fill)(udata, stream, stream_len);
   216 			SDL_mutexV(audio->mixer_lock);
   217 		}
   218 
   219 		/* Convert the audio if necessary */
   220 		if ( audio->convert.needed ) {
   221 			SDL_ConvertAudio(&audio->convert);
   222 			stream = audio->GetAudioBuf(audio);
   223 			if ( stream == NULL ) {
   224 				stream = audio->fake_stream;
   225 			}
   226 			memcpy(stream, audio->convert.buf,
   227 			               audio->convert.len_cvt);
   228 		}
   229 
   230 		/* Ready current buffer for play and change current buffer */
   231 		if ( stream != audio->fake_stream ) {
   232 			audio->PlayAudio(audio);
   233 #ifdef ENABLE_AHI
   234 /* AmigaOS don't have to wait the first time audio is played! */
   235 			started++;
   236 #endif
   237 		}
   238 	}
   239 	/* Wait for the audio to drain.. */
   240 	if ( audio->WaitDone ) {
   241 		audio->WaitDone(audio);
   242 	}
   243 
   244 #ifdef ENABLE_AHI
   245 	D(bug("WaitAudio...Done\n"));
   246 
   247 	audio->CloseAudio(audio);
   248 
   249 	D(bug("CloseAudio..Done, subtask exiting...\n"));
   250 	audio_configured = 0;
   251 #endif
   252 	return(0);
   253 }
   254 
   255 static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
   256 {
   257 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   258 		return;
   259 	}
   260 	SDL_mutexP(audio->mixer_lock);
   261 }
   262 
   263 static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
   264 {
   265 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   266 		return;
   267 	}
   268 	SDL_mutexV(audio->mixer_lock);
   269 }
   270 
   271 int SDL_AudioInit(const char *driver_name)
   272 {
   273 	SDL_AudioDevice *audio;
   274 	int i = 0, idx;
   275 
   276 	/* Check to make sure we don't overwrite 'current_audio' */
   277 	if ( current_audio != NULL ) {
   278 		SDL_AudioQuit();
   279 	}
   280 
   281 	/* Select the proper audio driver */
   282 	audio = NULL;
   283 	idx = 0;
   284 #ifdef unix
   285 	if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
   286 		/* Ahem, we know that if ESPEAKER is set, user probably wants
   287 		   to use ESD, but don't start it if it's not already running.
   288 		   This probably isn't the place to do this, but... Shh! :)
   289 		 */
   290 		for ( i=0; bootstrap[i]; ++i ) {
   291 			if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
   292 				const char *esd_no_spawn;
   293 
   294 				/* Don't start ESD if it's not running */
   295 				esd_no_spawn = getenv("ESD_NO_SPAWN");
   296 				if ( esd_no_spawn == NULL ) {
   297 					putenv("ESD_NO_SPAWN=1");
   298 				}
   299 				if ( bootstrap[i]->available() ) {
   300 					audio = bootstrap[i]->create(0);
   301 					break;
   302 				}
   303 #ifdef linux	/* No unsetenv() on most platforms */
   304 				if ( esd_no_spawn == NULL ) {
   305 					unsetenv("ESD_NO_SPAWN");
   306 				}
   307 #endif
   308 			}
   309 		}
   310 	}
   311 #endif /* unix */
   312 	if ( audio == NULL ) {
   313 		if ( driver_name != NULL ) {
   314 #if 0	/* This will be replaced with a better driver selection API */
   315 			if ( strrchr(driver_name, ':') != NULL ) {
   316 				idx = atoi(strrchr(driver_name, ':')+1);
   317 			}
   318 #endif
   319 			for ( i=0; bootstrap[i]; ++i ) {
   320 				if (strncmp(bootstrap[i]->name, driver_name,
   321 				            strlen(bootstrap[i]->name)) == 0) {
   322 					if ( bootstrap[i]->available() ) {
   323 						audio=bootstrap[i]->create(idx);
   324 						break;
   325 					}
   326 				}
   327 			}
   328 		} else {
   329 			for ( i=0; bootstrap[i]; ++i ) {
   330 				if ( bootstrap[i]->available() ) {
   331 					audio = bootstrap[i]->create(idx);
   332 					if ( audio != NULL ) {
   333 						break;
   334 					}
   335 				}
   336 			}
   337 		}
   338 		if ( audio == NULL ) {
   339 			SDL_SetError("No available audio device");
   340 #if 0 /* Don't fail SDL_Init() if audio isn't available.
   341          SDL_OpenAudio() will handle it at that point.  *sigh*
   342        */
   343 			return(-1);
   344 #endif
   345 		}
   346 	}
   347 	current_audio = audio;
   348 	if ( current_audio ) {
   349 		current_audio->name = bootstrap[i]->name;
   350 		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
   351 			current_audio->LockAudio = SDL_LockAudio_Default;
   352 			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
   353 		}
   354 	}
   355 	return(0);
   356 }
   357 
   358 char *SDL_AudioDriverName(char *namebuf, int maxlen)
   359 {
   360 	if ( current_audio != NULL ) {
   361 		strncpy(namebuf, current_audio->name, maxlen-1);
   362 		namebuf[maxlen-1] = '\0';
   363 		return(namebuf);
   364 	}
   365 	return(NULL);
   366 }
   367 
   368 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
   369 {
   370 	SDL_AudioDevice *audio;
   371 
   372 	/* Start up the audio driver, if necessary */
   373 	if ( ! current_audio ) {
   374 		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
   375 		     (current_audio == NULL) ) {
   376 			return(-1);
   377 		}
   378 	}
   379 	audio = current_audio;
   380 
   381 	if (audio->opened) {
   382 		SDL_SetError("Audio device is already opened");
   383 		return(-1);
   384 	}
   385 
   386 	/* Verify some parameters */
   387 	if ( desired->callback == NULL ) {
   388 		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   389 		return(-1);
   390 	}
   391 	switch ( desired->channels ) {
   392 	    case 1:	/* Mono */
   393 	    case 2:	/* Stereo */
   394 		break;
   395 	    default:
   396 		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
   397 		return(-1);
   398 	}
   399 
   400 #if defined(macintosh) || defined(__riscos__)
   401 	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
   402 #else
   403 #if defined(__MINT__) && !defined(ENABLE_THREADS)
   404 	/* Uses interrupt driven audio, without thread */
   405 #else
   406 	/* Create a semaphore for locking the sound buffers */
   407 	audio->mixer_lock = SDL_CreateMutex();
   408 	if ( audio->mixer_lock == NULL ) {
   409 		SDL_SetError("Couldn't create mixer lock");
   410 		SDL_CloseAudio();
   411 		return(-1);
   412 	}
   413 #endif /* __MINT__ */
   414 #endif /* macintosh */
   415 
   416 	/* Calculate the silence and size of the audio specification */
   417 	SDL_CalculateAudioSpec(desired);
   418 
   419 	/* Open the audio subsystem */
   420 	memcpy(&audio->spec, desired, sizeof(audio->spec));
   421 	audio->convert.needed = 0;
   422 	audio->enabled = 1;
   423 	audio->paused  = 1;
   424 
   425 #ifndef ENABLE_AHI
   426 
   427 /* AmigaOS opens audio inside the main loop */
   428 	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
   429 
   430 	if ( ! audio->opened ) {
   431 		SDL_CloseAudio();
   432 		return(-1);
   433 	}
   434 #else
   435 	D(bug("Locking semaphore..."));
   436 	SDL_mutexP(audio->mixer_lock);
   437 
   438 	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   439 	D(bug("Created thread...\n"));
   440 
   441 	if ( audio->thread == NULL ) {
   442 		SDL_mutexV(audio->mixer_lock);
   443 		SDL_CloseAudio();
   444 		SDL_SetError("Couldn't create audio thread");
   445 		return(-1);
   446 	}
   447 
   448 	while(!audio_configured)
   449 		SDL_Delay(100);
   450 #endif
   451 
   452 	/* If the audio driver changes the buffer size, accept it */
   453 	if ( audio->spec.samples != desired->samples ) {
   454 		desired->samples = audio->spec.samples;
   455 		SDL_CalculateAudioSpec(desired);
   456 	}
   457 
   458 	/* Allocate a fake audio memory buffer */
   459 	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
   460 	if ( audio->fake_stream == NULL ) {
   461 		SDL_CloseAudio();
   462 		SDL_OutOfMemory();
   463 		return(-1);
   464 	}
   465 
   466 	/* See if we need to do any conversion */
   467 	if ( obtained != NULL ) {
   468 		memcpy(obtained, &audio->spec, sizeof(audio->spec));
   469 	} else if ( desired->freq != audio->spec.freq ||
   470                     desired->format != audio->spec.format ||
   471 	            desired->channels != audio->spec.channels ) {
   472 		/* Build an audio conversion block */
   473 		if ( SDL_BuildAudioCVT(&audio->convert,
   474 			desired->format, desired->channels,
   475 					desired->freq,
   476 			audio->spec.format, audio->spec.channels,
   477 					audio->spec.freq) < 0 ) {
   478 			SDL_CloseAudio();
   479 			return(-1);
   480 		}
   481 		if ( audio->convert.needed ) {
   482 			audio->convert.len = desired->size;
   483 			audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   484 			   audio->convert.len*audio->convert.len_mult);
   485 			if ( audio->convert.buf == NULL ) {
   486 				SDL_CloseAudio();
   487 				SDL_OutOfMemory();
   488 				return(-1);
   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 }