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