mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 03:15:46 -04:00
dep/cubeb: Update to dc511c6
This commit is contained in:
@ -8,56 +8,56 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#define _BSD_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb/cubeb.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.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 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);
|
||||
@ -101,7 +101,8 @@ 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;
|
||||
|
||||
@ -116,13 +117,7 @@ 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. */
|
||||
@ -146,7 +141,8 @@ 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;
|
||||
@ -294,8 +290,10 @@ 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));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -327,7 +325,8 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
/* 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);
|
||||
WRAP(snd_pcm_poll_descriptors_revents)
|
||||
(stm->pcm, stm->fds, stm->nfds, &revents);
|
||||
|
||||
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
|
||||
|
||||
@ -337,8 +336,9 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
return RUNNING;
|
||||
}
|
||||
|
||||
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */
|
||||
if ((unsigned int) avail > stm->buffer_size) {
|
||||
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
|
||||
*/
|
||||
if ((unsigned int)avail > stm->buffer_size) {
|
||||
avail = stm->buffer_size;
|
||||
}
|
||||
|
||||
@ -352,7 +352,7 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
// TODO: should it be marked as DRAINING?
|
||||
}
|
||||
|
||||
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail);
|
||||
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);
|
||||
|
||||
if (got < 0) {
|
||||
avail = got; // the error handler below will recover us
|
||||
@ -366,18 +366,24 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
|
||||
/* 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)) {
|
||||
(!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;
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote);
|
||||
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
|
||||
other_buffer, wrote);
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
if (wrote < 0) {
|
||||
@ -392,14 +398,17 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
}
|
||||
|
||||
/* 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 &&
|
||||
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);
|
||||
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) {
|
||||
if (stm->other_stream &&
|
||||
got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
|
||||
got = stm->other_stream->bufframes;
|
||||
}
|
||||
|
||||
@ -419,11 +428,13 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
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;
|
||||
double drain_time = (double)drain_frames / stm->params.rate;
|
||||
|
||||
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
||||
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;
|
||||
|
||||
@ -440,12 +451,12 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
snd_pcm_sframes_t wrote;
|
||||
|
||||
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||
float * b = (float *) stm->buffer;
|
||||
float * b = (float *)stm->buffer;
|
||||
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
|
||||
b[i] *= stm->volume;
|
||||
}
|
||||
} else {
|
||||
short * b = (short *) stm->buffer;
|
||||
short * b = (short *)stm->buffer;
|
||||
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
|
||||
b[i] *= stm->volume;
|
||||
}
|
||||
@ -467,8 +478,7 @@ alsa_process_stream(cubeb_stream * stm)
|
||||
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
|
||||
|
||||
/* Capture pcm must be started after initial setup/recover */
|
||||
if (avail >= 0 &&
|
||||
stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
||||
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);
|
||||
}
|
||||
@ -533,7 +543,8 @@ 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);
|
||||
@ -548,7 +559,8 @@ 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);
|
||||
}
|
||||
@ -593,7 +605,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
||||
|
||||
r = WRAP(snd_config_get_string)(slave_pcm, &string);
|
||||
if (r >= 0) {
|
||||
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
|
||||
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
|
||||
&slave_def);
|
||||
if (r < 0) {
|
||||
return NULL;
|
||||
}
|
||||
@ -611,7 +624,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
||||
}
|
||||
|
||||
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);
|
||||
@ -633,7 +646,8 @@ 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)
|
||||
{
|
||||
@ -646,11 +660,11 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||
|
||||
lconf = NULL;
|
||||
|
||||
if (*WRAP(snd_config) == NULL) {
|
||||
if (WRAP(snd_config) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
|
||||
r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
|
||||
if (r < 0) {
|
||||
return NULL;
|
||||
}
|
||||
@ -667,7 +681,7 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||
}
|
||||
|
||||
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);
|
||||
@ -675,12 +689,14 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||
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. */
|
||||
/* 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);
|
||||
if (r < 0) {
|
||||
break;
|
||||
@ -722,13 +738,15 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||
}
|
||||
|
||||
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, char const * pcm_name,
|
||||
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 = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
|
||||
local_config);
|
||||
} else {
|
||||
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
||||
}
|
||||
@ -819,12 +837,13 @@ alsa_init(cubeb ** context, char const * context_name)
|
||||
}
|
||||
}
|
||||
|
||||
#define LOAD(x) { \
|
||||
cubeb_##x = dlsym(libasound, #x); \
|
||||
if (!cubeb_##x) { \
|
||||
dlclose(libasound); \
|
||||
return CUBEB_ERROR; \
|
||||
} \
|
||||
#define LOAD(x) \
|
||||
{ \
|
||||
cubeb_##x = dlsym(libasound, #x); \
|
||||
if (!cubeb_##x) { \
|
||||
dlclose(libasound); \
|
||||
return CUBEB_ERROR; \
|
||||
} \
|
||||
}
|
||||
|
||||
LIBASOUND_API_VISIT(LOAD);
|
||||
@ -876,7 +895,8 @@ 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, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
|
||||
NULL);
|
||||
if (r >= 0) {
|
||||
alsa_locked_pcm_close(dummy);
|
||||
}
|
||||
@ -886,7 +906,8 @@ 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, CUBEB_ALSA_PCM_NAME,
|
||||
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) {
|
||||
@ -944,17 +965,17 @@ alsa_destroy(cubeb * ctx)
|
||||
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,
|
||||
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)
|
||||
cubeb_state_callback state_callback, void * user_ptr)
|
||||
{
|
||||
(void)stream_name;
|
||||
cubeb_stream * stm;
|
||||
@ -962,7 +983,8 @@ 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;
|
||||
char const * pcm_name =
|
||||
deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;
|
||||
|
||||
assert(ctx && stream);
|
||||
|
||||
@ -1018,7 +1040,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||
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, pcm_name, stm->stream_type,
|
||||
ctx->local_config);
|
||||
if (r < 0) {
|
||||
alsa_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
@ -1034,12 +1057,12 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * 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);
|
||||
stm->params.channels, stm->params.rate, 1,
|
||||
latency_us);
|
||||
if (r < 0) {
|
||||
alsa_stream_destroy(stm);
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
@ -1048,9 +1071,11 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||
r = WRAP(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 */
|
||||
/* 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));
|
||||
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);
|
||||
@ -1059,7 +1084,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||
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);
|
||||
assert((nfds_t)r == stm->nfds);
|
||||
|
||||
if (alsa_register_stream(ctx, stm) != 0) {
|
||||
alsa_stream_destroy(stm);
|
||||
@ -1077,22 +1102,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
||||
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)
|
||||
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;
|
||||
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,
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1116,8 +1142,7 @@ 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;
|
||||
@ -1159,7 +1184,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;
|
||||
@ -1169,7 +1194,8 @@ 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;
|
||||
}
|
||||
@ -1192,7 +1218,8 @@ 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;
|
||||
@ -1202,7 +1229,8 @@ 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 = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NO_AUTO_RESAMPLE);
|
||||
if (r < 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
@ -1235,7 +1263,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
||||
}
|
||||
|
||||
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
|
||||
@ -1331,7 +1360,7 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
assert(delay >= 0);
|
||||
|
||||
*position = 0;
|
||||
if (stm->stream_position >= (snd_pcm_uframes_t) delay) {
|
||||
if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
|
||||
*position = stm->stream_position - delay;
|
||||
}
|
||||
|
||||
@ -1346,7 +1375,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. */
|
||||
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
|
||||
*/
|
||||
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
@ -1371,7 +1401,7 @@ static int
|
||||
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
cubeb_device_info* device = NULL;
|
||||
cubeb_device_info * device = NULL;
|
||||
|
||||
if (!context)
|
||||
return CUBEB_ERROR;
|
||||
@ -1390,13 +1420,13 @@ alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
}
|
||||
|
||||
char const * a_name = "default";
|
||||
device = (cubeb_device_info *) calloc(1, sizeof(cubeb_device_info));
|
||||
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->devid = (cubeb_devid)device->device_id;
|
||||
device->friendly_name = a_name;
|
||||
device->group_id = a_name;
|
||||
device->vendor_name = a_name;
|
||||
@ -1423,32 +1453,30 @@ alsa_device_collection_destroy(cubeb * context,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
assert(collection->count == 1);
|
||||
(void) context;
|
||||
(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_reset_default_device = NULL,
|
||||
.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 = 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};
|
||||
|
Reference in New Issue
Block a user