2 * Copyright © 2011 Mozilla Foundation
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
8 #define _DEFAULT_SOURCE
10 #if defined(__NetBSD__)
11 #define _NETBSD_SOURCE /* timersub() */
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>
26 #ifdef DISABLE_LIBASOUND_DLOPEN
29 #define WRAP(x) (*cubeb_##x)
30 #define LIBASOUND_API_VISIT(X) \
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) \
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) \
54 X(snd_pcm_open_lconf) \
56 X(snd_pcm_poll_descriptors) \
57 X(snd_pcm_poll_descriptors_count) \
58 X(snd_pcm_poll_descriptors_revents) \
61 X(snd_pcm_set_params) \
66 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
67 LIBASOUND_API_VISIT(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
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
;
90 struct cubeb_ops
const * ops
;
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. */
108 /* Control pipe for forcing poll to wake and rebuild fds or recalculate the
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
;
124 enum stream_state
{ INACTIVE
, RUNNING
, DRAINING
, PROCESSING
, ERROR
};
126 struct cubeb_stream
{
127 /* Note: Must match cubeb_stream layout in cubeb.c. */
131 pthread_mutex_t mutex
;
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
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. */
149 fds
; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
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
;
163 snd_pcm_uframes_t bufframes
;
164 snd_pcm_stream_t stream_type
;
166 struct cubeb_stream
* other_stream
;
170 any_revents(struct pollfd
* fds
, nfds_t nfds
)
174 for (i
= 0; i
< nfds
; ++i
) {
175 if (fds
[i
].revents
) {
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
) {
190 return a
->tv_usec
> b
->tv_usec
? 1 : -1;
192 return a
->tv_sec
> b
->tv_sec
? 1 : -1;
196 timeval_to_relative_ms(struct timeval
* tv
)
203 gettimeofday(&now
, NULL
);
204 r
= cmp_timeval(tv
, &now
);
206 timersub(tv
, &now
, &dt
);
208 timersub(&now
, tv
, &dt
);
212 t
+= (dt
.tv_usec
+ 500) / 1000;
216 } else if (t
< INT_MIN
) {
220 return r
>= 0 ? t
: -t
;
224 ms_until(struct timeval
* tv
)
226 return timeval_to_relative_ms(tv
);
230 ms_since(struct timeval
* tv
)
232 return -timeval_to_relative_ms(tv
);
243 assert(ctx
->rebuild
);
245 /* Always count context's control pipe fd. */
247 for (i
= 0; i
< CUBEB_STREAM_MAX
; ++i
) {
248 stm
= ctx
->streams
[i
];
251 if (stm
->state
== RUNNING
) {
258 ctx
->fds
= calloc(nfds
, sizeof(struct pollfd
));
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
];
279 poll_wake(cubeb
* ctx
)
281 if (write(ctx
->control_fd_write
, "x", 1) < 0) {
282 /* ignore write error */
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;
295 stream_buffer_decrement(cubeb_stream
* stm
, long count
)
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
;
305 alsa_set_stream_state(cubeb_stream
* stm
, enum stream_state state
)
312 r
= pthread_cond_broadcast(&stm
->cond
);
318 static enum stream_state
319 alsa_process_stream(cubeb_stream
* stm
)
321 unsigned short revents
;
322 snd_pcm_sframes_t avail
;
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. */
339 pthread_mutex_unlock(&stm
->mutex
);
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. */
356 // TODO: should it be marked as DRAINING?
359 got
= WRAP(snd_pcm_readi
)(stm
->pcm
, stm
->buffer
+ stm
->bufframes
, avail
);
362 avail
= got
; // the error handler below will recover us
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
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
);
394 avail
= wrote
; // the error handler below will recover us
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
;
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
);
424 avail
= got
; // the error handler below will recover us
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
;
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);
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
++) {
463 short * b
= (short *)stm
->buffer
;
464 for (uint32_t i
= 0; i
< avail
* stm
->params
.channels
; i
++) {
469 wrote
= WRAP(snd_pcm_writei
)(stm
->pcm
, stm
->buffer
, avail
);
471 avail
= wrote
; // the error handler below will recover us
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. */
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. */
493 pthread_mutex_unlock(&stm
->mutex
);
494 stm
->state_callback(stm
, stm
->user_ptr
, CUBEB_STATE_ERROR
);
498 pthread_mutex_unlock(&stm
->mutex
);
499 return draining
? DRAINING
: RUNNING
;
503 alsa_run(cubeb
* ctx
)
510 enum stream_state state
;
512 pthread_mutex_lock(&ctx
->mutex
);
518 /* Wake up at least once per second for the watchdog. */
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
) {
530 pthread_mutex_unlock(&ctx
->mutex
);
531 r
= poll(ctx
->fds
, ctx
->nfds
, timeout
);
532 pthread_mutex_lock(&ctx
->mutex
);
535 if (ctx
->fds
[0].revents
& POLLIN
) {
536 if (read(ctx
->control_fd_read
, &dummy
, 1) < 0) {
537 /* ignore read error */
541 pthread_mutex_unlock(&ctx
->mutex
);
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
);
560 for (i
= 0; i
< CUBEB_STREAM_MAX
; ++i
) {
561 stm
= ctx
->streams
[i
];
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
);
581 alsa_run_thread(void * context
)
583 cubeb
* ctx
= context
;
586 CUBEB_REGISTER_THREAD("cubeb rendering thread");
592 CUBEB_UNREGISTER_THREAD();
597 static snd_config_t
*
598 get_slave_pcm_node(snd_config_t
* lconf
, snd_config_t
* root_pcm
)
601 snd_config_t
* slave_pcm
;
602 snd_config_t
* slave_def
;
609 r
= WRAP(snd_config_search
)(root_pcm
, "slave", &slave_pcm
);
614 r
= WRAP(snd_config_get_string
)(slave_pcm
, &string
);
616 r
= WRAP(snd_config_search_definition
)(lconf
, "pcm_slave", string
,
624 r
= WRAP(snd_config_search
)(slave_def
? slave_def
: slave_pcm
, "pcm", &pcm
);
629 r
= WRAP(snd_config_get_string
)(slave_def
? slave_def
: slave_pcm
, &string
);
634 r
= snprintf(node_name
, sizeof(node_name
), "pcm.%s", string
);
635 if (r
< 0 || r
> (int)sizeof(node_name
)) {
638 r
= WRAP(snd_config_search
)(lconf
, node_name
, &pcm
);
647 WRAP(snd_config_delete
)(slave_def
);
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
)
663 snd_config_t
* lconf
;
664 snd_config_t
* pcm_node
;
671 if (WRAP(snd_config
) == NULL
) {
675 r
= WRAP(snd_config_copy
)(&lconf
, WRAP(snd_config
));
681 r
= WRAP(snd_config_search_definition
)(lconf
, "pcm", pcm_name
, &pcm_node
);
686 r
= WRAP(snd_config_get_id
)(pcm_node
, &string
);
691 r
= snprintf(node_name
, sizeof(node_name
), "pcm.%s", string
);
692 if (r
< 0 || r
> (int)sizeof(node_name
)) {
695 r
= WRAP(snd_config_search
)(lconf
, node_name
, &pcm_node
);
700 /* If this PCM has a slave, walk the slave configurations until we reach the
702 while ((node
= get_slave_pcm_node(lconf
, pcm_node
)) != NULL
) {
706 /* Fetch the PCM node's type, and bail out if it's not the PulseAudio
708 r
= WRAP(snd_config_search
)(pcm_node
, "type", &node
);
713 r
= WRAP(snd_config_get_string
)(node
, &string
);
718 if (strcmp(string
, "pulse") != 0) {
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
);
729 /* Disable pcm_pulse's asynchronous underrun handling. */
730 r
= WRAP(snd_config_imake_integer
)(&node
, "handle_underrun", 0);
735 r
= WRAP(snd_config_add
)(pcm_node
, node
);
743 WRAP(snd_config_delete
)(lconf
);
749 alsa_locked_pcm_open(snd_pcm_t
** pcm
, char const * pcm_name
,
750 snd_pcm_stream_t stream
, snd_config_t
* local_config
)
754 pthread_mutex_lock(&cubeb_alsa_mutex
);
756 r
= WRAP(snd_pcm_open_lconf
)(pcm
, pcm_name
, stream
, SND_PCM_NONBLOCK
,
759 r
= WRAP(snd_pcm_open
)(pcm
, pcm_name
, stream
, SND_PCM_NONBLOCK
);
761 pthread_mutex_unlock(&cubeb_alsa_mutex
);
767 alsa_locked_pcm_close(snd_pcm_t
* pcm
)
771 pthread_mutex_lock(&cubeb_alsa_mutex
);
772 r
= WRAP(snd_pcm_close
)(pcm
);
773 pthread_mutex_unlock(&cubeb_alsa_mutex
);
779 alsa_register_stream(cubeb
* ctx
, cubeb_stream
* stm
)
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
;
790 pthread_mutex_unlock(&ctx
->mutex
);
792 return i
== CUBEB_STREAM_MAX
;
796 alsa_unregister_stream(cubeb_stream
* stm
)
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
;
810 pthread_mutex_unlock(&ctx
->mutex
);
814 silent_error_handler(char const * file
, int line
, char const * function
,
815 int err
, char const * fmt
, ...)
825 alsa_init(cubeb
** context
, char const * context_name
)
828 void * libasound
= NULL
;
839 #ifndef DISABLE_LIBASOUND_DLOPEN
840 libasound
= dlopen("libasound.so.2", RTLD_LAZY
);
842 libasound
= dlopen("libasound.so", RTLD_LAZY
);
850 cubeb_##x = dlsym(libasound, #x); \
852 dlclose(libasound); \
853 return CUBEB_ERROR; \
857 LIBASOUND_API_VISIT(LOAD
);
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
));
871 ctx
->ops
= &alsa_ops
;
872 ctx
->libasound
= libasound
;
874 r
= pthread_mutex_init(&ctx
->mutex
, NULL
);
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. */
892 r
= pthread_attr_init(&attr
);
895 r
= pthread_attr_setstacksize(&attr
, 256 * 1024);
898 r
= pthread_create(&ctx
->thread
, &attr
, alsa_run_thread
, ctx
);
901 r
= pthread_attr_destroy(&attr
);
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
,
909 alsa_locked_pcm_close(dummy
);
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
) {
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. */
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
;
927 alsa_locked_pcm_close(dummy
);
937 alsa_get_backend_id(cubeb
* ctx
)
944 alsa_destroy(cubeb
* ctx
)
950 pthread_mutex_lock(&ctx
->mutex
);
953 pthread_mutex_unlock(&ctx
->mutex
);
955 r
= pthread_join(ctx
->thread
, NULL
);
958 close(ctx
->control_fd_read
);
959 close(ctx
->control_fd_write
);
960 pthread_mutex_destroy(&ctx
->mutex
);
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
);
977 alsa_stream_destroy(cubeb_stream
* stm
);
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
)
991 snd_pcm_format_t format
;
992 snd_pcm_uframes_t period_size
;
994 char const * pcm_name
=
995 deviceid
? (char const *)deviceid
: CUBEB_ALSA_PCM_NAME
;
997 assert(ctx
&& stream
);
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
;
1009 case CUBEB_SAMPLE_S16BE
:
1010 format
= SND_PCM_FORMAT_S16_BE
;
1012 case CUBEB_SAMPLE_FLOAT32LE
:
1013 format
= SND_PCM_FORMAT_FLOAT_LE
;
1015 case CUBEB_SAMPLE_FLOAT32BE
:
1016 format
= SND_PCM_FORMAT_FLOAT_BE
;
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
);
1027 ctx
->active_streams
+= 1;
1028 pthread_mutex_unlock(&ctx
->mutex
);
1030 stm
= calloc(1, sizeof(*stm
));
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
;
1042 stm
->stream_type
= stream_type
;
1043 stm
->other_stream
= NULL
;
1045 r
= pthread_mutex_init(&stm
->mutex
, NULL
);
1048 r
= pthread_cond_init(&stm
->cond
, NULL
);
1051 r
= alsa_locked_pcm_open(&stm
->pcm
, pcm_name
, stm
->stream_type
,
1054 alsa_stream_destroy(stm
);
1058 r
= WRAP(snd_pcm_nonblock
)(stm
->pcm
, 1);
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,
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
);
1082 /* Double internal buffer size to have enough space when waiting for the other
1083 * side of duplex connection */
1084 stm
->buffer_size
*= 2;
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
);
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
;
1148 alsa_stream_destroy(cubeb_stream
* stm
)
1153 assert(stm
&& (stm
->state
== INACTIVE
|| stm
->state
== ERROR
||
1154 stm
->state
== DRAINING
));
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
);
1165 if (stm
->state
== DRAINING
) {
1166 WRAP(snd_pcm_drain
)(stm
->pcm
);
1168 alsa_locked_pcm_close(stm
->pcm
);
1171 free(stm
->saved_fds
);
1172 pthread_mutex_unlock(&stm
->mutex
);
1173 pthread_mutex_destroy(&stm
->mutex
);
1175 r
= pthread_cond_destroy(&stm
->cond
);
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
);
1191 alsa_get_max_channel_count(cubeb
* ctx
, uint32_t * max_channels
)
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
);
1205 r
= alsa_stream_init(ctx
, &stm
, "", NULL
, NULL
, NULL
, ¶ms
, 100, NULL
,
1207 if (r
!= CUBEB_OK
) {
1213 r
= WRAP(snd_pcm_hw_params_any
)(stm
->pcm
, hw_params
);
1218 r
= WRAP(snd_pcm_hw_params_get_channels_max
)(hw_params
, max_channels
);
1223 alsa_stream_destroy(stm
);
1229 alsa_get_preferred_sample_rate(cubeb
* ctx
, uint32_t * rate
)
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
);
1246 r
= WRAP(snd_pcm_hw_params_any
)(pcm
, hw_params
);
1248 WRAP(snd_pcm_close
)(pcm
);
1252 r
= WRAP(snd_pcm_hw_params_get_rate
)(hw_params
, rate
, &dir
);
1254 /* There is a default rate: use it. */
1255 WRAP(snd_pcm_close
)(pcm
);
1259 /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
1262 r
= WRAP(snd_pcm_hw_params_set_rate_near
)(pcm
, hw_params
, rate
, NULL
);
1264 WRAP(snd_pcm_close
)(pcm
);
1268 WRAP(snd_pcm_close
)(pcm
);
1274 alsa_get_min_latency(cubeb
* ctx
, cubeb_stream_params params
,
1275 uint32_t * latency_frames
)
1278 /* 40ms is found to be an acceptable minimum, even on a super low-end
1280 *latency_frames
= 40 * params
.rate
/ 1000;
1286 alsa_stream_start(cubeb_stream
* stm
)
1293 if (stm
->stream_type
== SND_PCM_STREAM_PLAYBACK
&& stm
->other_stream
) {
1294 int r
= alsa_stream_start(stm
->other_stream
);
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
);
1314 alsa_set_stream_state(stm
, RUNNING
);
1315 pthread_mutex_unlock(&ctx
->mutex
);
1321 alsa_stream_stop(cubeb_stream
* stm
)
1329 if (stm
->stream_type
== SND_PCM_STREAM_PLAYBACK
&& stm
->other_stream
) {
1330 int r
= alsa_stream_stop(stm
->other_stream
);
1335 pthread_mutex_lock(&ctx
->mutex
);
1336 while (stm
->state
== PROCESSING
) {
1337 r
= pthread_cond_wait(&stm
->cond
, &ctx
->mutex
);
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
);
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
);
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
);
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
);
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
)) {
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
);
1409 alsa_enumerate_devices(cubeb
* context
, cubeb_device_type type
,
1410 cubeb_device_collection
* collection
)
1412 cubeb_device_info
* device
= NULL
;
1417 uint32_t rate
, max_channels
;
1420 r
= alsa_get_preferred_sample_rate(context
, &rate
);
1421 if (r
!= CUBEB_OK
) {
1425 r
= alsa_get_max_channel_count(context
, &max_channels
);
1426 if (r
!= CUBEB_OK
) {
1430 char const * a_name
= "default";
1431 device
= (cubeb_device_info
*)calloc(1, sizeof(cubeb_device_info
));
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;
1460 alsa_device_collection_destroy(cubeb
* context
,
1461 cubeb_device_collection
* collection
)
1463 assert(collection
->count
== 1);
1465 free(collection
->device
);
1469 static struct cubeb_ops
const alsa_ops
= {
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
};