tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cubeb_alsa.c (40461B)


      1 /*
      2 * Copyright © 2011 Mozilla Foundation
      3 *
      4 * This program is made available under an ISC-style license.  See the
      5 * accompanying file LICENSE for details.
      6 */
      7 #undef NDEBUG
      8 #define _DEFAULT_SOURCE
      9 #define _BSD_SOURCE
     10 #if defined(__NetBSD__)
     11 #define _NETBSD_SOURCE /* timersub() */
     12 #endif
     13 #define _XOPEN_SOURCE 500
     14 #include "cubeb-internal.h"
     15 #include "cubeb/cubeb.h"
     16 #include "cubeb_tracing.h"
     17 #include <alsa/asoundlib.h>
     18 #include <assert.h>
     19 #include <dlfcn.h>
     20 #include <limits.h>
     21 #include <poll.h>
     22 #include <pthread.h>
     23 #include <sys/time.h>
     24 #include <unistd.h>
     25 
     26 #ifdef DISABLE_LIBASOUND_DLOPEN
     27 #define WRAP(x) x
     28 #else
     29 #define WRAP(x) (*cubeb_##x)
     30 #define LIBASOUND_API_VISIT(X)                                                 \
     31  X(snd_config)                                                                \
     32  X(snd_config_add)                                                            \
     33  X(snd_config_copy)                                                           \
     34  X(snd_config_delete)                                                         \
     35  X(snd_config_get_id)                                                         \
     36  X(snd_config_get_string)                                                     \
     37  X(snd_config_imake_integer)                                                  \
     38  X(snd_config_search)                                                         \
     39  X(snd_config_search_definition)                                              \
     40  X(snd_lib_error_set_handler)                                                 \
     41  X(snd_pcm_avail_update)                                                      \
     42  X(snd_pcm_close)                                                             \
     43  X(snd_pcm_delay)                                                             \
     44  X(snd_pcm_drain)                                                             \
     45  X(snd_pcm_frames_to_bytes)                                                   \
     46  X(snd_pcm_get_params)                                                        \
     47  X(snd_pcm_hw_params_any)                                                     \
     48  X(snd_pcm_hw_params_get_channels_max)                                        \
     49  X(snd_pcm_hw_params_get_rate)                                                \
     50  X(snd_pcm_hw_params_set_rate_near)                                           \
     51  X(snd_pcm_hw_params_sizeof)                                                  \
     52  X(snd_pcm_nonblock)                                                          \
     53  X(snd_pcm_open)                                                              \
     54  X(snd_pcm_open_lconf)                                                        \
     55  X(snd_pcm_pause)                                                             \
     56  X(snd_pcm_poll_descriptors)                                                  \
     57  X(snd_pcm_poll_descriptors_count)                                            \
     58  X(snd_pcm_poll_descriptors_revents)                                          \
     59  X(snd_pcm_readi)                                                             \
     60  X(snd_pcm_recover)                                                           \
     61  X(snd_pcm_set_params)                                                        \
     62  X(snd_pcm_start)                                                             \
     63  X(snd_pcm_state)                                                             \
     64  X(snd_pcm_writei)
     65 
     66 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
     67 LIBASOUND_API_VISIT(MAKE_TYPEDEF);
     68 #undef MAKE_TYPEDEF
     69 /* snd_pcm_hw_params_alloca is actually a macro */
     70 #define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
     71 #endif
     72 
     73 #define CUBEB_STREAM_MAX 16
     74 #define CUBEB_WATCHDOG_MS 10000
     75 
     76 #define CUBEB_ALSA_PCM_NAME "default"
     77 
     78 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
     79 
     80 /* ALSA is not thread-safe.  snd_pcm_t instances are individually protected
     81   by the owning cubeb_stream's mutex.  snd_pcm_t creation and destruction
     82   is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
     83   so those calls must be wrapped in the following mutex. */
     84 static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
     85 static int cubeb_alsa_error_handler_set = 0;
     86 
     87 static struct cubeb_ops const alsa_ops;
     88 
     89 struct cubeb {
     90  struct cubeb_ops const * ops;
     91  void * libasound;
     92 
     93  pthread_t thread;
     94 
     95  /* Mutex for streams array, must not be held while blocked in poll(2). */
     96  pthread_mutex_t mutex;
     97 
     98  /* Sparse array of streams managed by this context. */
     99  cubeb_stream * streams[CUBEB_STREAM_MAX];
    100 
    101  /* fds and nfds are only updated by alsa_run when rebuild is set. */
    102  struct pollfd * fds;
    103  nfds_t nfds;
    104  int rebuild;
    105 
    106  int shutdown;
    107 
    108  /* Control pipe for forcing poll to wake and rebuild fds or recalculate the
    109   * timeout. */
    110  int control_fd_read;
    111  int control_fd_write;
    112 
    113  /* Track number of active streams.  This is limited to CUBEB_STREAM_MAX
    114     due to resource contraints. */
    115  unsigned int active_streams;
    116 
    117  /* Local configuration with handle_underrun workaround set for PulseAudio
    118     ALSA plugin.  Will be NULL if the PA ALSA plugin is not in use or the
    119     workaround is not required. */
    120  snd_config_t * local_config;
    121  int is_pa;
    122 };
    123 
    124 enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };
    125 
    126 struct cubeb_stream {
    127  /* Note: Must match cubeb_stream layout in cubeb.c. */
    128  cubeb * context;
    129  void * user_ptr;
    130  /**/
    131  pthread_mutex_t mutex;
    132  snd_pcm_t * pcm;
    133  cubeb_data_callback data_callback;
    134  cubeb_state_callback state_callback;
    135  snd_pcm_uframes_t stream_position;
    136  snd_pcm_uframes_t last_position;
    137  snd_pcm_uframes_t buffer_size;
    138  cubeb_stream_params params;
    139 
    140  /* Every member after this comment is protected by the owning context's
    141     mutex rather than the stream's mutex, or is only used on the context's
    142     run thread. */
    143  pthread_cond_t cond; /* Signaled when the stream's state is changed. */
    144 
    145  enum stream_state state;
    146 
    147  struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
    148  struct pollfd *
    149      fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
    150  nfds_t nfds;
    151 
    152  struct timeval drain_timeout;
    153 
    154  /* XXX: Horrible hack -- if an active stream has been idle for
    155     CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
    156     called.  This works around a bug seen with older versions of ALSA and
    157     PulseAudio where streams would stop requesting new data despite still
    158     being logically active and playing. */
    159  struct timeval last_activity;
    160  float volume;
    161 
    162  char * buffer;
    163  snd_pcm_uframes_t bufframes;
    164  snd_pcm_stream_t stream_type;
    165 
    166  struct cubeb_stream * other_stream;
    167 };
    168 
    169 static int
    170 any_revents(struct pollfd * fds, nfds_t nfds)
    171 {
    172  nfds_t i;
    173 
    174  for (i = 0; i < nfds; ++i) {
    175    if (fds[i].revents) {
    176      return 1;
    177    }
    178  }
    179 
    180  return 0;
    181 }
    182 
    183 static int
    184 cmp_timeval(struct timeval * a, struct timeval * b)
    185 {
    186  if (a->tv_sec == b->tv_sec) {
    187    if (a->tv_usec == b->tv_usec) {
    188      return 0;
    189    }
    190    return a->tv_usec > b->tv_usec ? 1 : -1;
    191  }
    192  return a->tv_sec > b->tv_sec ? 1 : -1;
    193 }
    194 
    195 static int
    196 timeval_to_relative_ms(struct timeval * tv)
    197 {
    198  struct timeval now;
    199  struct timeval dt;
    200  long long t;
    201  int r;
    202 
    203  gettimeofday(&now, NULL);
    204  r = cmp_timeval(tv, &now);
    205  if (r >= 0) {
    206    timersub(tv, &now, &dt);
    207  } else {
    208    timersub(&now, tv, &dt);
    209  }
    210  t = dt.tv_sec;
    211  t *= 1000;
    212  t += (dt.tv_usec + 500) / 1000;
    213 
    214  if (t > INT_MAX) {
    215    t = INT_MAX;
    216  } else if (t < INT_MIN) {
    217    t = INT_MIN;
    218  }
    219 
    220  return r >= 0 ? t : -t;
    221 }
    222 
    223 static int
    224 ms_until(struct timeval * tv)
    225 {
    226  return timeval_to_relative_ms(tv);
    227 }
    228 
    229 static int
    230 ms_since(struct timeval * tv)
    231 {
    232  return -timeval_to_relative_ms(tv);
    233 }
    234 
    235 static void
    236 rebuild(cubeb * ctx)
    237 {
    238  nfds_t nfds;
    239  int i;
    240  nfds_t j;
    241  cubeb_stream * stm;
    242 
    243  assert(ctx->rebuild);
    244 
    245  /* Always count context's control pipe fd. */
    246  nfds = 1;
    247  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    248    stm = ctx->streams[i];
    249    if (stm) {
    250      stm->fds = NULL;
    251      if (stm->state == RUNNING) {
    252        nfds += stm->nfds;
    253      }
    254    }
    255  }
    256 
    257  free(ctx->fds);
    258  ctx->fds = calloc(nfds, sizeof(struct pollfd));
    259  assert(ctx->fds);
    260  ctx->nfds = nfds;
    261 
    262  /* Include context's control pipe fd. */
    263  ctx->fds[0].fd = ctx->control_fd_read;
    264  ctx->fds[0].events = POLLIN | POLLERR;
    265 
    266  for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) {
    267    stm = ctx->streams[i];
    268    if (stm && stm->state == RUNNING) {
    269      memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd));
    270      stm->fds = &ctx->fds[j];
    271      j += stm->nfds;
    272    }
    273  }
    274 
    275  ctx->rebuild = 0;
    276 }
    277 
    278 static void
    279 poll_wake(cubeb * ctx)
    280 {
    281  if (write(ctx->control_fd_write, "x", 1) < 0) {
    282    /* ignore write error */
    283  }
    284 }
    285 
    286 static void
    287 set_timeout(struct timeval * timeout, unsigned int ms)
    288 {
    289  gettimeofday(timeout, NULL);
    290  timeout->tv_sec += ms / 1000;
    291  timeout->tv_usec += (ms % 1000) * 1000;
    292 }
    293 
    294 static void
    295 stream_buffer_decrement(cubeb_stream * stm, long count)
    296 {
    297  char * bufremains =
    298      stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
    299  memmove(stm->buffer, bufremains,
    300          WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
    301  stm->bufframes -= count;
    302 }
    303 
    304 static void
    305 alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
    306 {
    307  cubeb * ctx;
    308  int r;
    309 
    310  ctx = stm->context;
    311  stm->state = state;
    312  r = pthread_cond_broadcast(&stm->cond);
    313  assert(r == 0);
    314  ctx->rebuild = 1;
    315  poll_wake(ctx);
    316 }
    317 
    318 static enum stream_state
    319 alsa_process_stream(cubeb_stream * stm)
    320 {
    321  unsigned short revents;
    322  snd_pcm_sframes_t avail;
    323  int draining;
    324 
    325  draining = 0;
    326 
    327  pthread_mutex_lock(&stm->mutex);
    328 
    329  /* Call _poll_descriptors_revents() even if we don't use it
    330     to let underlying plugins clear null events.  Otherwise poll()
    331     may wake up again and again, producing unnecessary CPU usage. */
    332  WRAP(snd_pcm_poll_descriptors_revents)
    333  (stm->pcm, stm->fds, stm->nfds, &revents);
    334 
    335  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
    336 
    337  /* Got null event? Bail and wait for another wakeup. */
    338  if (avail == 0) {
    339    pthread_mutex_unlock(&stm->mutex);
    340    return RUNNING;
    341  }
    342 
    343  /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
    344   */
    345  if ((unsigned int)avail > stm->buffer_size) {
    346    avail = stm->buffer_size;
    347  }
    348 
    349  /* Capture: Read available frames */
    350  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) {
    351    snd_pcm_sframes_t got;
    352 
    353    if (avail + stm->bufframes > stm->buffer_size) {
    354      /* Buffer overflow. Skip and overwrite with new data. */
    355      stm->bufframes = 0;
    356      // TODO: should it be marked as DRAINING?
    357    }
    358 
    359    got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);
    360 
    361    if (got < 0) {
    362      avail = got; // the error handler below will recover us
    363    } else {
    364      stm->bufframes += got;
    365      stm->stream_position += got;
    366 
    367      gettimeofday(&stm->last_activity, NULL);
    368    }
    369  }
    370 
    371  /* Capture: Pass read frames to callback function */
    372  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
    373      (!stm->other_stream ||
    374       stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
    375    snd_pcm_sframes_t wrote = stm->bufframes;
    376    struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
    377    void * other_buffer = stm->other_stream ? stm->other_stream->buffer +
    378                                                  stm->other_stream->bufframes
    379                                            : NULL;
    380 
    381    /* Correct write size to the other stream available space */
    382    if (stm->other_stream &&
    383        wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size -
    384                                    stm->other_stream->bufframes)) {
    385      wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
    386    }
    387 
    388    pthread_mutex_unlock(&stm->mutex);
    389    wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
    390                               other_buffer, wrote);
    391    pthread_mutex_lock(&stm->mutex);
    392 
    393    if (wrote < 0) {
    394      avail = wrote; // the error handler below will recover us
    395    } else {
    396      stream_buffer_decrement(stm, wrote);
    397 
    398      if (stm->other_stream) {
    399        stm->other_stream->bufframes += wrote;
    400      }
    401    }
    402  }
    403 
    404  /* Playback: Don't have enough data? Let's ask for more. */
    405  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
    406      avail > (snd_pcm_sframes_t)stm->bufframes &&
    407      (!stm->other_stream || stm->other_stream->bufframes > 0)) {
    408    long got = avail - stm->bufframes;
    409    void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
    410    char * buftail =
    411        stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
    412 
    413    /* Correct read size to the other stream available frames */
    414    if (stm->other_stream &&
    415        got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
    416      got = stm->other_stream->bufframes;
    417    }
    418 
    419    pthread_mutex_unlock(&stm->mutex);
    420    got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got);
    421    pthread_mutex_lock(&stm->mutex);
    422 
    423    if (got < 0) {
    424      avail = got; // the error handler below will recover us
    425    } else {
    426      stm->bufframes += got;
    427 
    428      if (stm->other_stream) {
    429        stream_buffer_decrement(stm->other_stream, got);
    430      }
    431    }
    432  }
    433 
    434  /* Playback: Still don't have enough data? Add some silence. */
    435  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
    436      avail > (snd_pcm_sframes_t)stm->bufframes) {
    437    long drain_frames = avail - stm->bufframes;
    438    double drain_time = (double)drain_frames / stm->params.rate;
    439 
    440    char * buftail =
    441        stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
    442    memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
    443    stm->bufframes = avail;
    444 
    445    /* Mark as draining, unless we're waiting for capture */
    446    if (!stm->other_stream || stm->other_stream->bufframes > 0) {
    447      set_timeout(&stm->drain_timeout, drain_time * 1000);
    448 
    449      draining = 1;
    450    }
    451  }
    452 
    453  /* Playback: Have enough data and no errors. Let's write it out. */
    454  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) {
    455    snd_pcm_sframes_t wrote;
    456 
    457    if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
    458      float * b = (float *)stm->buffer;
    459      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
    460        b[i] *= stm->volume;
    461      }
    462    } else {
    463      short * b = (short *)stm->buffer;
    464      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
    465        b[i] *= stm->volume;
    466      }
    467    }
    468 
    469    wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
    470    if (wrote < 0) {
    471      avail = wrote; // the error handler below will recover us
    472    } else {
    473      stream_buffer_decrement(stm, wrote);
    474 
    475      stm->stream_position += wrote;
    476      gettimeofday(&stm->last_activity, NULL);
    477    }
    478  }
    479 
    480  /* Got some error? Let's try to recover the stream. */
    481  if (avail < 0) {
    482    avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
    483 
    484    /* Capture pcm must be started after initial setup/recover */
    485    if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE &&
    486        WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
    487      avail = WRAP(snd_pcm_start)(stm->pcm);
    488    }
    489  }
    490 
    491  /* Failed to recover, this stream must be broken. */
    492  if (avail < 0) {
    493    pthread_mutex_unlock(&stm->mutex);
    494    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    495    return ERROR;
    496  }
    497 
    498  pthread_mutex_unlock(&stm->mutex);
    499  return draining ? DRAINING : RUNNING;
    500 }
    501 
    502 static int
    503 alsa_run(cubeb * ctx)
    504 {
    505  int r;
    506  int timeout;
    507  int i;
    508  char dummy;
    509  cubeb_stream * stm;
    510  enum stream_state state;
    511 
    512  pthread_mutex_lock(&ctx->mutex);
    513 
    514  if (ctx->rebuild) {
    515    rebuild(ctx);
    516  }
    517 
    518  /* Wake up at least once per second for the watchdog. */
    519  timeout = 1000;
    520  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    521    stm = ctx->streams[i];
    522    if (stm && stm->state == DRAINING) {
    523      r = ms_until(&stm->drain_timeout);
    524      if (r >= 0 && timeout > r) {
    525        timeout = r;
    526      }
    527    }
    528  }
    529 
    530  pthread_mutex_unlock(&ctx->mutex);
    531  r = poll(ctx->fds, ctx->nfds, timeout);
    532  pthread_mutex_lock(&ctx->mutex);
    533 
    534  if (r > 0) {
    535    if (ctx->fds[0].revents & POLLIN) {
    536      if (read(ctx->control_fd_read, &dummy, 1) < 0) {
    537        /* ignore read error */
    538      }
    539 
    540      if (ctx->shutdown) {
    541        pthread_mutex_unlock(&ctx->mutex);
    542        return -1;
    543      }
    544    }
    545 
    546    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    547      stm = ctx->streams[i];
    548      /* We can't use snd_pcm_poll_descriptors_revents here because of
    549         https://github.com/kinetiknz/cubeb/issues/135. */
    550      if (stm && stm->state == RUNNING && stm->fds &&
    551          any_revents(stm->fds, stm->nfds)) {
    552        alsa_set_stream_state(stm, PROCESSING);
    553        pthread_mutex_unlock(&ctx->mutex);
    554        state = alsa_process_stream(stm);
    555        pthread_mutex_lock(&ctx->mutex);
    556        alsa_set_stream_state(stm, state);
    557      }
    558    }
    559  } else if (r == 0) {
    560    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    561      stm = ctx->streams[i];
    562      if (stm) {
    563        if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
    564          alsa_set_stream_state(stm, INACTIVE);
    565          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    566        } else if (stm->state == RUNNING &&
    567                   ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
    568          alsa_set_stream_state(stm, ERROR);
    569          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    570        }
    571      }
    572    }
    573  }
    574 
    575  pthread_mutex_unlock(&ctx->mutex);
    576 
    577  return 0;
    578 }
    579 
    580 static void *
    581 alsa_run_thread(void * context)
    582 {
    583  cubeb * ctx = context;
    584  int r;
    585 
    586  CUBEB_REGISTER_THREAD("cubeb rendering thread");
    587 
    588  do {
    589    r = alsa_run(ctx);
    590  } while (r >= 0);
    591 
    592  CUBEB_UNREGISTER_THREAD();
    593 
    594  return NULL;
    595 }
    596 
    597 static snd_config_t *
    598 get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
    599 {
    600  int r;
    601  snd_config_t * slave_pcm;
    602  snd_config_t * slave_def;
    603  snd_config_t * pcm;
    604  char const * string;
    605  char node_name[64];
    606 
    607  slave_def = NULL;
    608 
    609  r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
    610  if (r < 0) {
    611    return NULL;
    612  }
    613 
    614  r = WRAP(snd_config_get_string)(slave_pcm, &string);
    615  if (r >= 0) {
    616    r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
    617                                           &slave_def);
    618    if (r < 0) {
    619      return NULL;
    620    }
    621  }
    622 
    623  do {
    624    r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
    625    if (r < 0) {
    626      break;
    627    }
    628 
    629    r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
    630    if (r < 0) {
    631      break;
    632    }
    633 
    634    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
    635    if (r < 0 || r > (int)sizeof(node_name)) {
    636      break;
    637    }
    638    r = WRAP(snd_config_search)(lconf, node_name, &pcm);
    639    if (r < 0) {
    640      break;
    641    }
    642 
    643    return pcm;
    644  } while (0);
    645 
    646  if (slave_def) {
    647    WRAP(snd_config_delete)(slave_def);
    648  }
    649 
    650  return NULL;
    651 }
    652 
    653 /* Work around PulseAudio ALSA plugin bug where the PA server forces a
    654   higher than requested latency, but the plugin does not update its (and
    655   ALSA's) internal state to reflect that, leading to an immediate underrun
    656   situation.  Inspired by WINE's make_handle_underrun_config.
    657   Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
    658 */
    659 static snd_config_t *
    660 init_local_config_with_workaround(char const * pcm_name)
    661 {
    662  int r;
    663  snd_config_t * lconf;
    664  snd_config_t * pcm_node;
    665  snd_config_t * node;
    666  char const * string;
    667  char node_name[64];
    668 
    669  lconf = NULL;
    670 
    671  if (WRAP(snd_config) == NULL) {
    672    return NULL;
    673  }
    674 
    675  r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
    676  if (r < 0) {
    677    return NULL;
    678  }
    679 
    680  do {
    681    r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
    682    if (r < 0) {
    683      break;
    684    }
    685 
    686    r = WRAP(snd_config_get_id)(pcm_node, &string);
    687    if (r < 0) {
    688      break;
    689    }
    690 
    691    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
    692    if (r < 0 || r > (int)sizeof(node_name)) {
    693      break;
    694    }
    695    r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
    696    if (r < 0) {
    697      break;
    698    }
    699 
    700    /* If this PCM has a slave, walk the slave configurations until we reach the
    701     * bottom. */
    702    while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
    703      pcm_node = node;
    704    }
    705 
    706    /* Fetch the PCM node's type, and bail out if it's not the PulseAudio
    707     * plugin. */
    708    r = WRAP(snd_config_search)(pcm_node, "type", &node);
    709    if (r < 0) {
    710      break;
    711    }
    712 
    713    r = WRAP(snd_config_get_string)(node, &string);
    714    if (r < 0) {
    715      break;
    716    }
    717 
    718    if (strcmp(string, "pulse") != 0) {
    719      break;
    720    }
    721 
    722    /* Don't clobber an explicit existing handle_underrun value, set it only
    723       if it doesn't already exist. */
    724    r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
    725    if (r != -ENOENT) {
    726      break;
    727    }
    728 
    729    /* Disable pcm_pulse's asynchronous underrun handling. */
    730    r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
    731    if (r < 0) {
    732      break;
    733    }
    734 
    735    r = WRAP(snd_config_add)(pcm_node, node);
    736    if (r < 0) {
    737      break;
    738    }
    739 
    740    return lconf;
    741  } while (0);
    742 
    743  WRAP(snd_config_delete)(lconf);
    744 
    745  return NULL;
    746 }
    747 
    748 static int
    749 alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
    750                     snd_pcm_stream_t stream, snd_config_t * local_config)
    751 {
    752  int r;
    753 
    754  pthread_mutex_lock(&cubeb_alsa_mutex);
    755  if (local_config) {
    756    r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
    757                                 local_config);
    758  } else {
    759    r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
    760  }
    761  pthread_mutex_unlock(&cubeb_alsa_mutex);
    762 
    763  return r;
    764 }
    765 
    766 static int
    767 alsa_locked_pcm_close(snd_pcm_t * pcm)
    768 {
    769  int r;
    770 
    771  pthread_mutex_lock(&cubeb_alsa_mutex);
    772  r = WRAP(snd_pcm_close)(pcm);
    773  pthread_mutex_unlock(&cubeb_alsa_mutex);
    774 
    775  return r;
    776 }
    777 
    778 static int
    779 alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
    780 {
    781  int i;
    782 
    783  pthread_mutex_lock(&ctx->mutex);
    784  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    785    if (!ctx->streams[i]) {
    786      ctx->streams[i] = stm;
    787      break;
    788    }
    789  }
    790  pthread_mutex_unlock(&ctx->mutex);
    791 
    792  return i == CUBEB_STREAM_MAX;
    793 }
    794 
    795 static void
    796 alsa_unregister_stream(cubeb_stream * stm)
    797 {
    798  cubeb * ctx;
    799  int i;
    800 
    801  ctx = stm->context;
    802 
    803  pthread_mutex_lock(&ctx->mutex);
    804  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    805    if (ctx->streams[i] == stm) {
    806      ctx->streams[i] = NULL;
    807      break;
    808    }
    809  }
    810  pthread_mutex_unlock(&ctx->mutex);
    811 }
    812 
    813 static void
    814 silent_error_handler(char const * file, int line, char const * function,
    815                     int err, char const * fmt, ...)
    816 {
    817  (void)file;
    818  (void)line;
    819  (void)function;
    820  (void)err;
    821  (void)fmt;
    822 }
    823 
    824 /*static*/ int
    825 alsa_init(cubeb ** context, char const * context_name)
    826 {
    827  (void)context_name;
    828  void * libasound = NULL;
    829  cubeb * ctx;
    830  int r;
    831  int i;
    832  int fd[2];
    833  pthread_attr_t attr;
    834  snd_pcm_t * dummy;
    835 
    836  assert(context);
    837  *context = NULL;
    838 
    839 #ifndef DISABLE_LIBASOUND_DLOPEN
    840  libasound = dlopen("libasound.so.2", RTLD_LAZY);
    841  if (!libasound) {
    842    libasound = dlopen("libasound.so", RTLD_LAZY);
    843    if (!libasound) {
    844      return CUBEB_ERROR;
    845    }
    846  }
    847 
    848 #define LOAD(x)                                                                \
    849  {                                                                            \
    850    cubeb_##x = dlsym(libasound, #x);                                          \
    851    if (!cubeb_##x) {                                                          \
    852      dlclose(libasound);                                                      \
    853      return CUBEB_ERROR;                                                      \
    854    }                                                                          \
    855  }
    856 
    857  LIBASOUND_API_VISIT(LOAD);
    858 #undef LOAD
    859 #endif
    860 
    861  pthread_mutex_lock(&cubeb_alsa_mutex);
    862  if (!cubeb_alsa_error_handler_set) {
    863    WRAP(snd_lib_error_set_handler)(silent_error_handler);
    864    cubeb_alsa_error_handler_set = 1;
    865  }
    866  pthread_mutex_unlock(&cubeb_alsa_mutex);
    867 
    868  ctx = calloc(1, sizeof(*ctx));
    869  assert(ctx);
    870 
    871  ctx->ops = &alsa_ops;
    872  ctx->libasound = libasound;
    873 
    874  r = pthread_mutex_init(&ctx->mutex, NULL);
    875  assert(r == 0);
    876 
    877  r = pipe(fd);
    878  assert(r == 0);
    879 
    880  for (i = 0; i < 2; ++i) {
    881    fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
    882    fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
    883  }
    884 
    885  ctx->control_fd_read = fd[0];
    886  ctx->control_fd_write = fd[1];
    887 
    888  /* Force an early rebuild when alsa_run is first called to ensure fds and
    889     nfds have been initialized. */
    890  ctx->rebuild = 1;
    891 
    892  r = pthread_attr_init(&attr);
    893  assert(r == 0);
    894 
    895  r = pthread_attr_setstacksize(&attr, 256 * 1024);
    896  assert(r == 0);
    897 
    898  r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
    899  assert(r == 0);
    900 
    901  r = pthread_attr_destroy(&attr);
    902  assert(r == 0);
    903 
    904  /* Open a dummy PCM to force the configuration space to be evaluated so that
    905     init_local_config_with_workaround can find and modify the default node. */
    906  r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
    907                           NULL);
    908  if (r >= 0) {
    909    alsa_locked_pcm_close(dummy);
    910  }
    911  ctx->is_pa = 0;
    912  pthread_mutex_lock(&cubeb_alsa_mutex);
    913  ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
    914  pthread_mutex_unlock(&cubeb_alsa_mutex);
    915  if (ctx->local_config) {
    916    ctx->is_pa = 1;
    917    r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME,
    918                             SND_PCM_STREAM_PLAYBACK, ctx->local_config);
    919    /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
    920       config fails with EINVAL, the PA PCM is too old for this workaround. */
    921    if (r == -EINVAL) {
    922      pthread_mutex_lock(&cubeb_alsa_mutex);
    923      WRAP(snd_config_delete)(ctx->local_config);
    924      pthread_mutex_unlock(&cubeb_alsa_mutex);
    925      ctx->local_config = NULL;
    926    } else if (r >= 0) {
    927      alsa_locked_pcm_close(dummy);
    928    }
    929  }
    930 
    931  *context = ctx;
    932 
    933  return CUBEB_OK;
    934 }
    935 
    936 static char const *
    937 alsa_get_backend_id(cubeb * ctx)
    938 {
    939  (void)ctx;
    940  return "alsa";
    941 }
    942 
    943 static void
    944 alsa_destroy(cubeb * ctx)
    945 {
    946  int r;
    947 
    948  assert(ctx);
    949 
    950  pthread_mutex_lock(&ctx->mutex);
    951  ctx->shutdown = 1;
    952  poll_wake(ctx);
    953  pthread_mutex_unlock(&ctx->mutex);
    954 
    955  r = pthread_join(ctx->thread, NULL);
    956  assert(r == 0);
    957 
    958  close(ctx->control_fd_read);
    959  close(ctx->control_fd_write);
    960  pthread_mutex_destroy(&ctx->mutex);
    961  free(ctx->fds);
    962 
    963  if (ctx->local_config) {
    964    pthread_mutex_lock(&cubeb_alsa_mutex);
    965    WRAP(snd_config_delete)(ctx->local_config);
    966    pthread_mutex_unlock(&cubeb_alsa_mutex);
    967  }
    968 #ifndef DISABLE_LIBASOUND_DLOPEN
    969  if (ctx->libasound) {
    970    dlclose(ctx->libasound);
    971  }
    972 #endif
    973  free(ctx);
    974 }
    975 
    976 static void
    977 alsa_stream_destroy(cubeb_stream * stm);
    978 
    979 static int
    980 alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
    981                        char const * stream_name, snd_pcm_stream_t stream_type,
    982                        cubeb_devid deviceid,
    983                        cubeb_stream_params * stream_params,
    984                        unsigned int latency_frames,
    985                        cubeb_data_callback data_callback,
    986                        cubeb_state_callback state_callback, void * user_ptr)
    987 {
    988  (void)stream_name;
    989  cubeb_stream * stm;
    990  int r;
    991  snd_pcm_format_t format;
    992  snd_pcm_uframes_t period_size;
    993  int latency_us = 0;
    994  char const * pcm_name =
    995      deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;
    996 
    997  assert(ctx && stream);
    998 
    999  *stream = NULL;
   1000 
   1001  if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
   1002    return CUBEB_ERROR_NOT_SUPPORTED;
   1003  }
   1004 
   1005  switch (stream_params->format) {
   1006  case CUBEB_SAMPLE_S16LE:
   1007    format = SND_PCM_FORMAT_S16_LE;
   1008    break;
   1009  case CUBEB_SAMPLE_S16BE:
   1010    format = SND_PCM_FORMAT_S16_BE;
   1011    break;
   1012  case CUBEB_SAMPLE_FLOAT32LE:
   1013    format = SND_PCM_FORMAT_FLOAT_LE;
   1014    break;
   1015  case CUBEB_SAMPLE_FLOAT32BE:
   1016    format = SND_PCM_FORMAT_FLOAT_BE;
   1017    break;
   1018  default:
   1019    return CUBEB_ERROR_INVALID_FORMAT;
   1020  }
   1021 
   1022  pthread_mutex_lock(&ctx->mutex);
   1023  if (ctx->active_streams >= CUBEB_STREAM_MAX) {
   1024    pthread_mutex_unlock(&ctx->mutex);
   1025    return CUBEB_ERROR;
   1026  }
   1027  ctx->active_streams += 1;
   1028  pthread_mutex_unlock(&ctx->mutex);
   1029 
   1030  stm = calloc(1, sizeof(*stm));
   1031  assert(stm);
   1032 
   1033  stm->context = ctx;
   1034  stm->data_callback = data_callback;
   1035  stm->state_callback = state_callback;
   1036  stm->user_ptr = user_ptr;
   1037  stm->params = *stream_params;
   1038  stm->state = INACTIVE;
   1039  stm->volume = 1.0;
   1040  stm->buffer = NULL;
   1041  stm->bufframes = 0;
   1042  stm->stream_type = stream_type;
   1043  stm->other_stream = NULL;
   1044 
   1045  r = pthread_mutex_init(&stm->mutex, NULL);
   1046  assert(r == 0);
   1047 
   1048  r = pthread_cond_init(&stm->cond, NULL);
   1049  assert(r == 0);
   1050 
   1051  r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type,
   1052                           ctx->local_config);
   1053  if (r < 0) {
   1054    alsa_stream_destroy(stm);
   1055    return CUBEB_ERROR;
   1056  }
   1057 
   1058  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
   1059  assert(r == 0);
   1060 
   1061  latency_us = latency_frames * 1e6 / stm->params.rate;
   1062 
   1063  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
   1064     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
   1065     Only resort to this hack if the handle_underrun workaround failed. */
   1066  if (!ctx->local_config && ctx->is_pa) {
   1067    const int min_latency = 5e5;
   1068    latency_us = latency_us < min_latency ? min_latency : latency_us;
   1069  }
   1070 
   1071  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
   1072                               stm->params.channels, stm->params.rate, 1,
   1073                               latency_us);
   1074  if (r < 0) {
   1075    alsa_stream_destroy(stm);
   1076    return CUBEB_ERROR_INVALID_FORMAT;
   1077  }
   1078 
   1079  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
   1080  assert(r == 0);
   1081 
   1082  /* Double internal buffer size to have enough space when waiting for the other
   1083   * side of duplex connection */
   1084  stm->buffer_size *= 2;
   1085  stm->buffer =
   1086      calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
   1087  assert(stm->buffer);
   1088 
   1089  stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
   1090  assert(stm->nfds > 0);
   1091 
   1092  stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
   1093  assert(stm->saved_fds);
   1094  r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
   1095  assert((nfds_t)r == stm->nfds);
   1096 
   1097  if (alsa_register_stream(ctx, stm) != 0) {
   1098    alsa_stream_destroy(stm);
   1099    return CUBEB_ERROR;
   1100  }
   1101 
   1102  *stream = stm;
   1103 
   1104  return CUBEB_OK;
   1105 }
   1106 
   1107 static int
   1108 alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
   1109                 cubeb_devid input_device,
   1110                 cubeb_stream_params * input_stream_params,
   1111                 cubeb_devid output_device,
   1112                 cubeb_stream_params * output_stream_params,
   1113                 unsigned int latency_frames, cubeb_data_callback data_callback,
   1114                 cubeb_state_callback state_callback, void * user_ptr)
   1115 {
   1116  int result = CUBEB_OK;
   1117  cubeb_stream *instm = NULL, *outstm = NULL;
   1118 
   1119  if (result == CUBEB_OK && input_stream_params) {
   1120    result = alsa_stream_init_single(ctx, &instm, stream_name,
   1121                                     SND_PCM_STREAM_CAPTURE, input_device,
   1122                                     input_stream_params, latency_frames,
   1123                                     data_callback, state_callback, user_ptr);
   1124  }
   1125 
   1126  if (result == CUBEB_OK && output_stream_params) {
   1127    result = alsa_stream_init_single(ctx, &outstm, stream_name,
   1128                                     SND_PCM_STREAM_PLAYBACK, output_device,
   1129                                     output_stream_params, latency_frames,
   1130                                     data_callback, state_callback, user_ptr);
   1131  }
   1132 
   1133  if (result == CUBEB_OK && input_stream_params && output_stream_params) {
   1134    instm->other_stream = outstm;
   1135    outstm->other_stream = instm;
   1136  }
   1137 
   1138  if (result != CUBEB_OK && instm) {
   1139    alsa_stream_destroy(instm);
   1140  }
   1141 
   1142  *stream = outstm ? outstm : instm;
   1143 
   1144  return result;
   1145 }
   1146 
   1147 static void
   1148 alsa_stream_destroy(cubeb_stream * stm)
   1149 {
   1150  int r;
   1151  cubeb * ctx;
   1152 
   1153  assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
   1154                 stm->state == DRAINING));
   1155 
   1156  ctx = stm->context;
   1157 
   1158  if (stm->other_stream) {
   1159    stm->other_stream->other_stream = NULL; // to stop infinite recursion
   1160    alsa_stream_destroy(stm->other_stream);
   1161  }
   1162 
   1163  pthread_mutex_lock(&stm->mutex);
   1164  if (stm->pcm) {
   1165    if (stm->state == DRAINING) {
   1166      WRAP(snd_pcm_drain)(stm->pcm);
   1167    }
   1168    alsa_locked_pcm_close(stm->pcm);
   1169    stm->pcm = NULL;
   1170  }
   1171  free(stm->saved_fds);
   1172  pthread_mutex_unlock(&stm->mutex);
   1173  pthread_mutex_destroy(&stm->mutex);
   1174 
   1175  r = pthread_cond_destroy(&stm->cond);
   1176  assert(r == 0);
   1177 
   1178  alsa_unregister_stream(stm);
   1179 
   1180  pthread_mutex_lock(&ctx->mutex);
   1181  assert(ctx->active_streams >= 1);
   1182  ctx->active_streams -= 1;
   1183  pthread_mutex_unlock(&ctx->mutex);
   1184 
   1185  free(stm->buffer);
   1186 
   1187  free(stm);
   1188 }
   1189 
   1190 static int
   1191 alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
   1192 {
   1193  int r;
   1194  cubeb_stream * stm;
   1195  snd_pcm_hw_params_t * hw_params;
   1196  cubeb_stream_params params;
   1197  params.rate = 44100;
   1198  params.format = CUBEB_SAMPLE_FLOAT32NE;
   1199  params.channels = 2;
   1200 
   1201  snd_pcm_hw_params_alloca(&hw_params);
   1202 
   1203  assert(ctx);
   1204 
   1205  r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL,
   1206                       NULL, NULL);
   1207  if (r != CUBEB_OK) {
   1208    return CUBEB_ERROR;
   1209  }
   1210 
   1211  assert(stm);
   1212 
   1213  r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
   1214  if (r < 0) {
   1215    return CUBEB_ERROR;
   1216  }
   1217 
   1218  r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
   1219  if (r < 0) {
   1220    return CUBEB_ERROR;
   1221  }
   1222 
   1223  alsa_stream_destroy(stm);
   1224 
   1225  return CUBEB_OK;
   1226 }
   1227 
   1228 static int
   1229 alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
   1230 {
   1231  (void)ctx;
   1232  int r, dir;
   1233  snd_pcm_t * pcm;
   1234  snd_pcm_hw_params_t * hw_params;
   1235 
   1236  snd_pcm_hw_params_alloca(&hw_params);
   1237 
   1238  /* get a pcm, disabling resampling, so we get a rate the
   1239   * hardware/dmix/pulse/etc. supports. */
   1240  r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
   1241                         SND_PCM_NO_AUTO_RESAMPLE);
   1242  if (r < 0) {
   1243    return CUBEB_ERROR;
   1244  }
   1245 
   1246  r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
   1247  if (r < 0) {
   1248    WRAP(snd_pcm_close)(pcm);
   1249    return CUBEB_ERROR;
   1250  }
   1251 
   1252  r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
   1253  if (r >= 0) {
   1254    /* There is a default rate: use it. */
   1255    WRAP(snd_pcm_close)(pcm);
   1256    return CUBEB_OK;
   1257  }
   1258 
   1259  /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
   1260  *rate = 44100;
   1261 
   1262  r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
   1263  if (r < 0) {
   1264    WRAP(snd_pcm_close)(pcm);
   1265    return CUBEB_ERROR;
   1266  }
   1267 
   1268  WRAP(snd_pcm_close)(pcm);
   1269 
   1270  return CUBEB_OK;
   1271 }
   1272 
   1273 static int
   1274 alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
   1275                     uint32_t * latency_frames)
   1276 {
   1277  (void)ctx;
   1278  /* 40ms is found to be an acceptable minimum, even on a super low-end
   1279   * machine. */
   1280  *latency_frames = 40 * params.rate / 1000;
   1281 
   1282  return CUBEB_OK;
   1283 }
   1284 
   1285 static int
   1286 alsa_stream_start(cubeb_stream * stm)
   1287 {
   1288  cubeb * ctx;
   1289 
   1290  assert(stm);
   1291  ctx = stm->context;
   1292 
   1293  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
   1294    int r = alsa_stream_start(stm->other_stream);
   1295    if (r != CUBEB_OK)
   1296      return r;
   1297  }
   1298 
   1299  pthread_mutex_lock(&stm->mutex);
   1300  /* Capture pcm must be started after initial setup/recover */
   1301  if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
   1302      WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
   1303    WRAP(snd_pcm_start)(stm->pcm);
   1304  }
   1305  WRAP(snd_pcm_pause)(stm->pcm, 0);
   1306  gettimeofday(&stm->last_activity, NULL);
   1307  pthread_mutex_unlock(&stm->mutex);
   1308 
   1309  pthread_mutex_lock(&ctx->mutex);
   1310  if (stm->state != INACTIVE) {
   1311    pthread_mutex_unlock(&ctx->mutex);
   1312    return CUBEB_ERROR;
   1313  }
   1314  alsa_set_stream_state(stm, RUNNING);
   1315  pthread_mutex_unlock(&ctx->mutex);
   1316 
   1317  return CUBEB_OK;
   1318 }
   1319 
   1320 static int
   1321 alsa_stream_stop(cubeb_stream * stm)
   1322 {
   1323  cubeb * ctx;
   1324  int r;
   1325 
   1326  assert(stm);
   1327  ctx = stm->context;
   1328 
   1329  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
   1330    int r = alsa_stream_stop(stm->other_stream);
   1331    if (r != CUBEB_OK)
   1332      return r;
   1333  }
   1334 
   1335  pthread_mutex_lock(&ctx->mutex);
   1336  while (stm->state == PROCESSING) {
   1337    r = pthread_cond_wait(&stm->cond, &ctx->mutex);
   1338    assert(r == 0);
   1339  }
   1340 
   1341  alsa_set_stream_state(stm, INACTIVE);
   1342  pthread_mutex_unlock(&ctx->mutex);
   1343 
   1344  pthread_mutex_lock(&stm->mutex);
   1345  WRAP(snd_pcm_pause)(stm->pcm, 1);
   1346  pthread_mutex_unlock(&stm->mutex);
   1347 
   1348  return CUBEB_OK;
   1349 }
   1350 
   1351 static int
   1352 alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
   1353 {
   1354  snd_pcm_sframes_t delay;
   1355 
   1356  assert(stm && position);
   1357 
   1358  pthread_mutex_lock(&stm->mutex);
   1359 
   1360  delay = -1;
   1361  if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
   1362      WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
   1363    *position = stm->last_position;
   1364    pthread_mutex_unlock(&stm->mutex);
   1365    return CUBEB_OK;
   1366  }
   1367 
   1368  assert(delay >= 0);
   1369 
   1370  *position = 0;
   1371  if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
   1372    *position = stm->stream_position - delay;
   1373  }
   1374 
   1375  stm->last_position = *position;
   1376 
   1377  pthread_mutex_unlock(&stm->mutex);
   1378  return CUBEB_OK;
   1379 }
   1380 
   1381 static int
   1382 alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
   1383 {
   1384  snd_pcm_sframes_t delay;
   1385  /* This function returns the delay in frames until a frame written using
   1386     snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
   1387   */
   1388  if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
   1389    return CUBEB_ERROR;
   1390  }
   1391 
   1392  *latency = delay;
   1393 
   1394  return CUBEB_OK;
   1395 }
   1396 
   1397 static int
   1398 alsa_stream_set_volume(cubeb_stream * stm, float volume)
   1399 {
   1400  /* setting the volume using an API call does not seem very stable/supported */
   1401  pthread_mutex_lock(&stm->mutex);
   1402  stm->volume = volume;
   1403  pthread_mutex_unlock(&stm->mutex);
   1404 
   1405  return CUBEB_OK;
   1406 }
   1407 
   1408 static int
   1409 alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
   1410                       cubeb_device_collection * collection)
   1411 {
   1412  cubeb_device_info * device = NULL;
   1413 
   1414  if (!context)
   1415    return CUBEB_ERROR;
   1416 
   1417  uint32_t rate, max_channels;
   1418  int r;
   1419 
   1420  r = alsa_get_preferred_sample_rate(context, &rate);
   1421  if (r != CUBEB_OK) {
   1422    return CUBEB_ERROR;
   1423  }
   1424 
   1425  r = alsa_get_max_channel_count(context, &max_channels);
   1426  if (r != CUBEB_OK) {
   1427    return CUBEB_ERROR;
   1428  }
   1429 
   1430  char const * a_name = "default";
   1431  device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
   1432  assert(device);
   1433  if (!device)
   1434    return CUBEB_ERROR;
   1435 
   1436  device->device_id = a_name;
   1437  device->devid = (cubeb_devid)device->device_id;
   1438  device->friendly_name = a_name;
   1439  device->group_id = a_name;
   1440  device->vendor_name = a_name;
   1441  device->type = type;
   1442  device->state = CUBEB_DEVICE_STATE_ENABLED;
   1443  device->preferred = CUBEB_DEVICE_PREF_ALL;
   1444  device->format = CUBEB_DEVICE_FMT_S16NE;
   1445  device->default_format = CUBEB_DEVICE_FMT_S16NE;
   1446  device->max_channels = max_channels;
   1447  device->min_rate = rate;
   1448  device->max_rate = rate;
   1449  device->default_rate = rate;
   1450  device->latency_lo = 0;
   1451  device->latency_hi = 0;
   1452 
   1453  collection->device = device;
   1454  collection->count = 1;
   1455 
   1456  return CUBEB_OK;
   1457 }
   1458 
   1459 static int
   1460 alsa_device_collection_destroy(cubeb * context,
   1461                               cubeb_device_collection * collection)
   1462 {
   1463  assert(collection->count == 1);
   1464  (void)context;
   1465  free(collection->device);
   1466  return CUBEB_OK;
   1467 }
   1468 
   1469 static struct cubeb_ops const alsa_ops = {
   1470    .init = alsa_init,
   1471    .get_backend_id = alsa_get_backend_id,
   1472    .get_max_channel_count = alsa_get_max_channel_count,
   1473    .get_min_latency = alsa_get_min_latency,
   1474    .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
   1475    .get_supported_input_processing_params = NULL,
   1476    .enumerate_devices = alsa_enumerate_devices,
   1477    .device_collection_destroy = alsa_device_collection_destroy,
   1478    .destroy = alsa_destroy,
   1479    .stream_init = alsa_stream_init,
   1480    .stream_destroy = alsa_stream_destroy,
   1481    .stream_start = alsa_stream_start,
   1482    .stream_stop = alsa_stream_stop,
   1483    .stream_get_position = alsa_stream_get_position,
   1484    .stream_get_latency = alsa_stream_get_latency,
   1485    .stream_get_input_latency = NULL,
   1486    .stream_set_volume = alsa_stream_set_volume,
   1487    .stream_set_name = NULL,
   1488    .stream_get_current_device = NULL,
   1489    .stream_set_input_mute = NULL,
   1490    .stream_set_input_processing_params = NULL,
   1491    .stream_device_destroy = NULL,
   1492    .stream_register_device_changed_callback = NULL,
   1493    .register_device_collection_changed = NULL};