src/audio/dsp/SDL_dspaudio.c

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

FUNCTIONS

This source file includes following functions.
  1. Audio_Available
  2. Audio_DeleteDevice
  3. Audio_CreateDevice
  4. DSP_WaitAudio
  5. DSP_PlayAudio
  6. DSP_GetAudioBuf
  7. DSP_CloseAudio
  8. DSP_ReopenAudio
  9. DSP_OpenAudio

   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_dspaudio.c,v 1.1.2.5 2001/03/21 17:19:56 hercules Exp $";
  26 #endif
  27 
  28 /* Allow access to a raw mixing buffer */
  29 
  30 #include <stdlib.h>
  31 #include <stdio.h>
  32 #include <string.h>
  33 #include <errno.h>
  34 #include <unistd.h>
  35 #include <fcntl.h>
  36 #include <signal.h>
  37 #include <sys/time.h>
  38 #include <sys/ioctl.h>
  39 #include <sys/stat.h>
  40 #ifdef linux
  41 #include <linux/soundcard.h>
  42 #endif
  43 #ifdef __bsdi__
  44 #include <sys/soundcard.h>
  45 #endif
  46 #ifdef __FreeBSD__
  47 #include <machine/soundcard.h>
  48 #endif
  49 #ifdef __USLC__
  50 #include <sys/soundcard.h>
  51 #endif
  52 
  53 #include "SDL_audio.h"
  54 #include "SDL_error.h"
  55 #include "SDL_audiomem.h"
  56 #include "SDL_audio_c.h"
  57 #include "SDL_timer.h"
  58 #include "SDL_audiodev_c.h"
  59 #include "SDL_dspaudio.h"
  60 
  61 /* The tag name used by DSP audio */
  62 #define DSP_DRIVER_NAME         "dsp"
  63 
  64 /* Open the audio device for playback, and don't block if busy */
  65 /*#define USE_BLOCKING_WRITES*/
  66 #ifdef USE_BLOCKING_WRITES
  67 #define OPEN_FLAGS      O_WRONLY
  68 #else
  69 #define OPEN_FLAGS      (O_WRONLY|O_NONBLOCK)
  70 #endif
  71 
  72 /* Audio driver functions */
  73 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
  74 static void DSP_WaitAudio(_THIS);
  75 static void DSP_PlayAudio(_THIS);
  76 static Uint8 *DSP_GetAudioBuf(_THIS);
  77 static void DSP_CloseAudio(_THIS);
  78 
  79 /* Audio driver bootstrap functions */
  80 
  81 static int Audio_Available(void)
     /* [<][>][^][v][top][bottom][index][help] */
  82 {
  83         int fd;
  84         int available;
  85 
  86         available = 0;
  87         fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
  88         if ( fd >= 0 ) {
  89                 available = 1;
  90                 close(fd);
  91         }
  92         return(available);
  93 }
  94 
  95 static void Audio_DeleteDevice(SDL_AudioDevice *device)
     /* [<][>][^][v][top][bottom][index][help] */
  96 {
  97         free(device->hidden);
  98         free(device);
  99 }
 100 
 101 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
     /* [<][>][^][v][top][bottom][index][help] */
 102 {
 103         SDL_AudioDevice *this;
 104 
 105         /* Initialize all variables that we clean on shutdown */
 106         this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
 107         if ( this ) {
 108                 memset(this, 0, (sizeof *this));
 109                 this->hidden = (struct SDL_PrivateAudioData *)
 110                                 malloc((sizeof *this->hidden));
 111         }
 112         if ( (this == NULL) || (this->hidden == NULL) ) {
 113                 SDL_OutOfMemory();
 114                 if ( this ) {
 115                         free(this);
 116                 }
 117                 return(0);
 118         }
 119         memset(this->hidden, 0, (sizeof *this->hidden));
 120         audio_fd = -1;
 121 
 122         /* Set the function pointers */
 123         this->OpenAudio = DSP_OpenAudio;
 124         this->WaitAudio = DSP_WaitAudio;
 125         this->PlayAudio = DSP_PlayAudio;
 126         this->GetAudioBuf = DSP_GetAudioBuf;
 127         this->CloseAudio = DSP_CloseAudio;
 128 
 129         this->free = Audio_DeleteDevice;
 130 
 131         return this;
 132 }
 133 
 134 AudioBootStrap DSP_bootstrap = {
 135         DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
 136         Audio_Available, Audio_CreateDevice
 137 };
 138 
 139 /* This function waits until it is possible to write a full sound buffer */
 140 static void DSP_WaitAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 141 {
 142 #ifndef USE_BLOCKING_WRITES /* Not necessary because of blocking writes */
 143         fd_set fdset;
 144 
 145         /* Check to see if the thread-parent process is still alive */
 146         { static int cnt = 0;
 147                 /* Note that this only works with thread implementations 
 148                    that use a different process id for each thread.
 149                 */
 150                 if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
 151                         if ( kill(parent, 0) < 0 ) {
 152                                 this->enabled = 0;
 153                         }
 154                 }
 155         }
 156 
 157         /* See if we need to use timed audio synchronization */
 158         if ( frame_ticks ) {
 159                 /* Use timer for general audio synchronization */
 160                 Sint32 ticks;
 161 
 162                 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
 163                 if ( ticks > 0 ) {
 164                         SDL_Delay(ticks);
 165                 }
 166         } else {
 167                 /* Use select() for audio synchronization */
 168                 struct timeval timeout;
 169                 FD_ZERO(&fdset);
 170                 FD_SET(audio_fd, &fdset);
 171                 timeout.tv_sec = 10;
 172                 timeout.tv_usec = 0;
 173 #ifdef DEBUG_AUDIO
 174                 fprintf(stderr, "Waiting for audio to get ready\n");
 175 #endif
 176                 if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
 177                         const char *message =
 178                         "Audio timeout - buggy audio driver? (disabled)";
 179                         /* In general we should never print to the screen,
 180                            but in this case we have no other way of letting
 181                            the user know what happened.
 182                         */
 183                         fprintf(stderr, "SDL: %s\n", message);
 184                         this->enabled = 0;
 185                         /* Don't try to close - may hang */
 186                         audio_fd = -1;
 187 #ifdef DEBUG_AUDIO
 188                         fprintf(stderr, "Done disabling audio\n");
 189 #endif
 190                 }
 191 #ifdef DEBUG_AUDIO
 192                 fprintf(stderr, "Ready!\n");
 193 #endif
 194         }
 195 #endif /* !USE_BLOCKING_WRITES */
 196 }
 197 
 198 static void DSP_PlayAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 199 {
 200         int written;
 201 
 202         /* Write the audio data, checking for EAGAIN on broken audio drivers */
 203         do {
 204                 written = write(audio_fd, mixbuf, mixlen);
 205                 if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
 206                         SDL_Delay(1);   /* Let a little CPU time go by */
 207                 }
 208         } while ( (written < 0) && 
 209                   ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
 210 
 211         /* If timer synchronization is enabled, set the next write frame */
 212         if ( frame_ticks ) {
 213                 next_frame += frame_ticks;
 214         }
 215 
 216         /* If we couldn't write, assume fatal error for now */
 217         if ( written < 0 ) {
 218                 this->enabled = 0;
 219         }
 220 #ifdef DEBUG_AUDIO
 221         fprintf(stderr, "Wrote %d bytes of audio data\n", written);
 222 #endif
 223 }
 224 
 225 static Uint8 *DSP_GetAudioBuf(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 226 {
 227         return(mixbuf);
 228 }
 229 
 230 static void DSP_CloseAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 231 {
 232         if ( mixbuf != NULL ) {
 233                 SDL_FreeAudioMem(mixbuf);
 234                 mixbuf = NULL;
 235         }
 236         if ( audio_fd >= 0 ) {
 237                 close(audio_fd);
 238                 audio_fd = -1;
 239         }
 240 }
 241 
 242 static int DSP_ReopenAudio(_THIS, const char *audiodev, int format,
     /* [<][>][^][v][top][bottom][index][help] */
 243                                                 SDL_AudioSpec *spec)
 244 {
 245         int frag_spec;
 246         int value;
 247 
 248         /* Close and then reopen the audio device */
 249         close(audio_fd);
 250         audio_fd = open(audiodev, O_WRONLY, 0);
 251         if ( audio_fd < 0 ) {
 252                 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
 253                 return(-1);
 254         }
 255 
 256         /* Calculate the final parameters for this audio specification */
 257         SDL_CalculateAudioSpec(spec);
 258 
 259         /* Determine the power of two of the fragment size */
 260         for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
 261         if ( (0x01<<frag_spec) != spec->size ) {
 262                 SDL_SetError("Fragment size must be a power of two");
 263                 return(-1);
 264         }
 265         frag_spec |= 0x00020000;        /* two fragments, for low latency */
 266 
 267         /* Set the audio buffering parameters */
 268 #ifdef DEBUG_AUDIO
 269         fprintf(stderr, "Requesting %d fragments of size %d\n",
 270                 (frag_spec >> 16), 1<<(frag_spec&0xFFFF));
 271 #endif
 272         if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
 273                 fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
 274         }
 275 #ifdef DEBUG_AUDIO
 276         { audio_buf_info info;
 277           ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
 278           fprintf(stderr, "fragments = %d\n", info.fragments);
 279           fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
 280           fprintf(stderr, "fragsize = %d\n", info.fragsize);
 281           fprintf(stderr, "bytes = %d\n", info.bytes);
 282         }
 283 #endif
 284 
 285         /* Set the audio format */
 286         value = format;
 287         if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
 288                                                 (value != format) ) {
 289                 SDL_SetError("Couldn't set audio format");
 290                 return(-1);
 291         }
 292 
 293         /* Set the number of channels of output */
 294         value = spec->channels;
 295 #ifdef SNDCTL_DSP_CHANNELS
 296         if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
 297 #endif
 298                 value = (spec->channels > 1);
 299                 ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
 300                 value = (value ? 2 : 1);
 301 #ifdef SNDCTL_DSP_CHANNELS
 302         }
 303 #endif
 304         if ( value != spec->channels ) {
 305                 SDL_SetError("Couldn't set audio channels");
 306                 return(-1);
 307         }
 308 
 309         /* Set the DSP frequency */
 310         value = spec->freq;
 311         if ( ioctl(audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
 312                 SDL_SetError("Couldn't set audio frequency");
 313                 return(-1);
 314         }
 315         spec->freq = value;
 316 
 317         /* We successfully re-opened the audio */
 318         return(0);
 319 }
 320 
 321 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
     /* [<][>][^][v][top][bottom][index][help] */
 322 {
 323         char audiodev[1024];
 324         int format;
 325         int value;
 326         Uint16 test_format;
 327 
 328         /* Reset the timer synchronization flag */
 329         frame_ticks = 0.0;
 330 
 331         /* Open the audio device */
 332         audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
 333         if ( audio_fd < 0 ) {
 334                 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
 335                 return(-1);
 336         }
 337         mixbuf = NULL;
 338 
 339         /* Get a list of supported hardware formats */
 340         if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
 341                 SDL_SetError("Couldn't get audio format list");
 342                 return(-1);
 343         }
 344 
 345         /* Try for a closest match on audio format */
 346         format = 0;
 347         for ( test_format = SDL_FirstAudioFormat(spec->format);
 348                                                 ! format && test_format; ) {
 349 #ifdef DEBUG_AUDIO
 350                 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
 351 #endif
 352                 switch ( test_format ) {
 353                         case AUDIO_U8:
 354                                 if ( value & AFMT_U8 ) {
 355                                         format = AFMT_U8;
 356                                 }
 357                                 break;
 358                         case AUDIO_S8:
 359                                 if ( value & AFMT_S8 ) {
 360                                         format = AFMT_S8;
 361                                 }
 362                                 break;
 363                         case AUDIO_S16LSB:
 364                                 if ( value & AFMT_S16_LE ) {
 365                                         format = AFMT_S16_LE;
 366                                 }
 367                                 break;
 368                         case AUDIO_S16MSB:
 369                                 if ( value & AFMT_S16_BE ) {
 370                                         format = AFMT_S16_BE;
 371                                 }
 372                                 break;
 373                         case AUDIO_U16LSB:
 374                                 if ( value & AFMT_U16_LE ) {
 375                                         format = AFMT_U16_LE;
 376                                 }
 377                                 break;
 378                         case AUDIO_U16MSB:
 379                                 if ( value & AFMT_U16_BE ) {
 380                                         format = AFMT_U16_BE;
 381                                 }
 382                                 break;
 383                         default:
 384                                 break;
 385                 }
 386                 if ( ! format ) {
 387                         test_format = SDL_NextAudioFormat();
 388                 }
 389         }
 390         if ( format == 0 ) {
 391                 SDL_SetError("Couldn't find any hardware audio formats");
 392                 return(-1);
 393         }
 394         spec->format = test_format;
 395 
 396         /* Set the audio format */
 397         value = format;
 398         if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
 399                                                 (value != format) ) {
 400                 SDL_SetError("Couldn't set audio format");
 401                 return(-1);
 402         }
 403 
 404         /* Set the number of channels of output */
 405         value = spec->channels;
 406 #ifdef SNDCTL_DSP_CHANNELS
 407         if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
 408 #endif
 409                 value = (spec->channels > 1);
 410                 ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
 411                 value = (value ? 2 : 1);
 412 #ifdef SNDCTL_DSP_CHANNELS
 413         }
 414 #endif
 415         spec->channels = value;
 416 
 417         /* Because some drivers don't allow setting the buffer size
 418            after setting the format, we must re-open the audio device
 419            once we know what format and channels are supported
 420          */
 421         if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
 422                 /* Error is set by DSP_ReopenAudio() */
 423                 return(-1);
 424         }
 425 
 426         /* Allocate mixing buffer */
 427         mixlen = spec->size;
 428         mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
 429         if ( mixbuf == NULL ) {
 430                 return(-1);
 431         }
 432         memset(mixbuf, spec->silence, spec->size);
 433 
 434 #ifndef USE_BLOCKING_WRITES
 435         /* Check to see if we need to use select() workaround */
 436         { char *workaround;
 437                 workaround = getenv("SDL_DSP_NOSELECT");
 438                 if ( workaround ) {
 439                         frame_ticks = (float)(spec->samples*1000)/spec->freq;
 440                         next_frame = SDL_GetTicks()+frame_ticks;
 441                 }
 442         }
 443 #endif /* !USE_BLOCKING_WRITES */
 444 
 445         /* Get the parent process id (we're the parent of the audio thread) */
 446         parent = getpid();
 447 
 448         /* We're ready to rock and roll. :-) */
 449         return(0);
 450 }

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