[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