src/audio/SDL_audio.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. SDL_RunAudio
  2. SDL_AudioInit
  3. SDL_AudioDriverName
  4. SDL_OpenAudio
  5. SDL_GetAudioStatus
  6. SDL_PauseAudio
  7. SDL_LockAudio
  8. SDL_UnlockAudio
  9. SDL_CloseAudio
  10. SDL_AudioQuit
  11. SDL_FirstAudioFormat
  12. SDL_NextAudioFormat
  13. SDL_CalculateAudioSpec

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

/* [<][>][^][v][top][bottom][index][help] */