diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_alsa.c')
-rw-r--r-- | media/libcubeb/src/cubeb_alsa.c | 694 |
1 files changed, 183 insertions, 511 deletions
diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c index 4a55b02317..72a6acfb1c 100644 --- a/media/libcubeb/src/cubeb_alsa.c +++ b/media/libcubeb/src/cubeb_alsa.c @@ -8,63 +8,15 @@ #define _DEFAULT_SOURCE #define _BSD_SOURCE #define _XOPEN_SOURCE 500 -#include "cubeb-internal.h" -#include "cubeb/cubeb.h" -#include <alsa/asoundlib.h> +#include <pthread.h> +#include <sys/time.h> #include <assert.h> -#include <dlfcn.h> #include <limits.h> #include <poll.h> -#include <pthread.h> -#include <sys/time.h> #include <unistd.h> - -#ifdef DISABLE_LIBASOUND_DLOPEN -#define WRAP(x) x -#else -#define WRAP(x) cubeb_##x -#define LIBASOUND_API_VISIT(X) \ - X(snd_config) \ - X(snd_config_add) \ - X(snd_config_copy) \ - X(snd_config_delete) \ - X(snd_config_get_id) \ - X(snd_config_get_string) \ - X(snd_config_imake_integer) \ - X(snd_config_search) \ - X(snd_config_search_definition) \ - X(snd_lib_error_set_handler) \ - X(snd_pcm_avail_update) \ - X(snd_pcm_close) \ - X(snd_pcm_delay) \ - X(snd_pcm_drain) \ - X(snd_pcm_frames_to_bytes) \ - X(snd_pcm_get_params) \ - X(snd_pcm_hw_params_any) \ - X(snd_pcm_hw_params_get_channels_max) \ - X(snd_pcm_hw_params_get_rate) \ - X(snd_pcm_hw_params_set_rate_near) \ - X(snd_pcm_hw_params_sizeof) \ - X(snd_pcm_nonblock) \ - X(snd_pcm_open) \ - X(snd_pcm_open_lconf) \ - X(snd_pcm_pause) \ - X(snd_pcm_poll_descriptors) \ - X(snd_pcm_poll_descriptors_count) \ - X(snd_pcm_poll_descriptors_revents) \ - X(snd_pcm_readi) \ - X(snd_pcm_recover) \ - X(snd_pcm_set_params) \ - X(snd_pcm_start) \ - X(snd_pcm_state) \ - X(snd_pcm_writei) - -#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; -LIBASOUND_API_VISIT(MAKE_TYPEDEF); -#undef MAKE_TYPEDEF -/* snd_pcm_hw_params_alloca is actually a macro */ -#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof -#endif +#include <alsa/asoundlib.h> +#include "cubeb/cubeb.h" +#include "cubeb-internal.h" #define CUBEB_STREAM_MAX 16 #define CUBEB_WATCHDOG_MS 10000 @@ -84,7 +36,6 @@ static struct cubeb_ops const alsa_ops; struct cubeb { struct cubeb_ops const * ops; - void * libasound; pthread_t thread; @@ -101,8 +52,7 @@ struct cubeb { int shutdown; - /* Control pipe for forcing poll to wake and rebuild fds or recalculate the - * timeout. */ + /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */ int control_fd_read; int control_fd_write; @@ -117,18 +67,22 @@ struct cubeb { int is_pa; }; -enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR }; +enum stream_state { + INACTIVE, + RUNNING, + DRAINING, + PROCESSING, + ERROR +}; struct cubeb_stream { - /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context; - void * user_ptr; - /**/ pthread_mutex_t mutex; snd_pcm_t * pcm; cubeb_data_callback data_callback; cubeb_state_callback state_callback; - snd_pcm_uframes_t stream_position; + void * user_ptr; + snd_pcm_uframes_t write_position; snd_pcm_uframes_t last_position; snd_pcm_uframes_t buffer_size; cubeb_stream_params params; @@ -141,8 +95,7 @@ struct cubeb_stream { enum stream_state state; struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */ - struct pollfd * - fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */ + struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */ nfds_t nfds; struct timeval drain_timeout; @@ -154,12 +107,6 @@ struct cubeb_stream { being logically active and playing. */ struct timeval last_activity; float volume; - - char * buffer; - snd_pcm_uframes_t bufframes; - snd_pcm_stream_t stream_type; - - struct cubeb_stream * other_stream; }; static int @@ -288,16 +235,6 @@ set_timeout(struct timeval * timeout, unsigned int ms) } static void -stream_buffer_decrement(cubeb_stream * stm, long count) -{ - char * bufremains = - stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count); - memmove(stm->buffer, bufremains, - WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count)); - stm->bufframes -= count; -} - -static void alsa_set_stream_state(cubeb_stream * stm, enum stream_state state) { cubeb * ctx; @@ -312,185 +249,97 @@ alsa_set_stream_state(cubeb_stream * stm, enum stream_state state) } static enum stream_state -alsa_process_stream(cubeb_stream * stm) +alsa_refill_stream(cubeb_stream * stm) { - unsigned short revents; snd_pcm_sframes_t avail; + long got; + void * p; int draining; draining = 0; pthread_mutex_lock(&stm->mutex); - /* Call _poll_descriptors_revents() even if we don't use it - to let underlying plugins clear null events. Otherwise poll() - may wake up again and again, producing unnecessary CPU usage. */ - WRAP(snd_pcm_poll_descriptors_revents) - (stm->pcm, stm->fds, stm->nfds, &revents); - - avail = WRAP(snd_pcm_avail_update)(stm->pcm); + avail = snd_pcm_avail_update(stm->pcm); + if (avail < 0) { + snd_pcm_recover(stm->pcm, avail, 1); + avail = snd_pcm_avail_update(stm->pcm); + } - /* Got null event? Bail and wait for another wakeup. */ - if (avail == 0) { + /* Failed to recover from an xrun, this stream must be broken. */ + if (avail < 0) { pthread_mutex_unlock(&stm->mutex); - return RUNNING; + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); + return ERROR; } - /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. - */ - if ((unsigned int)avail > stm->buffer_size) { + /* This should never happen. */ + if ((unsigned int) avail > stm->buffer_size) { avail = stm->buffer_size; } - /* Capture: Read available frames */ - if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) { - snd_pcm_sframes_t got; - - if (avail + stm->bufframes > stm->buffer_size) { - /* Buffer overflow. Skip and overwrite with new data. */ - stm->bufframes = 0; - // TODO: should it be marked as DRAINING? - } - - got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail); - - if (got < 0) { - avail = got; // the error handler below will recover us - } else { - stm->bufframes += got; - stm->stream_position += got; - - gettimeofday(&stm->last_activity, NULL); - } - } - - /* Capture: Pass read frames to callback function */ - if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 && - (!stm->other_stream || - stm->other_stream->bufframes < stm->other_stream->buffer_size)) { - snd_pcm_sframes_t wrote = stm->bufframes; - struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm; - void * other_buffer = stm->other_stream ? stm->other_stream->buffer + - stm->other_stream->bufframes - : NULL; - - /* Correct write size to the other stream available space */ - if (stm->other_stream && - wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size - - stm->other_stream->bufframes)) { - wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes; - } - + /* poll(2) claims this stream is active, so there should be some space + available to write. If avail is still zero here, the stream must be in + a funky state, bail and wait for another wakeup. */ + if (avail == 0) { pthread_mutex_unlock(&stm->mutex); - wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, - other_buffer, wrote); - pthread_mutex_lock(&stm->mutex); - - if (wrote < 0) { - avail = wrote; // the error handler below will recover us - } else { - stream_buffer_decrement(stm, wrote); - - if (stm->other_stream) { - stm->other_stream->bufframes += wrote; - } - } + return RUNNING; } - /* Playback: Don't have enough data? Let's ask for more. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && - avail > (snd_pcm_sframes_t)stm->bufframes && - (!stm->other_stream || stm->other_stream->bufframes > 0)) { - long got = avail - stm->bufframes; - void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL; - char * buftail = - stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); - - /* Correct read size to the other stream available frames */ - if (stm->other_stream && - got > (snd_pcm_sframes_t)stm->other_stream->bufframes) { - got = stm->other_stream->bufframes; - } + p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail)); + assert(p); + pthread_mutex_unlock(&stm->mutex); + got = stm->data_callback(stm, stm->user_ptr, NULL, p, avail); + pthread_mutex_lock(&stm->mutex); + if (got < 0) { pthread_mutex_unlock(&stm->mutex); - got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got); - pthread_mutex_lock(&stm->mutex); - - if (got < 0) { - avail = got; // the error handler below will recover us - } else { - stm->bufframes += got; - - if (stm->other_stream) { - stream_buffer_decrement(stm->other_stream, got); - } - } - } - - /* Playback: Still don't have enough data? Add some silence. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && - avail > (snd_pcm_sframes_t)stm->bufframes) { - long drain_frames = avail - stm->bufframes; - double drain_time = (double)drain_frames / stm->params.rate; - - char * buftail = - stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); - memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames)); - stm->bufframes = avail; - - /* Mark as draining, unless we're waiting for capture */ - if (!stm->other_stream || stm->other_stream->bufframes > 0) { - set_timeout(&stm->drain_timeout, drain_time * 1000); - - draining = 1; - } + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); + free(p); + return ERROR; } - - /* Playback: Have enough data and no errors. Let's write it out. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) { + if (got > 0) { snd_pcm_sframes_t wrote; if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { - float * b = (float *)stm->buffer; - for (uint32_t i = 0; i < avail * stm->params.channels; i++) { + float * b = (float *) p; + for (uint32_t i = 0; i < got * stm->params.channels; i++) { b[i] *= stm->volume; } } else { - short * b = (short *)stm->buffer; - for (uint32_t i = 0; i < avail * stm->params.channels; i++) { + short * b = (short *) p; + for (uint32_t i = 0; i < got * stm->params.channels; i++) { b[i] *= stm->volume; } } - - wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail); + wrote = snd_pcm_writei(stm->pcm, p, got); if (wrote < 0) { - avail = wrote; // the error handler below will recover us - } else { - stream_buffer_decrement(stm, wrote); - - stm->stream_position += wrote; - gettimeofday(&stm->last_activity, NULL); + snd_pcm_recover(stm->pcm, wrote, 1); + wrote = snd_pcm_writei(stm->pcm, p, got); + } + if (wrote < 0 || wrote != got) { + /* Recovery failed, somehow. */ + pthread_mutex_unlock(&stm->mutex); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); + return ERROR; } + stm->write_position += wrote; + gettimeofday(&stm->last_activity, NULL); } + if (got != avail) { + long buffer_fill = stm->buffer_size - (avail - got); + double buffer_time = (double) buffer_fill / stm->params.rate; - /* Got some error? Let's try to recover the stream. */ - if (avail < 0) { - avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0); + /* Fill the remaining buffer with silence to guarantee one full period + has been written. */ + snd_pcm_writei(stm->pcm, (char *) p + got, avail - got); - /* Capture pcm must be started after initial setup/recover */ - if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE && - WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) { - avail = WRAP(snd_pcm_start)(stm->pcm); - } - } + set_timeout(&stm->drain_timeout, buffer_time * 1000); - /* Failed to recover, this stream must be broken. */ - if (avail < 0) { - pthread_mutex_unlock(&stm->mutex); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return ERROR; + draining = 1; } + free(p); pthread_mutex_unlock(&stm->mutex); return draining ? DRAINING : RUNNING; } @@ -543,11 +392,10 @@ alsa_run(cubeb * ctx) stm = ctx->streams[i]; /* We can't use snd_pcm_poll_descriptors_revents here because of https://github.com/kinetiknz/cubeb/issues/135. */ - if (stm && stm->state == RUNNING && stm->fds && - any_revents(stm->fds, stm->nfds)) { + if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) { alsa_set_stream_state(stm, PROCESSING); pthread_mutex_unlock(&ctx->mutex); - state = alsa_process_stream(stm); + state = alsa_refill_stream(stm); pthread_mutex_lock(&ctx->mutex); alsa_set_stream_state(stm, state); } @@ -559,8 +407,7 @@ alsa_run(cubeb * ctx) if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) { alsa_set_stream_state(stm, INACTIVE); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - } else if (stm->state == RUNNING && - ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) { + } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) { alsa_set_stream_state(stm, ERROR); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); } @@ -598,36 +445,35 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) slave_def = NULL; - r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm); + r = snd_config_search(root_pcm, "slave", &slave_pcm); if (r < 0) { return NULL; } - r = WRAP(snd_config_get_string)(slave_pcm, &string); + r = snd_config_get_string(slave_pcm, &string); if (r >= 0) { - r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, - &slave_def); + r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def); if (r < 0) { return NULL; } } do { - r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm); + r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm); if (r < 0) { break; } - r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string); + r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string); if (r < 0) { break; } r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int)sizeof(node_name)) { + if (r < 0 || r > (int) sizeof(node_name)) { break; } - r = WRAP(snd_config_search)(lconf, node_name, &pcm); + r = snd_config_search(lconf, node_name, &pcm); if (r < 0) { break; } @@ -636,7 +482,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) } while (0); if (slave_def) { - WRAP(snd_config_delete)(slave_def); + snd_config_delete(slave_def); } return NULL; @@ -646,8 +492,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) higher than requested latency, but the plugin does not update its (and ALSA's) internal state to reflect that, leading to an immediate underrun situation. Inspired by WINE's make_handle_underrun_config. - Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 - */ + Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */ static snd_config_t * init_local_config_with_workaround(char const * pcm_name) { @@ -660,49 +505,47 @@ init_local_config_with_workaround(char const * pcm_name) lconf = NULL; - if (*WRAP(snd_config) == NULL) { + if (snd_config == NULL) { return NULL; } - r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config)); + r = snd_config_copy(&lconf, snd_config); if (r < 0) { return NULL; } do { - r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node); + r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node); if (r < 0) { break; } - r = WRAP(snd_config_get_id)(pcm_node, &string); + r = snd_config_get_id(pcm_node, &string); if (r < 0) { break; } r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int)sizeof(node_name)) { + if (r < 0 || r > (int) sizeof(node_name)) { break; } - r = WRAP(snd_config_search)(lconf, node_name, &pcm_node); + r = snd_config_search(lconf, node_name, &pcm_node); if (r < 0) { break; } - /* If this PCM has a slave, walk the slave configurations until we reach the - * bottom. */ + /* If this PCM has a slave, walk the slave configurations until we reach the bottom. */ while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) { pcm_node = node; } - /* Fetch the PCM node's type, and bail out if it's not the PulseAudio - * plugin. */ - r = WRAP(snd_config_search)(pcm_node, "type", &node); + /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */ + r = snd_config_search(pcm_node, "type", &node); if (r < 0) { break; } - r = WRAP(snd_config_get_string)(node, &string); + r = snd_config_get_string(node, &string); if (r < 0) { break; } @@ -713,18 +556,18 @@ init_local_config_with_workaround(char const * pcm_name) /* Don't clobber an explicit existing handle_underrun value, set it only if it doesn't already exist. */ - r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node); + r = snd_config_search(pcm_node, "handle_underrun", &node); if (r != -ENOENT) { break; } /* Disable pcm_pulse's asynchronous underrun handling. */ - r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0); + r = snd_config_imake_integer(&node, "handle_underrun", 0); if (r < 0) { break; } - r = WRAP(snd_config_add)(pcm_node, node); + r = snd_config_add(pcm_node, node); if (r < 0) { break; } @@ -732,23 +575,21 @@ init_local_config_with_workaround(char const * pcm_name) return lconf; } while (0); - WRAP(snd_config_delete)(lconf); + snd_config_delete(lconf); return NULL; } static int -alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, - snd_pcm_stream_t stream, snd_config_t * local_config) +alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * local_config) { int r; pthread_mutex_lock(&cubeb_alsa_mutex); if (local_config) { - r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, - local_config); + r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config); } else { - r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK); + r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK); } pthread_mutex_unlock(&cubeb_alsa_mutex); @@ -761,7 +602,7 @@ alsa_locked_pcm_close(snd_pcm_t * pcm) int r; pthread_mutex_lock(&cubeb_alsa_mutex); - r = WRAP(snd_pcm_close)(pcm); + r = snd_pcm_close(pcm); pthread_mutex_unlock(&cubeb_alsa_mutex); return r; @@ -817,7 +658,6 @@ silent_error_handler(char const * file, int line, char const * function, alsa_init(cubeb ** context, char const * context_name) { (void)context_name; - void * libasound = NULL; cubeb * ctx; int r; int i; @@ -828,31 +668,9 @@ alsa_init(cubeb ** context, char const * context_name) assert(context); *context = NULL; -#ifndef DISABLE_LIBASOUND_DLOPEN - libasound = dlopen("libasound.so.2", RTLD_LAZY); - if (!libasound) { - libasound = dlopen("libasound.so", RTLD_LAZY); - if (!libasound) { - return CUBEB_ERROR; - } - } - -#define LOAD(x) \ - { \ - cubeb_##x = dlsym(libasound, #x); \ - if (!cubeb_##x) { \ - dlclose(libasound); \ - return CUBEB_ERROR; \ - } \ - } - - LIBASOUND_API_VISIT(LOAD); -#undef LOAD -#endif - pthread_mutex_lock(&cubeb_alsa_mutex); if (!cubeb_alsa_error_handler_set) { - WRAP(snd_lib_error_set_handler)(silent_error_handler); + snd_lib_error_set_handler(silent_error_handler); cubeb_alsa_error_handler_set = 1; } pthread_mutex_unlock(&cubeb_alsa_mutex); @@ -861,7 +679,6 @@ alsa_init(cubeb ** context, char const * context_name) assert(ctx); ctx->ops = &alsa_ops; - ctx->libasound = libasound; r = pthread_mutex_init(&ctx->mutex, NULL); assert(r == 0); @@ -895,8 +712,7 @@ alsa_init(cubeb ** context, char const * context_name) /* Open a dummy PCM to force the configuration space to be evaluated so that init_local_config_with_workaround can find and modify the default node. */ - r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, - NULL); + r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL); if (r >= 0) { alsa_locked_pcm_close(dummy); } @@ -906,13 +722,12 @@ alsa_init(cubeb ** context, char const * context_name) pthread_mutex_unlock(&cubeb_alsa_mutex); if (ctx->local_config) { ctx->is_pa = 1; - r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, - SND_PCM_STREAM_PLAYBACK, ctx->local_config); + r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config); /* If we got a local_config, we found a PA PCM. If opening a PCM with that config fails with EINVAL, the PA PCM is too old for this workaround. */ if (r == -EINVAL) { pthread_mutex_lock(&cubeb_alsa_mutex); - WRAP(snd_config_delete)(ctx->local_config); + snd_config_delete(ctx->local_config); pthread_mutex_unlock(&cubeb_alsa_mutex); ctx->local_config = NULL; } else if (r >= 0) { @@ -954,28 +769,24 @@ alsa_destroy(cubeb * ctx) if (ctx->local_config) { pthread_mutex_lock(&cubeb_alsa_mutex); - WRAP(snd_config_delete)(ctx->local_config); + snd_config_delete(ctx->local_config); pthread_mutex_unlock(&cubeb_alsa_mutex); } - if (ctx->libasound) { - dlclose(ctx->libasound); - } - free(ctx); } -static void -alsa_stream_destroy(cubeb_stream * stm); +static void alsa_stream_destroy(cubeb_stream * stm); 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) +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) { (void)stream_name; cubeb_stream * stm; @@ -983,18 +794,23 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, snd_pcm_format_t format; snd_pcm_uframes_t period_size; int latency_us = 0; - char const * pcm_name = - deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME; - assert(ctx && stream); - *stream = NULL; + assert(ctx && stream); - if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + if (input_stream_params) { + /* Capture support not yet implemented. */ return CUBEB_ERROR_NOT_SUPPORTED; } - switch (stream_params->format) { + if (input_device || output_device) { + /* Device selection not yet implemented. */ + return CUBEB_ERROR_DEVICE_UNAVAILABLE; + } + + *stream = NULL; + + switch (output_stream_params->format) { case CUBEB_SAMPLE_S16LE: format = SND_PCM_FORMAT_S16_LE; break; @@ -1026,28 +842,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; - stm->params = *stream_params; + stm->params = *output_stream_params; stm->state = INACTIVE; stm->volume = 1.0; - stm->buffer = NULL; - stm->bufframes = 0; - stm->stream_type = stream_type; - stm->other_stream = NULL; r = pthread_mutex_init(&stm->mutex, NULL); assert(r == 0); - r = pthread_cond_init(&stm->cond, NULL); - assert(r == 0); - - r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, - ctx->local_config); + r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config); if (r < 0) { alsa_stream_destroy(stm); return CUBEB_ERROR; } - r = WRAP(snd_pcm_nonblock)(stm->pcm, 1); + r = snd_pcm_nonblock(stm->pcm, 1); assert(r == 0); latency_us = latency_frames * 1e6 / stm->params.rate; @@ -1057,34 +865,30 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, Only resort to this hack if the handle_underrun workaround failed. */ if (!ctx->local_config && ctx->is_pa) { const int min_latency = 5e5; - latency_us = latency_us < min_latency ? min_latency : latency_us; + latency_us = latency_us < min_latency ? min_latency: latency_us; } - r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, - stm->params.channels, stm->params.rate, 1, - latency_us); + r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, + stm->params.channels, stm->params.rate, 1, + latency_us); if (r < 0) { alsa_stream_destroy(stm); return CUBEB_ERROR_INVALID_FORMAT; } - r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size); + r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size); assert(r == 0); - /* Double internal buffer size to have enough space when waiting for the other - * side of duplex connection */ - stm->buffer_size *= 2; - stm->buffer = - calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size)); - assert(stm->buffer); - - stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm); + stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm); assert(stm->nfds > 0); stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd)); assert(stm->saved_fds); - r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds); - assert((nfds_t)r == stm->nfds); + r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds); + assert((nfds_t) r == stm->nfds); + + r = pthread_cond_init(&stm->cond, NULL); + assert(r == 0); if (alsa_register_stream(ctx, stm) != 0) { alsa_stream_destroy(stm); @@ -1096,66 +900,22 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, return CUBEB_OK; } -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) -{ - int result = CUBEB_OK; - cubeb_stream *instm = NULL, *outstm = NULL; - - if (result == CUBEB_OK && input_stream_params) { - result = alsa_stream_init_single(ctx, &instm, stream_name, - SND_PCM_STREAM_CAPTURE, input_device, - input_stream_params, latency_frames, - data_callback, state_callback, user_ptr); - } - - if (result == CUBEB_OK && output_stream_params) { - result = alsa_stream_init_single(ctx, &outstm, stream_name, - SND_PCM_STREAM_PLAYBACK, output_device, - output_stream_params, latency_frames, - data_callback, state_callback, user_ptr); - } - - if (result == CUBEB_OK && input_stream_params && output_stream_params) { - instm->other_stream = outstm; - outstm->other_stream = instm; - } - - if (result != CUBEB_OK && instm) { - alsa_stream_destroy(instm); - } - - *stream = outstm ? outstm : instm; - - return result; -} - static void alsa_stream_destroy(cubeb_stream * stm) { int r; cubeb * ctx; - assert(stm && (stm->state == INACTIVE || stm->state == ERROR || + assert(stm && (stm->state == INACTIVE || + stm->state == ERROR || stm->state == DRAINING)); ctx = stm->context; - if (stm->other_stream) { - stm->other_stream->other_stream = NULL; // to stop infinite recursion - alsa_stream_destroy(stm->other_stream); - } - pthread_mutex_lock(&stm->mutex); if (stm->pcm) { if (stm->state == DRAINING) { - WRAP(snd_pcm_drain)(stm->pcm); + snd_pcm_drain(stm->pcm); } alsa_locked_pcm_close(stm->pcm); stm->pcm = NULL; @@ -1174,8 +934,6 @@ alsa_stream_destroy(cubeb_stream * stm) ctx->active_streams -= 1; pthread_mutex_unlock(&ctx->mutex); - free(stm->buffer); - free(stm); } @@ -1184,7 +942,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { int r; cubeb_stream * stm; - snd_pcm_hw_params_t * hw_params; + snd_pcm_hw_params_t* hw_params; cubeb_stream_params params; params.rate = 44100; params.format = CUBEB_SAMPLE_FLOAT32NE; @@ -1194,20 +952,17 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) assert(ctx); - r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, - NULL, NULL); + r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, NULL, NULL); if (r != CUBEB_OK) { return CUBEB_ERROR; } - assert(stm); - - r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params); + r = snd_pcm_hw_params_any(stm->pcm, hw_params); if (r < 0) { return CUBEB_ERROR; } - r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels); + r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels); if (r < 0) { return CUBEB_ERROR; } @@ -1218,8 +973,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ +alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { (void)ctx; int r, dir; snd_pcm_t * pcm; @@ -1229,42 +983,40 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) /* get a pcm, disabling resampling, so we get a rate the * hardware/dmix/pulse/etc. supports. */ - r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NO_AUTO_RESAMPLE); + r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE); if (r < 0) { return CUBEB_ERROR; } - r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params); + r = snd_pcm_hw_params_any(pcm, hw_params); if (r < 0) { - WRAP(snd_pcm_close)(pcm); + snd_pcm_close(pcm); return CUBEB_ERROR; } - r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir); + r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir); if (r >= 0) { /* There is a default rate: use it. */ - WRAP(snd_pcm_close)(pcm); + snd_pcm_close(pcm); return CUBEB_OK; } /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */ *rate = 44100; - r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL); + r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL); if (r < 0) { - WRAP(snd_pcm_close)(pcm); + snd_pcm_close(pcm); return CUBEB_ERROR; } - WRAP(snd_pcm_close)(pcm); + snd_pcm_close(pcm); return CUBEB_OK; } static int -alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, - uint32_t * latency_frames) +alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { (void)ctx; /* 40ms is found to be an acceptable minimum, even on a super low-end @@ -1282,19 +1034,8 @@ alsa_stream_start(cubeb_stream * stm) assert(stm); ctx = stm->context; - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) { - int r = alsa_stream_start(stm->other_stream); - if (r != CUBEB_OK) - return r; - } - pthread_mutex_lock(&stm->mutex); - /* Capture pcm must be started after initial setup/recover */ - if (stm->stream_type == SND_PCM_STREAM_CAPTURE && - WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) { - WRAP(snd_pcm_start)(stm->pcm); - } - WRAP(snd_pcm_pause)(stm->pcm, 0); + snd_pcm_pause(stm->pcm, 0); gettimeofday(&stm->last_activity, NULL); pthread_mutex_unlock(&stm->mutex); @@ -1318,12 +1059,6 @@ alsa_stream_stop(cubeb_stream * stm) assert(stm); ctx = stm->context; - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) { - int r = alsa_stream_stop(stm->other_stream); - if (r != CUBEB_OK) - return r; - } - pthread_mutex_lock(&ctx->mutex); while (stm->state == PROCESSING) { r = pthread_cond_wait(&stm->cond, &ctx->mutex); @@ -1334,7 +1069,7 @@ alsa_stream_stop(cubeb_stream * stm) pthread_mutex_unlock(&ctx->mutex); pthread_mutex_lock(&stm->mutex); - WRAP(snd_pcm_pause)(stm->pcm, 1); + snd_pcm_pause(stm->pcm, 1); pthread_mutex_unlock(&stm->mutex); return CUBEB_OK; @@ -1350,8 +1085,8 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) pthread_mutex_lock(&stm->mutex); delay = -1; - if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING || - WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) { + if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING || + snd_pcm_delay(stm->pcm, &delay) != 0) { *position = stm->last_position; pthread_mutex_unlock(&stm->mutex); return CUBEB_OK; @@ -1360,8 +1095,8 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) assert(delay >= 0); *position = 0; - if (stm->stream_position >= (snd_pcm_uframes_t)delay) { - *position = stm->stream_position - delay; + if (stm->write_position >= (snd_pcm_uframes_t) delay) { + *position = stm->write_position - delay; } stm->last_position = *position; @@ -1375,9 +1110,8 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { snd_pcm_sframes_t delay; /* This function returns the delay in frames until a frame written using - snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. - */ - if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) { + snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */ + if (snd_pcm_delay(stm->pcm, &delay)) { return CUBEB_ERROR; } @@ -1397,86 +1131,24 @@ alsa_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } -static int -alsa_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection) -{ - cubeb_device_info * device = NULL; - - if (!context) - return CUBEB_ERROR; - - uint32_t rate, max_channels; - int r; - - r = alsa_get_preferred_sample_rate(context, &rate); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - r = alsa_get_max_channel_count(context, &max_channels); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - char const * a_name = "default"; - device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info)); - assert(device); - if (!device) - return CUBEB_ERROR; - - device->device_id = a_name; - device->devid = (cubeb_devid)device->device_id; - device->friendly_name = a_name; - device->group_id = a_name; - device->vendor_name = a_name; - device->type = type; - device->state = CUBEB_DEVICE_STATE_ENABLED; - device->preferred = CUBEB_DEVICE_PREF_ALL; - device->format = CUBEB_DEVICE_FMT_S16NE; - device->default_format = CUBEB_DEVICE_FMT_S16NE; - device->max_channels = max_channels; - device->min_rate = rate; - device->max_rate = rate; - device->default_rate = rate; - device->latency_lo = 0; - device->latency_hi = 0; - - collection->device = device; - collection->count = 1; - - return CUBEB_OK; -} - -static int -alsa_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection) -{ - assert(collection->count == 1); - (void)context; - free(collection->device); - return CUBEB_OK; -} - static struct cubeb_ops const alsa_ops = { - .init = alsa_init, - .get_backend_id = alsa_get_backend_id, - .get_max_channel_count = alsa_get_max_channel_count, - .get_min_latency = alsa_get_min_latency, - .get_preferred_sample_rate = alsa_get_preferred_sample_rate, - .enumerate_devices = alsa_enumerate_devices, - .device_collection_destroy = alsa_device_collection_destroy, - .destroy = alsa_destroy, - .stream_init = alsa_stream_init, - .stream_destroy = alsa_stream_destroy, - .stream_start = alsa_stream_start, - .stream_stop = alsa_stream_stop, - .stream_get_position = alsa_stream_get_position, - .stream_get_latency = alsa_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = alsa_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL}; + .init = alsa_init, + .get_backend_id = alsa_get_backend_id, + .get_max_channel_count = alsa_get_max_channel_count, + .get_min_latency = alsa_get_min_latency, + .get_preferred_sample_rate = alsa_get_preferred_sample_rate, + .enumerate_devices = NULL, + .destroy = alsa_destroy, + .stream_init = alsa_stream_init, + .stream_destroy = alsa_stream_destroy, + .stream_start = alsa_stream_start, + .stream_stop = alsa_stream_stop, + .stream_get_position = alsa_stream_get_position, + .stream_get_latency = alsa_stream_get_latency, + .stream_set_volume = alsa_stream_set_volume, + .stream_set_panning = NULL, + .stream_get_current_device = NULL, + .stream_device_destroy = NULL, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL +}; |