In ALSA PCM interface, some parameters are used to configure runtime of PCM substream independently of actual hardware. These parameters are mainly used to decide the detailed timing to start/stop PCM substream and release I/O blocking state of application. These parameters are represented and delivered by a structure.
In alsa-lib PCM API, the structure is hidden from userspace applications. The applications can set/get actual parameters by helper functions.
In aplay, three of the parameters are configurable. This commit adds support for them. When no options are given, default values are used.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- axfer/xfer-libasound.c | 86 ++++++++++++++++++++++++++++++++++++++++-- axfer/xfer-libasound.h | 4 ++ 2 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/axfer/xfer-libasound.c b/axfer/xfer-libasound.c index a23021e..1e709e0 100644 --- a/axfer/xfer-libasound.c +++ b/axfer/xfer-libasound.c @@ -17,7 +17,7 @@ enum no_short_opts { OPT_TEST_NOWAIT, };
-#define S_OPTS "D:NMF:B:" +#define S_OPTS "D:NMF:B:A:R:T:" static const struct option l_opts[] = { {"device", 1, 0, 'D'}, {"nonblock", 0, 0, 'N'}, @@ -26,6 +26,9 @@ static const struct option l_opts[] = { {"buffer-time", 1, 0, 'B'}, {"period-size", 1, 0, OPT_PERIOD_SIZE}, {"buffer-size", 1, 0, OPT_BUFFER_SIZE}, + {"avail-min", 1, 0, 'A'}, + {"start-delay", 1, 0, 'R'}, + {"stop-delay", 1, 0, 'T'}, // For debugging. {"fatal-errors", 0, 0, OPT_FATAL_ERRORS}, {"test-nowait", 0, 0, OPT_TEST_NOWAIT}, @@ -68,6 +71,12 @@ static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key, state->frames_per_period = arg_parse_decimal_num(optarg, &err); else if (key == OPT_BUFFER_SIZE) state->frames_per_buffer = arg_parse_decimal_num(optarg, &err); + else if (key == 'A') + state->msec_for_avail_min = arg_parse_decimal_num(optarg, &err); + else if (key == 'R') + state->msec_for_start_threshold = arg_parse_decimal_num(optarg, &err); + else if (key == 'T') + state->msec_for_stop_threshold = arg_parse_decimal_num(optarg, &err); else if (key == OPT_FATAL_ERRORS) state->finish_at_xrun = true; else if (key == OPT_TEST_NOWAIT) @@ -377,8 +386,76 @@ static int retrieve_actual_hw_params(snd_pcm_hw_params_t *hw_params,
static int configure_sw_params(struct libasound_state *state, unsigned int frames_per_second, - unsigned int frames_per_buffer) + unsigned int frames_per_buffer, + unsigned int msec_for_avail_min, + unsigned int msec_for_start_threshold, + unsigned int msec_for_stop_threshold) { + snd_pcm_uframes_t frame_count; + int err; + + if (msec_for_avail_min > 0) { + frame_count = msec_for_avail_min * frames_per_second / 1000000; + if (frame_count == 0 || frame_count > frames_per_buffer) { + logging(state, + "The msec for 'avail_min' is too %s: %u " + "msec (%lu frames at %u).\n", + frame_count == 0 ? "small" : "large", + msec_for_avail_min, frame_count, + frames_per_second); + return -EINVAL; + } + err = snd_pcm_sw_params_set_avail_min(state->handle, + state->sw_params, frame_count); + if (err < 0) { + logging(state, + "Fail to configure 'avail-min'.\n"); + return -EINVAL; + } + } + + if (msec_for_start_threshold > 0) { + frame_count = msec_for_start_threshold * frames_per_second / + 1000000; + if (frame_count == 0 || frame_count > frames_per_buffer) { + logging(state, + "The msec for 'start-delay' is too %s: %u " + "msec (%lu frames at %u).\n", + frame_count == 0 ? "small" : "large", + msec_for_start_threshold, frame_count, + frames_per_second); + return -EINVAL; + } + err = snd_pcm_sw_params_set_start_threshold(state->handle, + state->sw_params, frame_count); + if (err < 0) { + logging(state, + "Fail to configure 'start-delay'.\n"); + return -EINVAL; + } + } + + if (msec_for_stop_threshold > 0) { + frame_count = msec_for_stop_threshold * frames_per_second / + 1000000; + if (frame_count == 0 || frame_count > frames_per_buffer) { + logging(state, + "The msec for 'stop-delay' is too %s: %u " + "msec (%lu frames at %u).\n", + frame_count == 0 ? "small" : "large", + msec_for_stop_threshold, frame_count, + frames_per_second); + return -EINVAL; + } + err = snd_pcm_sw_params_set_stop_threshold(state->handle, + state->sw_params, frame_count); + if (err < 0) { + logging(state, + "Fail to configure 'stop-delay'.\n"); + return -EINVAL; + } + } + return snd_pcm_sw_params(state->handle, state->sw_params); }
@@ -444,7 +521,10 @@ static int xfer_libasound_pre_process(struct xfer_context *xfer, return err;
err = configure_sw_params(state, *frames_per_second, - *frames_per_buffer); + *frames_per_buffer, + state->msec_for_avail_min, + state->msec_for_start_threshold, + state->msec_for_stop_threshold); if (err < 0) { logging(state, "Current software parameters:\n"); snd_pcm_sw_params_dump(state->sw_params, state->log); diff --git a/axfer/xfer-libasound.h b/axfer/xfer-libasound.h index 4456fab..113c1b9 100644 --- a/axfer/xfer-libasound.h +++ b/axfer/xfer-libasound.h @@ -35,6 +35,10 @@ struct libasound_state { unsigned int frames_per_period; unsigned int frames_per_buffer;
+ unsigned int msec_for_avail_min; + unsigned int msec_for_start_threshold; + unsigned int msec_for_stop_threshold; + bool finish_at_xrun:1; bool nonblock:1; bool mmap:1;