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