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