[pulseaudio-discuss] [PATCH 13/13] loopback: add parameter buffer_latency_msec
Georg Chini
georg at chini.tk
Wed Feb 25 10:43:25 PST 2015
The parameter buffer_latency_msec can be used together with latency_msec.
If buffer_latency_msec is specified, the resulting latency
will be latency_msec + buffer_latency_msec. Latency_msec then refers only to
the source/sink latency while buffer_latency_msec specifies the buffer part.
This can be used to save a lot of CPU at low latencies, running 10 ms latency
with latency_msec=6 buffer_latency_msec=4 gives 8% CPU on my system compared to
12% when I only specify latency_msec=10.
Additionally you can go beyond the safe-guard limits that are built in, you can
access the range 1 - 3 ms or lower the buffer latency for fixed latency devices.
Some of my USB devices run fine at a buffer latency of fragment size + 4 ms
instead of the default fragment size + 20 ms.
---
src/modules/module-loopback.c | 81 +++++++++++++++++++++++++++++++------------
1 file changed, 58 insertions(+), 23 deletions(-)
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 7474ef2..3cff092 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -47,6 +47,7 @@ PA_MODULE_USAGE(
"sink=<sink to connect to> "
"adjust_time=<how often to readjust rates in s> "
"latency_msec=<latency in ms> "
+ "buffer_latency_msec=<buffer latency in ms> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
@@ -101,6 +102,7 @@ struct userdata {
bool in_pop;
bool pop_called;
bool source_sink_changed;
+ bool buffer_latency_set;
struct {
int64_t send_counter;
@@ -120,6 +122,7 @@ static const char* const valid_modargs[] = {
"sink",
"adjust_time",
"latency_msec",
+ "buffer_latency_msec",
"format",
"rate",
"channels",
@@ -270,7 +273,10 @@ static void adjust_rates(struct userdata *u) {
source_sink_latency = u->sink_latency_sum / u->sink_adjust_counter +
u->source_latency_sum / u->source_adjust_counter;
- final_latency = PA_MAX(u->latency, source_sink_latency + u->buffer_latency);
+ final_latency = u->latency;
+ if (u->buffer_latency_set)
+ final_latency += u->initial_buffer_latency;
+ final_latency = PA_MAX(final_latency, source_sink_latency + u->buffer_latency);
latency_difference = (int32_t)(corrected_latency - final_latency);
pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms (at the base rate: %0.2f ms, old estimate: %0.2f ms)",
@@ -347,8 +353,12 @@ static void update_adjust_timer(struct userdata *u) {
}
static pa_usec_t get_requested_latency(struct userdata *u) {
+ pa_usec_t requested_latency;
- return PA_MAX(u->configured_sink_latency + u->buffer_latency, u->latency);
+ requested_latency = u->latency;
+ if(u->buffer_latency_set)
+ requested_latency += u->buffer_latency;
+ return PA_MAX(u->configured_sink_latency + u->buffer_latency, requested_latency);
}
/* Called from all contexts */
@@ -440,12 +450,15 @@ static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data,
static void set_source_output_latency(struct userdata *u, pa_source *source) {
pa_usec_t min_latency, max_latency, buffer_msec, latency;
- /* Set lower limit of source latency to 2.333 ms */
+ /* Set lower limit of source latency to 2.333 ms, if buffer
+ * latency is specified, use latency_msec as source latency */
latency = PA_MAX(u->latency / 3, 2.333 * PA_USEC_PER_MSEC);
+ if (u->buffer_latency_set)
+ latency = u->latency;
if(source->flags & PA_SOURCE_DYNAMIC_LATENCY) {
pa_source_get_latency_range(source, &min_latency, &max_latency);
- if (min_latency > latency) {
+ if (min_latency > latency && !u->buffer_latency_set) {
u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(min_latency * 0.75));
pa_log_warn("Cannot set requested source latency, adjusting buffer to %0.2f ms", (double)u->buffer_latency / PA_USEC_PER_MSEC);
}
@@ -456,7 +469,7 @@ static void set_source_output_latency(struct userdata *u, pa_source *source) {
if (latency == 0)
latency = pa_source_get_fixed_latency(source);
buffer_msec = u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC;
- if (u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
+ if (!u->buffer_latency_set && u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
pa_log_warn("Fixed latency device, setting buffer latency to %zd.00 ms", buffer_msec);
u->buffer_latency = buffer_msec * PA_USEC_PER_MSEC;
}
@@ -561,10 +574,12 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
memblockq_adjust(u, 0, true);
return;
}
- if (u->sink_input->sink->flags & PA_SINK_DYNAMIC_LATENCY)
- u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_sink_latency * 0.75));
- else
- u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+ if (!u->buffer_latency_set) {
+ if (u->sink_input->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+ u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_sink_latency * 0.75));
+ else
+ u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+ }
pa_sink_input_get_latency(u->sink_input, &sink_latency);
if (u->send_counter > u->recv_counter)
@@ -612,7 +627,7 @@ static void update_source_requested_latency_cb(pa_source_output *i) {
if (source_latency > u->configured_source_latency) {
pa_log_warn("Source latency increased to %0.2f ms", (double)source_latency / PA_USEC_PER_MSEC);
u->configured_source_latency = source_latency;
- if (u->buffer_latency < source_latency * 0.75)
+ if (!u->buffer_latency_set && u->buffer_latency < source_latency * 0.75)
u->buffer_latency = source_latency * 0.75;
if (!u->source_sink_changed) {
u->source_adjust_counter = 0;
@@ -744,12 +759,15 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
pa_usec_t min_latency, max_latency, buffer_msec, latency;
- /* Set lower limit of sink latency to 2.333 ms */
+ /* Set lower limit of sink latency to 2.333 ms, if buffer
+ * latency is specified, use latency_msec as sink latency */
latency = PA_MAX(u->latency / 3, 2.333 * PA_USEC_PER_MSEC);
+ if (u->buffer_latency_set)
+ latency = u->latency;
if(sink->flags & PA_SINK_DYNAMIC_LATENCY) {
pa_sink_get_latency_range(sink, &min_latency, &max_latency);
- if (min_latency > latency) {
+ if (min_latency > latency && !u->buffer_latency_set) {
u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(min_latency * 0.75));
pa_log_warn("Cannot set requested sink latency, adjusting buffer to %0.2f ms", (double)u->buffer_latency / PA_USEC_PER_MSEC);
}
@@ -760,7 +778,7 @@ static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
if (latency == 0)
latency = pa_sink_get_fixed_latency(sink);
buffer_msec = u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC;
- if (u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
+ if (!u->buffer_latency_set && u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
pa_log_warn("Fixed latency device, setting buffer latency to %zd.00 ms", buffer_msec);
u->buffer_latency = buffer_msec * PA_USEC_PER_MSEC;
}
@@ -884,10 +902,12 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
memblockq_adjust(u, 0, true);
return;
}
- if (u->source_output->source->flags & PA_SOURCE_DYNAMIC_LATENCY)
- u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_source_latency * 0.75));
- else
- u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+ if (!u->buffer_latency_set) {
+ if (u->source_output->source->flags & PA_SOURCE_DYNAMIC_LATENCY)
+ u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_source_latency * 0.75));
+ else
+ u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+ }
pa_source_output_get_latency(u->source_output, &source_latency);
if (u->send_counter > u->recv_counter)
@@ -941,7 +961,7 @@ static void update_sink_requested_latency_cb(pa_sink_input *i) {
if (sink_latency > u->configured_sink_latency) {
pa_log_warn("Sink latency increased to %0.2f ms", (double)sink_latency / PA_USEC_PER_MSEC);
u->configured_sink_latency = sink_latency;
- if (u->buffer_latency < sink_latency * 0.75)
+ if (!u->buffer_latency_set && u->buffer_latency < sink_latency * 0.75)
u->buffer_latency = sink_latency * 0.75;
if (!u->source_sink_changed) {
u->sink_adjust_counter = 0;
@@ -974,12 +994,13 @@ int pa__init(pa_module *m) {
pa_source *source = NULL;
pa_source_output_new_data source_output_data;
bool source_dont_move;
- uint32_t latency_msec;
+ uint32_t latency_msec, buffer_latency_msec;
pa_sample_spec ss;
pa_channel_map map;
bool format_set = false;
bool rate_set = false;
bool channels_set = false;
+ bool buffer_latency_set = false;
pa_memchunk silence;
uint32_t adjust_time_sec;
const char *n;
@@ -1053,7 +1074,17 @@ int pa__init(pa_module *m) {
if (pa_modargs_get_value(ma, "channels", NULL) || pa_modargs_get_value(ma, "channel_map", NULL))
channels_set = true;
- latency_msec = DEFAULT_LATENCY_MSEC;
+ buffer_latency_msec = 0;
+ if (pa_modargs_get_value_u32(ma, "buffer_latency_msec", &buffer_latency_msec) < 0 || buffer_latency_msec > 30000) {
+ pa_log_info("Invalid buffer latency specification");
+ goto fail;
+ }
+ else if (buffer_latency_msec > 0)
+ buffer_latency_set = true;
+
+ latency_msec = 0;
+ if (!buffer_latency_set)
+ latency_msec = DEFAULT_LATENCY_MSEC;
if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec > 30000) {
pa_log("Invalid latency specification");
goto fail;
@@ -1063,8 +1094,12 @@ int pa__init(pa_module *m) {
u->core = m->core;
u->module = m;
u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
- u->initial_buffer_latency = PA_MAX(u->latency / 4, 1.667 * PA_USEC_PER_MSEC);
+ if (buffer_latency_set)
+ u->initial_buffer_latency = (pa_usec_t) buffer_latency_msec * PA_USEC_PER_MSEC;
+ else
+ u->initial_buffer_latency = PA_MAX(u->latency / 4, 1.667 * PA_USEC_PER_MSEC);
u->buffer_latency = u->initial_buffer_latency;
+ u->buffer_latency_set = buffer_latency_set;
u->sink_latency_sum = 0;
u->source_latency_sum = 0;
u->pop_called = false;
@@ -1150,8 +1185,8 @@ int pa__init(pa_module *m) {
u->sink_input->update_sink_requested_latency = update_sink_requested_latency_cb;
u->sink_input->userdata = u;
- if (u->latency < 4 * PA_USEC_PER_MSEC)
- pa_log_warn("Latency limited to 4 ms");
+ if (u->latency < 4 * PA_USEC_PER_MSEC && !buffer_latency_set)
+ pa_log_warn("Latency limited to 4 ms, try buffer_latency_msec together with latency_msec if you know what you are doing");
set_sink_input_latency(u, u->sink_input->sink);
--
2.1.4
More information about the pulseaudio-discuss
mailing list