ALSA driver improvements:
authorSam Lantinga <slouken@libsdl.org>
Tue, 14 Mar 2017 07:20:14 -0700
changeset 109367ef6524d95c4
parent 10935 9a4c02660fcf
child 10937 9ff840e45cfb
ALSA driver improvements:
* alsa hotplug thread is low priority
* give a chance for other threads to catch up when audio playback is not progressing
* use nonblocking for alsa audio capture
There is a bug with SDL hanging when an audio capture USB device is removed, because poll never returns
src/audio/alsa/SDL_alsa_audio.c
     1.1 --- a/src/audio/alsa/SDL_alsa_audio.c	Tue Mar 14 07:16:56 2017 -0700
     1.2 +++ b/src/audio/alsa/SDL_alsa_audio.c	Tue Mar 14 07:20:14 2017 -0700
     1.3 @@ -364,6 +364,13 @@
     1.4              }
     1.5              continue;
     1.6          }
     1.7 +        else if (status == 0) {
     1.8 +            /* No frames were written (no available space in pcm device).
     1.9 +               Allow other threads to catch up. */
    1.10 +            Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq;
    1.11 +            SDL_Delay(delay);
    1.12 +        }
    1.13 +
    1.14          sample_buf += status * frame_size;
    1.15          frames_left -= status;
    1.16      }
    1.17 @@ -383,23 +390,22 @@
    1.18                                  this->spec.channels;
    1.19      const int total_frames = buflen / frame_size;
    1.20      snd_pcm_uframes_t frames_left = total_frames;
    1.21 +    snd_pcm_uframes_t wait_time = frame_size / 2;
    1.22  
    1.23      SDL_assert((buflen % frame_size) == 0);
    1.24  
    1.25      while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
    1.26 -        /* !!! FIXME: This works, but needs more testing before going live */
    1.27 -        /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
    1.28 -        int status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
    1.29 +        int status;
    1.30 +
    1.31 +        status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
    1.32                                          sample_buf, frames_left);
    1.33  
    1.34 -        if (status < 0) {
    1.35 +        if (status == -EAGAIN) {
    1.36 +            ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
    1.37 +            status = 0;
    1.38 +        }
    1.39 +        else if (status < 0) {
    1.40              /*printf("ALSA: capture error %d\n", status);*/
    1.41 -            if (status == -EAGAIN) {
    1.42 -                /* Apparently snd_pcm_recover() doesn't handle this case -
    1.43 -                   does it assume snd_pcm_wait() above? */
    1.44 -                SDL_Delay(1);
    1.45 -                continue;
    1.46 -            }
    1.47              status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
    1.48              if (status < 0) {
    1.49                  /* Hmm, not much we can do - abort */
    1.50 @@ -745,8 +751,9 @@
    1.51          SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
    1.52      }
    1.53  
    1.54 -    /* Switch to blocking mode for playback */
    1.55 -    ALSA_snd_pcm_nonblock(pcm_handle, 0);
    1.56 +    if (!iscapture) {
    1.57 +        ALSA_snd_pcm_nonblock(pcm_handle, 0);
    1.58 +    }
    1.59  
    1.60      /* We're ready to rock and roll. :-) */
    1.61      return 0;
    1.62 @@ -763,16 +770,26 @@
    1.63  add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
    1.64  {
    1.65      ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
    1.66 -    char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
    1.67 +    char *desc;
    1.68      char *handle = NULL;
    1.69      char *ptr;
    1.70  
    1.71 -    if (!desc) {
    1.72 -        SDL_free(dev);
    1.73 +    if (!dev) {
    1.74          return;
    1.75 -    } else if (!dev) {
    1.76 -        free(desc);
    1.77 -        return;
    1.78 +    }
    1.79 +
    1.80 +    /* Not all alsa devices are enumerable via snd_device_name_get_hint
    1.81 +       (i.e. bluetooth devices).  Therefore if hint is passed in to this
    1.82 +       function as  NULL, assume name contains desc.
    1.83 +       Make sure not to free the storage associated with desc in this case */
    1.84 +    if (hint) {
    1.85 +        desc = ALSA_snd_device_name_get_hint(hint, "DESC");
    1.86 +        if (!desc) {
    1.87 +            SDL_free(dev);
    1.88 +            return;
    1.89 +        }
    1.90 +    } else {
    1.91 +        desc = (char *) name;
    1.92      }
    1.93  
    1.94      SDL_assert(name != NULL);
    1.95 @@ -788,14 +805,16 @@
    1.96  
    1.97      handle = SDL_strdup(name);
    1.98      if (!handle) {
    1.99 -        free(desc);
   1.100 +        if (hint) {
   1.101 +            free(desc);
   1.102 +        }
   1.103          SDL_free(dev);
   1.104          return;
   1.105      }
   1.106  
   1.107      SDL_AddAudioDevice(iscapture, desc, handle);
   1.108 -    free(desc);
   1.109 -
   1.110 +    if (hint)
   1.111 +        free(desc);
   1.112      dev->name = handle;
   1.113      dev->iscapture = iscapture;
   1.114      dev->next = *pSeen;
   1.115 @@ -815,12 +834,15 @@
   1.116      ALSA_Device *dev;
   1.117      Uint32 ticks;
   1.118  
   1.119 +    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
   1.120 +
   1.121      while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
   1.122          void **hints = NULL;
   1.123 +        ALSA_Device *unseen;
   1.124 +        ALSA_Device *seen;
   1.125 +        ALSA_Device *prev;
   1.126 +
   1.127          if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
   1.128 -            ALSA_Device *unseen = devices;
   1.129 -            ALSA_Device *seen = NULL;
   1.130 -            ALSA_Device *prev;
   1.131              int i, j;
   1.132              const char *match = NULL;
   1.133              int bestmatch = 0xFFFF;
   1.134 @@ -830,6 +852,8 @@
   1.135                  "hw:", "sysdefault:", "default:", NULL
   1.136              };
   1.137  
   1.138 +            unseen = devices;
   1.139 +            seen = NULL;
   1.140              /* Apparently there are several different ways that ALSA lists
   1.141                 actual hardware. It could be prefixed with "hw:" or "default:"
   1.142                 or "sysdefault:" and maybe others. Go through the list and see
   1.143 @@ -924,7 +948,7 @@
   1.144  
   1.145              /* report anything still in unseen as removed. */
   1.146              for (dev = unseen; dev; dev = next) {
   1.147 -                /*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
   1.148 +                /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
   1.149                  next = dev->next;
   1.150                  SDL_RemoveAudioDevice(dev->iscapture, dev->name);
   1.151                  SDL_free(dev->name);