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