Merge autoland to mozilla central a=merge
[gecko.git] / media / libcubeb / src / cubeb_alsa.c
blobf114f27d7beef9b80cfcbf12a84034c5dd456d19
1 /*
2 * Copyright © 2011 Mozilla Foundation
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>
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)
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
73 #define CUBEB_STREAM_MAX 16
74 #define CUBEB_WATCHDOG_MS 10000
76 #define CUBEB_ALSA_PCM_NAME "default"
78 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
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;
87 static struct cubeb_ops const alsa_ops;
89 struct cubeb {
90 struct cubeb_ops const * ops;
91 void * libasound;
93 pthread_t thread;
95 /* Mutex for streams array, must not be held while blocked in poll(2). */
96 pthread_mutex_t mutex;
98 /* Sparse array of streams managed by this context. */
99 cubeb_stream * streams[CUBEB_STREAM_MAX];
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;
106 int shutdown;
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;
113 /* Track number of active streams. This is limited to CUBEB_STREAM_MAX
114 due to resource contraints. */
115 unsigned int active_streams;
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;
124 enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };
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;
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. */
145 enum stream_state state;
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;
152 struct timeval drain_timeout;
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;
162 char * buffer;
163 snd_pcm_uframes_t bufframes;
164 snd_pcm_stream_t stream_type;
166 struct cubeb_stream * other_stream;
169 static int
170 any_revents(struct pollfd * fds, nfds_t nfds)
172 nfds_t i;
174 for (i = 0; i < nfds; ++i) {
175 if (fds[i].revents) {
176 return 1;
180 return 0;
183 static int
184 cmp_timeval(struct timeval * a, struct timeval * b)
186 if (a->tv_sec == b->tv_sec) {
187 if (a->tv_usec == b->tv_usec) {
188 return 0;
190 return a->tv_usec > b->tv_usec ? 1 : -1;
192 return a->tv_sec > b->tv_sec ? 1 : -1;
195 static int
196 timeval_to_relative_ms(struct timeval * tv)
198 struct timeval now;
199 struct timeval dt;
200 long long t;
201 int r;
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);
210 t = dt.tv_sec;
211 t *= 1000;
212 t += (dt.tv_usec + 500) / 1000;
214 if (t > INT_MAX) {
215 t = INT_MAX;
216 } else if (t < INT_MIN) {
217 t = INT_MIN;
220 return r >= 0 ? t : -t;
223 static int
224 ms_until(struct timeval * tv)
226 return timeval_to_relative_ms(tv);
229 static int
230 ms_since(struct timeval * tv)
232 return -timeval_to_relative_ms(tv);
235 static void
236 rebuild(cubeb * ctx)
238 nfds_t nfds;
239 int i;
240 nfds_t j;
241 cubeb_stream * stm;
243 assert(ctx->rebuild);
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;
257 free(ctx->fds);
258 ctx->fds = calloc(nfds, sizeof(struct pollfd));
259 assert(ctx->fds);
260 ctx->nfds = nfds;
262 /* Include context's control pipe fd. */
263 ctx->fds[0].fd = ctx->control_fd_read;
264 ctx->fds[0].events = POLLIN | POLLERR;
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;
275 ctx->rebuild = 0;
278 static void
279 poll_wake(cubeb * ctx)
281 if (write(ctx->control_fd_write, "x", 1) < 0) {
282 /* ignore write error */
286 static void
287 set_timeout(struct timeval * timeout, unsigned int ms)
289 gettimeofday(timeout, NULL);
290 timeout->tv_sec += ms / 1000;
291 timeout->tv_usec += (ms % 1000) * 1000;
294 static void
295 stream_buffer_decrement(cubeb_stream * stm, long count)
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;
304 static void
305 alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
307 cubeb * ctx;
308 int r;
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);
318 static enum stream_state
319 alsa_process_stream(cubeb_stream * stm)
321 unsigned short revents;
322 snd_pcm_sframes_t avail;
323 int draining;
325 draining = 0;
327 pthread_mutex_lock(&stm->mutex);
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);
335 avail = WRAP(snd_pcm_avail_update)(stm->pcm);
337 /* Got null event? Bail and wait for another wakeup. */
338 if (avail == 0) {
339 pthread_mutex_unlock(&stm->mutex);
340 return RUNNING;
343 /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
345 if ((unsigned int)avail > stm->buffer_size) {
346 avail = stm->buffer_size;
349 /* Capture: Read available frames */
350 if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) {
351 snd_pcm_sframes_t got;
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?
359 got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);
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;
367 gettimeofday(&stm->last_activity, NULL);
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;
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;
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);
393 if (wrote < 0) {
394 avail = wrote; // the error handler below will recover us
395 } else {
396 stream_buffer_decrement(stm, wrote);
398 if (stm->other_stream) {
399 stm->other_stream->bufframes += wrote;
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);
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;
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);
423 if (got < 0) {
424 avail = got; // the error handler below will recover us
425 } else {
426 stm->bufframes += got;
428 if (stm->other_stream) {
429 stream_buffer_decrement(stm->other_stream, got);
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;
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;
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);
449 draining = 1;
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;
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;
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;
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);
475 stm->stream_position += wrote;
476 gettimeofday(&stm->last_activity, NULL);
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);
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);
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;
498 pthread_mutex_unlock(&stm->mutex);
499 return draining ? DRAINING : RUNNING;
502 static int
503 alsa_run(cubeb * ctx)
505 int r;
506 int timeout;
507 int i;
508 char dummy;
509 cubeb_stream * stm;
510 enum stream_state state;
512 pthread_mutex_lock(&ctx->mutex);
514 if (ctx->rebuild) {
515 rebuild(ctx);
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;
530 pthread_mutex_unlock(&ctx->mutex);
531 r = poll(ctx->fds, ctx->nfds, timeout);
532 pthread_mutex_lock(&ctx->mutex);
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 */
540 if (ctx->shutdown) {
541 pthread_mutex_unlock(&ctx->mutex);
542 return -1;
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);
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);
575 pthread_mutex_unlock(&ctx->mutex);
577 return 0;
580 static void *
581 alsa_run_thread(void * context)
583 cubeb * ctx = context;
584 int r;
586 CUBEB_REGISTER_THREAD("cubeb rendering thread");
588 do {
589 r = alsa_run(ctx);
590 } while (r >= 0);
592 CUBEB_UNREGISTER_THREAD();
594 return NULL;
597 static snd_config_t *
598 get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
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];
607 slave_def = NULL;
609 r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
610 if (r < 0) {
611 return NULL;
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;
623 do {
624 r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
625 if (r < 0) {
626 break;
629 r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
630 if (r < 0) {
631 break;
634 r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
635 if (r < 0 || r > (int)sizeof(node_name)) {
636 break;
638 r = WRAP(snd_config_search)(lconf, node_name, &pcm);
639 if (r < 0) {
640 break;
643 return pcm;
644 } while (0);
646 if (slave_def) {
647 WRAP(snd_config_delete)(slave_def);
650 return NULL;
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
659 static snd_config_t *
660 init_local_config_with_workaround(char const * pcm_name)
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];
669 lconf = NULL;
671 if (WRAP(snd_config) == NULL) {
672 return NULL;
675 r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
676 if (r < 0) {
677 return NULL;
680 do {
681 r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
682 if (r < 0) {
683 break;
686 r = WRAP(snd_config_get_id)(pcm_node, &string);
687 if (r < 0) {
688 break;
691 r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
692 if (r < 0 || r > (int)sizeof(node_name)) {
693 break;
695 r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
696 if (r < 0) {
697 break;
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;
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;
713 r = WRAP(snd_config_get_string)(node, &string);
714 if (r < 0) {
715 break;
718 if (strcmp(string, "pulse") != 0) {
719 break;
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;
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;
735 r = WRAP(snd_config_add)(pcm_node, node);
736 if (r < 0) {
737 break;
740 return lconf;
741 } while (0);
743 WRAP(snd_config_delete)(lconf);
745 return NULL;
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)
752 int r;
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);
761 pthread_mutex_unlock(&cubeb_alsa_mutex);
763 return r;
766 static int
767 alsa_locked_pcm_close(snd_pcm_t * pcm)
769 int r;
771 pthread_mutex_lock(&cubeb_alsa_mutex);
772 r = WRAP(snd_pcm_close)(pcm);
773 pthread_mutex_unlock(&cubeb_alsa_mutex);
775 return r;
778 static int
779 alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
781 int i;
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;
790 pthread_mutex_unlock(&ctx->mutex);
792 return i == CUBEB_STREAM_MAX;
795 static void
796 alsa_unregister_stream(cubeb_stream * stm)
798 cubeb * ctx;
799 int i;
801 ctx = stm->context;
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;
810 pthread_mutex_unlock(&ctx->mutex);
813 static void
814 silent_error_handler(char const * file, int line, char const * function,
815 int err, char const * fmt, ...)
817 (void)file;
818 (void)line;
819 (void)function;
820 (void)err;
821 (void)fmt;
824 /*static*/ int
825 alsa_init(cubeb ** context, char const * context_name)
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;
836 assert(context);
837 *context = NULL;
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;
848 #define LOAD(x) \
850 cubeb_##x = dlsym(libasound, #x); \
851 if (!cubeb_##x) { \
852 dlclose(libasound); \
853 return CUBEB_ERROR; \
857 LIBASOUND_API_VISIT(LOAD);
858 #undef LOAD
859 #endif
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;
866 pthread_mutex_unlock(&cubeb_alsa_mutex);
868 ctx = calloc(1, sizeof(*ctx));
869 assert(ctx);
871 ctx->ops = &alsa_ops;
872 ctx->libasound = libasound;
874 r = pthread_mutex_init(&ctx->mutex, NULL);
875 assert(r == 0);
877 r = pipe(fd);
878 assert(r == 0);
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);
885 ctx->control_fd_read = fd[0];
886 ctx->control_fd_write = fd[1];
888 /* Force an early rebuild when alsa_run is first called to ensure fds and
889 nfds have been initialized. */
890 ctx->rebuild = 1;
892 r = pthread_attr_init(&attr);
893 assert(r == 0);
895 r = pthread_attr_setstacksize(&attr, 256 * 1024);
896 assert(r == 0);
898 r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
899 assert(r == 0);
901 r = pthread_attr_destroy(&attr);
902 assert(r == 0);
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);
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);
931 *context = ctx;
933 return CUBEB_OK;
936 static char const *
937 alsa_get_backend_id(cubeb * ctx)
939 (void)ctx;
940 return "alsa";
943 static void
944 alsa_destroy(cubeb * ctx)
946 int r;
948 assert(ctx);
950 pthread_mutex_lock(&ctx->mutex);
951 ctx->shutdown = 1;
952 poll_wake(ctx);
953 pthread_mutex_unlock(&ctx->mutex);
955 r = pthread_join(ctx->thread, NULL);
956 assert(r == 0);
958 close(ctx->control_fd_read);
959 close(ctx->control_fd_write);
960 pthread_mutex_destroy(&ctx->mutex);
961 free(ctx->fds);
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);
968 #ifndef DISABLE_LIBASOUND_DLOPEN
969 if (ctx->libasound) {
970 dlclose(ctx->libasound);
972 #endif
973 free(ctx);
976 static void
977 alsa_stream_destroy(cubeb_stream * stm);
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)
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;
997 assert(ctx && stream);
999 *stream = NULL;
1001 if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
1002 return CUBEB_ERROR_NOT_SUPPORTED;
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;
1022 pthread_mutex_lock(&ctx->mutex);
1023 if (ctx->active_streams >= CUBEB_STREAM_MAX) {
1024 pthread_mutex_unlock(&ctx->mutex);
1025 return CUBEB_ERROR;
1027 ctx->active_streams += 1;
1028 pthread_mutex_unlock(&ctx->mutex);
1030 stm = calloc(1, sizeof(*stm));
1031 assert(stm);
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;
1045 r = pthread_mutex_init(&stm->mutex, NULL);
1046 assert(r == 0);
1048 r = pthread_cond_init(&stm->cond, NULL);
1049 assert(r == 0);
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;
1058 r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
1059 assert(r == 0);
1061 latency_us = latency_frames * 1e6 / stm->params.rate;
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;
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;
1079 r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
1080 assert(r == 0);
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);
1089 stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
1090 assert(stm->nfds > 0);
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);
1097 if (alsa_register_stream(ctx, stm) != 0) {
1098 alsa_stream_destroy(stm);
1099 return CUBEB_ERROR;
1102 *stream = stm;
1104 return CUBEB_OK;
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)
1116 int result = CUBEB_OK;
1117 cubeb_stream *instm = NULL, *outstm = NULL;
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);
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);
1133 if (result == CUBEB_OK && input_stream_params && output_stream_params) {
1134 instm->other_stream = outstm;
1135 outstm->other_stream = instm;
1138 if (result != CUBEB_OK && instm) {
1139 alsa_stream_destroy(instm);
1142 *stream = outstm ? outstm : instm;
1144 return result;
1147 static void
1148 alsa_stream_destroy(cubeb_stream * stm)
1150 int r;
1151 cubeb * ctx;
1153 assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
1154 stm->state == DRAINING));
1156 ctx = stm->context;
1158 if (stm->other_stream) {
1159 stm->other_stream->other_stream = NULL; // to stop infinite recursion
1160 alsa_stream_destroy(stm->other_stream);
1163 pthread_mutex_lock(&stm->mutex);
1164 if (stm->pcm) {
1165 if (stm->state == DRAINING) {
1166 WRAP(snd_pcm_drain)(stm->pcm);
1168 alsa_locked_pcm_close(stm->pcm);
1169 stm->pcm = NULL;
1171 free(stm->saved_fds);
1172 pthread_mutex_unlock(&stm->mutex);
1173 pthread_mutex_destroy(&stm->mutex);
1175 r = pthread_cond_destroy(&stm->cond);
1176 assert(r == 0);
1178 alsa_unregister_stream(stm);
1180 pthread_mutex_lock(&ctx->mutex);
1181 assert(ctx->active_streams >= 1);
1182 ctx->active_streams -= 1;
1183 pthread_mutex_unlock(&ctx->mutex);
1185 free(stm->buffer);
1187 free(stm);
1190 static int
1191 alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
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;
1201 snd_pcm_hw_params_alloca(&hw_params);
1203 assert(ctx);
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;
1211 assert(stm);
1213 r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
1214 if (r < 0) {
1215 return CUBEB_ERROR;
1218 r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
1219 if (r < 0) {
1220 return CUBEB_ERROR;
1223 alsa_stream_destroy(stm);
1225 return CUBEB_OK;
1228 static int
1229 alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
1231 (void)ctx;
1232 int r, dir;
1233 snd_pcm_t * pcm;
1234 snd_pcm_hw_params_t * hw_params;
1236 snd_pcm_hw_params_alloca(&hw_params);
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;
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;
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;
1259 /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
1260 *rate = 44100;
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;
1268 WRAP(snd_pcm_close)(pcm);
1270 return CUBEB_OK;
1273 static int
1274 alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
1275 uint32_t * latency_frames)
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;
1282 return CUBEB_OK;
1285 static int
1286 alsa_stream_start(cubeb_stream * stm)
1288 cubeb * ctx;
1290 assert(stm);
1291 ctx = stm->context;
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;
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);
1305 WRAP(snd_pcm_pause)(stm->pcm, 0);
1306 gettimeofday(&stm->last_activity, NULL);
1307 pthread_mutex_unlock(&stm->mutex);
1309 pthread_mutex_lock(&ctx->mutex);
1310 if (stm->state != INACTIVE) {
1311 pthread_mutex_unlock(&ctx->mutex);
1312 return CUBEB_ERROR;
1314 alsa_set_stream_state(stm, RUNNING);
1315 pthread_mutex_unlock(&ctx->mutex);
1317 return CUBEB_OK;
1320 static int
1321 alsa_stream_stop(cubeb_stream * stm)
1323 cubeb * ctx;
1324 int r;
1326 assert(stm);
1327 ctx = stm->context;
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;
1335 pthread_mutex_lock(&ctx->mutex);
1336 while (stm->state == PROCESSING) {
1337 r = pthread_cond_wait(&stm->cond, &ctx->mutex);
1338 assert(r == 0);
1341 alsa_set_stream_state(stm, INACTIVE);
1342 pthread_mutex_unlock(&ctx->mutex);
1344 pthread_mutex_lock(&stm->mutex);
1345 WRAP(snd_pcm_pause)(stm->pcm, 1);
1346 pthread_mutex_unlock(&stm->mutex);
1348 return CUBEB_OK;
1351 static int
1352 alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
1354 snd_pcm_sframes_t delay;
1356 assert(stm && position);
1358 pthread_mutex_lock(&stm->mutex);
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;
1368 assert(delay >= 0);
1370 *position = 0;
1371 if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
1372 *position = stm->stream_position - delay;
1375 stm->last_position = *position;
1377 pthread_mutex_unlock(&stm->mutex);
1378 return CUBEB_OK;
1381 static int
1382 alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
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.
1388 if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
1389 return CUBEB_ERROR;
1392 *latency = delay;
1394 return CUBEB_OK;
1397 static int
1398 alsa_stream_set_volume(cubeb_stream * stm, float volume)
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);
1405 return CUBEB_OK;
1408 static int
1409 alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
1410 cubeb_device_collection * collection)
1412 cubeb_device_info * device = NULL;
1414 if (!context)
1415 return CUBEB_ERROR;
1417 uint32_t rate, max_channels;
1418 int r;
1420 r = alsa_get_preferred_sample_rate(context, &rate);
1421 if (r != CUBEB_OK) {
1422 return CUBEB_ERROR;
1425 r = alsa_get_max_channel_count(context, &max_channels);
1426 if (r != CUBEB_OK) {
1427 return CUBEB_ERROR;
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;
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;
1453 collection->device = device;
1454 collection->count = 1;
1456 return CUBEB_OK;
1459 static int
1460 alsa_device_collection_destroy(cubeb * context,
1461 cubeb_device_collection * collection)
1463 assert(collection->count == 1);
1464 (void)context;
1465 free(collection->device);
1466 return CUBEB_OK;
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};