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