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