summaryrefslogtreecommitdiffstats
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.c560
1 files changed, 131 insertions, 429 deletions
diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c
index a564fbfc6..72a6acfb1 100644
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -14,58 +14,10 @@
#include <limits.h>
#include <poll.h>
#include <unistd.h>
-#include <dlfcn.h>
#include <alsa/asoundlib.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.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
-
#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;
@@ -125,15 +76,13 @@ enum stream_state {
};
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;
@@ -158,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
@@ -292,14 +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;
@@ -314,173 +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. */
+ /* 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;
}
@@ -536,7 +395,7 @@ alsa_run(cubeb * ctx)
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);
}
@@ -586,26 +445,26 @@ 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;
}
@@ -614,7 +473,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
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;
}
@@ -623,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,22 +505,22 @@ 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;
}
@@ -670,7 +529,7 @@ init_local_config_with_workaround(char const * pcm_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;
}
@@ -681,12 +540,12 @@ init_local_config_with_workaround(char const * pcm_name)
}
/* 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);
+ 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;
}
@@ -697,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;
}
@@ -716,21 +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);
@@ -743,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;
@@ -799,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;
@@ -810,30 +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);
@@ -842,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);
@@ -876,7 +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);
}
@@ -886,12 +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) {
@@ -933,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 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;
@@ -962,17 +794,23 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * 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;
@@ -1004,27 +842,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * 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;
@@ -1037,7 +868,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
latency_us = latency_us < min_latency ? min_latency: latency_us;
}
- r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+ 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) {
@@ -1045,22 +876,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
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);
+ 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);
return CUBEB_ERROR;
@@ -1071,45 +900,6 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * 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)
{
@@ -1122,15 +912,10 @@ alsa_stream_destroy(cubeb_stream * stm)
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;
@@ -1149,8 +934,6 @@ alsa_stream_destroy(cubeb_stream * stm)
ctx->active_streams -= 1;
pthread_mutex_unlock(&ctx->mutex);
- free(stm->buffer);
-
free(stm);
}
@@ -1174,14 +957,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
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;
}
@@ -1202,34 +983,34 @@ 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;
}
@@ -1253,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);
@@ -1289,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);
@@ -1305,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;
@@ -1321,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;
@@ -1331,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;
@@ -1347,7 +1111,7 @@ 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)) {
+ if (snd_pcm_delay(stm->pcm, &delay)) {
return CUBEB_ERROR;
}
@@ -1367,84 +1131,22 @@ 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,
+ .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_reset_default_device = NULL,
.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,