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