Merged r4991:5154 from branches/SDL-1.2/src/audio/alsa: many 1.2.14 ALSA fixes.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 10 Jan 2010 07:40:12 +0000
changeset 3627631173ffd68f
parent 3626 596468a8459e
child 3628 4d46850be3f6
Merged r4991:5154 from branches/SDL-1.2/src/audio/alsa: many 1.2.14 ALSA fixes.
src/audio/alsa/SDL_alsa_audio.c
src/audio/alsa/SDL_alsa_audio.h
     1.1 --- a/src/audio/alsa/SDL_alsa_audio.c	Sun Jan 10 06:32:41 2010 +0000
     1.2 +++ b/src/audio/alsa/SDL_alsa_audio.c	Sun Jan 10 07:40:12 2010 +0000
     1.3 @@ -41,20 +41,19 @@
     1.4  /* The tag name used by ALSA audio */
     1.5  #define DRIVER_NAME         "alsa"
     1.6  
     1.7 -/* The default ALSA audio driver */
     1.8 -#define DEFAULT_DEVICE	"default"
     1.9 -
    1.10  static int (*ALSA_snd_pcm_open)
    1.11    (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
    1.12  static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
    1.13  static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
    1.14    (snd_pcm_t *, const void *, snd_pcm_uframes_t);
    1.15 -static int (*ALSA_snd_pcm_resume) (snd_pcm_t *);
    1.16 +static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
    1.17  static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
    1.18  static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
    1.19  static const char *(*ALSA_snd_strerror) (int);
    1.20  static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
    1.21  static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
    1.22 +static void (*ALSA_snd_pcm_hw_params_copy)
    1.23 +  (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
    1.24  static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
    1.25  static int (*ALSA_snd_pcm_hw_params_set_access)
    1.26    (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
    1.27 @@ -62,26 +61,30 @@
    1.28    (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
    1.29  static int (*ALSA_snd_pcm_hw_params_set_channels)
    1.30    (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
    1.31 +static int (*ALSA_snd_pcm_hw_params_get_channels)
    1.32 +  (const snd_pcm_hw_params_t *, unsigned int *);
    1.33  static int (*ALSA_snd_pcm_hw_params_set_rate_near)
    1.34    (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
    1.35  static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
    1.36    (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
    1.37 -static int (*ALSA_snd_pcm_hw_params_get_channels)
    1.38 -  (const snd_pcm_hw_params_t *, unsigned int *);
    1.39 -static snd_pcm_sframes_t(*ALSA_snd_pcm_hw_params_get_period_size)
    1.40 -  (const snd_pcm_hw_params_t *);
    1.41 +static int (*ALSA_snd_pcm_hw_params_get_period_size)
    1.42 +  (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
    1.43  static int (*ALSA_snd_pcm_hw_params_set_periods_near)
    1.44    (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
    1.45 -static int (*ALSA_snd_pcm_hw_params_get_periods) (snd_pcm_hw_params_t *);
    1.46 +static int (*ALSA_snd_pcm_hw_params_get_periods)
    1.47 +  (const snd_pcm_hw_params_t *, unsigned int *, int *);
    1.48 +static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
    1.49 +  (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
    1.50 +static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
    1.51 +  (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
    1.52  static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
    1.53  static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
    1.54                                                snd_pcm_sw_params_t *);
    1.55  static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
    1.56    (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
    1.57 -static int (*ALSA_snd_pcm_sw_params_set_avail_min)
    1.58 -  (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
    1.59  static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
    1.60  static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
    1.61 +static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
    1.62  #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
    1.63  #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
    1.64  
    1.65 @@ -116,12 +119,13 @@
    1.66      SDL_ALSA_SYM(snd_pcm_open);
    1.67      SDL_ALSA_SYM(snd_pcm_close);
    1.68      SDL_ALSA_SYM(snd_pcm_writei);
    1.69 -    SDL_ALSA_SYM(snd_pcm_resume);
    1.70 +    SDL_ALSA_SYM(snd_pcm_recover);
    1.71      SDL_ALSA_SYM(snd_pcm_prepare);
    1.72      SDL_ALSA_SYM(snd_pcm_drain);
    1.73      SDL_ALSA_SYM(snd_strerror);
    1.74      SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
    1.75      SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
    1.76 +    SDL_ALSA_SYM(snd_pcm_hw_params_copy);
    1.77      SDL_ALSA_SYM(snd_pcm_hw_params_any);
    1.78      SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
    1.79      SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
    1.80 @@ -132,12 +136,14 @@
    1.81      SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
    1.82      SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
    1.83      SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
    1.84 +    SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
    1.85 +    SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
    1.86      SDL_ALSA_SYM(snd_pcm_hw_params);
    1.87      SDL_ALSA_SYM(snd_pcm_sw_params_current);
    1.88      SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
    1.89 -    SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
    1.90      SDL_ALSA_SYM(snd_pcm_sw_params);
    1.91      SDL_ALSA_SYM(snd_pcm_nonblock);
    1.92 +    SDL_ALSA_SYM(snd_pcm_wait);
    1.93      return 0;
    1.94  }
    1.95  
    1.96 @@ -196,12 +202,17 @@
    1.97  
    1.98      device = SDL_getenv("AUDIODEV");    /* Is there a standard variable name? */
    1.99      if (device == NULL) {
   1.100 -        if (channels == 6)
   1.101 -            device = "surround51";
   1.102 -        else if (channels == 4)
   1.103 -            device = "surround40";
   1.104 -        else
   1.105 -            device = DEFAULT_DEVICE;
   1.106 +        switch (channels) {
   1.107 +        case 6:
   1.108 +            device = "plug:surround51";
   1.109 +            break;
   1.110 +        case 4:
   1.111 +            device = "plug:surround40";
   1.112 +            break;
   1.113 +        default:
   1.114 +            device = "default";
   1.115 +            break;
   1.116 +        }
   1.117      }
   1.118      return device;
   1.119  }
   1.120 @@ -211,19 +222,7 @@
   1.121  static void
   1.122  ALSA_WaitDevice(_THIS)
   1.123  {
   1.124 -    /* Check to see if the thread-parent process is still alive */
   1.125 -    {
   1.126 -        static int cnt = 0;
   1.127 -        /* Note that this only works with thread implementations 
   1.128 -           that use a different process id for each thread.
   1.129 -         */
   1.130 -        /* Check every 10 loops */
   1.131 -        if (this->hidden->parent && (((++cnt) % 10) == 0)) {
   1.132 -            if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
   1.133 -                this->enabled = 0;
   1.134 -            }
   1.135 -        }
   1.136 -    }
   1.137 +    /* We're in blocking mode, so there's nothing to do here */
   1.138  }
   1.139  
   1.140  
   1.141 @@ -297,41 +296,38 @@
   1.142  ALSA_PlayDevice(_THIS)
   1.143  {
   1.144      int status;
   1.145 -    int sample_len;
   1.146 -    signed short *sample_buf;
   1.147 +    const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
   1.148 +    const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
   1.149 +                                this->spec.channels;
   1.150 +    snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
   1.151  
   1.152      swizzle_alsa_channels(this);
   1.153  
   1.154 -    sample_len = this->spec.samples;
   1.155 -    sample_buf = (signed short *) this->hidden->mixbuf;
   1.156 -
   1.157 -    while (sample_len > 0) {
   1.158 +    while ( frames_left > 0 && this->enabled ) {
   1.159 +        /* !!! FIXME: This works, but needs more testing before going live */
   1.160 +        /*ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1);*/
   1.161          status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
   1.162 -                                     sample_buf, sample_len);
   1.163 +                                     sample_buf, frames_left);
   1.164  
   1.165          if (status < 0) {
   1.166              if (status == -EAGAIN) {
   1.167 +                /* Apparently snd_pcm_recover() doesn't handle this case -
   1.168 +                   does it assume snd_pcm_wait() above? */
   1.169                  SDL_Delay(1);
   1.170                  continue;
   1.171              }
   1.172 -            if (status == -ESTRPIPE) {
   1.173 -                do {
   1.174 -                    SDL_Delay(1);
   1.175 -                    status = ALSA_snd_pcm_resume(this->hidden->pcm_handle);
   1.176 -                } while (status == -EAGAIN);
   1.177 -            }
   1.178 -            if (status < 0) {
   1.179 -                status = ALSA_snd_pcm_prepare(this->hidden->pcm_handle);
   1.180 -            }
   1.181 +            status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
   1.182              if (status < 0) {
   1.183                  /* Hmm, not much we can do - abort */
   1.184 +                fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
   1.185 +                        ALSA_snd_strerror(status));
   1.186                  this->enabled = 0;
   1.187                  return;
   1.188              }
   1.189              continue;
   1.190          }
   1.191 -        sample_buf += status * this->spec.channels;
   1.192 -        sample_len -= status;
   1.193 +        sample_buf += status * frame_size;
   1.194 +        frames_left -= status;
   1.195      }
   1.196  }
   1.197  
   1.198 @@ -360,6 +356,118 @@
   1.199  }
   1.200  
   1.201  static int
   1.202 +ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
   1.203 +{
   1.204 +    int status;
   1.205 +    snd_pcm_uframes_t bufsize;
   1.206 +
   1.207 +    /* "set" the hardware with the desired parameters */
   1.208 +    status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
   1.209 +    if ( status < 0 ) {
   1.210 +        return(-1);
   1.211 +    }
   1.212 +
   1.213 +    /* Get samples for the actual buffer size */
   1.214 +    status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
   1.215 +    if ( status < 0 ) {
   1.216 +        return(-1);
   1.217 +    }
   1.218 +    if ( !override && bufsize != this->spec.samples * 2 ) {
   1.219 +        return(-1);
   1.220 +    }
   1.221 +
   1.222 +    /* !!! FIXME: Is this safe to do? */
   1.223 +    this->spec.samples = bufsize / 2;
   1.224 +
   1.225 +    /* This is useful for debugging */
   1.226 +    if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
   1.227 +        snd_pcm_uframes_t persize = 0;
   1.228 +        unsigned int periods = 0;
   1.229 +
   1.230 +        ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
   1.231 +        ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
   1.232 +
   1.233 +        fprintf(stderr,
   1.234 +            "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
   1.235 +            persize, periods, bufsize);
   1.236 +    }
   1.237 +
   1.238 +    return(0);
   1.239 +}
   1.240 +
   1.241 +static int
   1.242 +ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
   1.243 +{
   1.244 +    const char *env;
   1.245 +    int status;
   1.246 +    snd_pcm_hw_params_t *hwparams;
   1.247 +    snd_pcm_uframes_t frames;
   1.248 +    unsigned int periods;
   1.249 +
   1.250 +    /* Copy the hardware parameters for this setup */
   1.251 +    snd_pcm_hw_params_alloca(&hwparams);
   1.252 +    ALSA_snd_pcm_hw_params_copy(hwparams, params);
   1.253 +
   1.254 +    if ( !override ) {
   1.255 +        env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
   1.256 +        if ( env ) {
   1.257 +            override = SDL_atoi(env);
   1.258 +            if ( override == 0 ) {
   1.259 +                return(-1);
   1.260 +            }
   1.261 +        }
   1.262 +    }
   1.263 +
   1.264 +    frames = this->spec.samples;
   1.265 +    status = ALSA_snd_pcm_hw_params_set_period_size_near(
   1.266 +                this->hidden->pcm_handle, hwparams, &frames, NULL);
   1.267 +    if ( status < 0 ) {
   1.268 +        return(-1);
   1.269 +    }
   1.270 +
   1.271 +    periods = 2;
   1.272 +    status = ALSA_snd_pcm_hw_params_set_periods_near(
   1.273 +                this->hidden->pcm_handle, hwparams, &periods, NULL);
   1.274 +    if ( status < 0 ) {
   1.275 +        return(-1);
   1.276 +    }
   1.277 +
   1.278 +    return ALSA_finalize_hardware(this, hwparams, override);
   1.279 +}
   1.280 +
   1.281 +static int
   1.282 +ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
   1.283 +{
   1.284 +    const char *env;
   1.285 +    int status;
   1.286 +    snd_pcm_hw_params_t *hwparams;
   1.287 +    snd_pcm_uframes_t frames;
   1.288 +
   1.289 +    /* Copy the hardware parameters for this setup */
   1.290 +    snd_pcm_hw_params_alloca(&hwparams);
   1.291 +    ALSA_snd_pcm_hw_params_copy(hwparams, params);
   1.292 +
   1.293 +    if ( !override ) {
   1.294 +        env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
   1.295 +        if ( env ) {
   1.296 +            override = SDL_atoi(env);
   1.297 +            if ( override == 0 ) {
   1.298 +                return(-1);
   1.299 +            }
   1.300 +        }
   1.301 +    }
   1.302 +
   1.303 +    frames = this->spec.samples * 2;
   1.304 +    status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
   1.305 +                    this->hidden->pcm_handle, hwparams, &frames);
   1.306 +    if ( status < 0 ) {
   1.307 +        return(-1);
   1.308 +    }
   1.309 +
   1.310 +    return ALSA_finalize_hardware(this, hwparams, override);
   1.311 +}
   1.312 +
   1.313 +static int
   1.314  ALSA_OpenDevice(_THIS, const char *devname, int iscapture)
   1.315  {
   1.316      int status = 0;
   1.317 @@ -367,10 +475,8 @@
   1.318      snd_pcm_hw_params_t *hwparams = NULL;
   1.319      snd_pcm_sw_params_t *swparams = NULL;
   1.320      snd_pcm_format_t format = 0;
   1.321 -    snd_pcm_uframes_t frames = 0;
   1.322      SDL_AudioFormat test_format = 0;
   1.323      unsigned int rate = 0;
   1.324 -    unsigned int periods = 0;
   1.325      unsigned int channels = 0;
   1.326  
   1.327      /* Initialize all variables that we clean on shutdown */
   1.328 @@ -499,40 +605,15 @@
   1.329      this->spec.freq = rate;
   1.330  
   1.331      /* Set the buffer size, in samples */
   1.332 -    frames = this->spec.samples;
   1.333 -    status = ALSA_snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams,
   1.334 -                                                         &frames, NULL);
   1.335 -    if ( status < 0 ) {
   1.336 -        ALSA_CloseDevice(this);
   1.337 -        SDL_SetError("ALSA: Couldn't set audio frequency: %s",
   1.338 -                     ALSA_snd_strerror(status));
   1.339 -        return(-1);
   1.340 +    if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
   1.341 +         ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
   1.342 +        /* Failed to set desired buffer size, do the best you can... */
   1.343 +        if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
   1.344 +            ALSA_CloseDevice(this);
   1.345 +            SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
   1.346 +            return(-1);
   1.347 +        }
   1.348      }
   1.349 -    this->spec.samples = frames;
   1.350 -
   1.351 -    periods = 2;
   1.352 -    ALSA_snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams,
   1.353 -                                            &periods, NULL);
   1.354 -
   1.355 -    /* "set" the hardware with the desired parameters */
   1.356 -    status = ALSA_snd_pcm_hw_params(pcm_handle, hwparams);
   1.357 -    if (status < 0) {
   1.358 -        ALSA_CloseDevice(this);
   1.359 -        SDL_SetError("ALSA: Couldn't set hardware audio parameters: %s",
   1.360 -                     ALSA_snd_strerror(status));
   1.361 -        return 0;
   1.362 -    }
   1.363 -#if AUDIO_DEBUG
   1.364 -    {
   1.365 -        snd_pcm_sframes_t bufsize;
   1.366 -        int fragments;
   1.367 -        bufsize = ALSA_snd_pcm_hw_params_get_period_size(hwparams);
   1.368 -        fragments = ALSA_snd_pcm_hw_params_get_periods(hwparams);
   1.369 -        fprintf(stderr, "ALSA: bufsize = %ld, fragments = %d\n", bufsize,
   1.370 -                fragments);
   1.371 -    }
   1.372 -#endif
   1.373 -
   1.374      /* Set the software parameters */
   1.375      snd_pcm_sw_params_alloca(&swparams);
   1.376      status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
   1.377 @@ -543,20 +624,13 @@
   1.378          return 0;
   1.379      }
   1.380      status =
   1.381 -        ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 0);
   1.382 +        ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
   1.383      if (status < 0) {
   1.384          ALSA_CloseDevice(this);
   1.385          SDL_SetError("ALSA: Couldn't set start threshold: %s",
   1.386                       ALSA_snd_strerror(status));
   1.387          return 0;
   1.388      }
   1.389 -    status =
   1.390 -        ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, frames);
   1.391 -    if (status < 0) {
   1.392 -        ALSA_CloseDevice(this);
   1.393 -        SDL_SetError("Couldn't set avail min: %s", ALSA_snd_strerror(status));
   1.394 -        return 0;
   1.395 -    }
   1.396      status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
   1.397      if (status < 0) {
   1.398          ALSA_CloseDevice(this);
   1.399 @@ -578,9 +652,6 @@
   1.400      }
   1.401      SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   1.402  
   1.403 -    /* Get the parent process id (we're the parent of the audio thread) */
   1.404 -    this->hidden->parent = getpid();
   1.405 -
   1.406      /* Switch to blocking mode for playback */
   1.407      ALSA_snd_pcm_nonblock(pcm_handle, 0);
   1.408  
   1.409 @@ -615,7 +686,7 @@
   1.410  
   1.411  
   1.412  AudioBootStrap ALSA_bootstrap = {
   1.413 -    DRIVER_NAME, "ALSA 0.9 PCM audio", ALSA_Init, 0
   1.414 +    DRIVER_NAME, "ALSA PCM audio", ALSA_Init, 0
   1.415  };
   1.416  
   1.417  /* vi: set ts=4 sw=4 expandtab: */
     2.1 --- a/src/audio/alsa/SDL_alsa_audio.h	Sun Jan 10 06:32:41 2010 +0000
     2.2 +++ b/src/audio/alsa/SDL_alsa_audio.h	Sun Jan 10 07:40:12 2010 +0000
     2.3 @@ -36,9 +36,6 @@
     2.4      /* The audio device handle */
     2.5      snd_pcm_t *pcm_handle;
     2.6  
     2.7 -    /* The parent process id, to detect when application quits */
     2.8 -    pid_t parent;
     2.9 -
    2.10      /* Raw mixing buffer */
    2.11      Uint8 *mixbuf;
    2.12      int mixlen;