summaryrefslogtreecommitdiff
path: root/media/libcubeb/src/cubeb_alsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/src/cubeb_alsa.c')
-rw-r--r--media/libcubeb/src/cubeb_alsa.c694
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, &params, 100, NULL,
- NULL, NULL);
+ r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 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
+};