duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.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};