[pulseaudio-discuss] [PATCH 19/22] loopback: Only use controller weight after target latency has been crossed twice

Georg Chini georg at chini.tk
Mon Feb 13 12:02:16 UTC 2017


The previous patch slows down initial convergence. Therefore do not use
the controller weight until we can assume that we reached an equilibrium.
Because it takes some time before the reported latency values are reliable,
assume that a steady state is reached when the target latency has been
crossed twice.

---
 src/modules/module-loopback.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index fc4d363..14253a8 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -133,6 +133,7 @@ struct userdata {
     uint32_t iteration_counter;
     uint32_t underrun_counter;
     uint32_t adjust_counter;
+    uint32_t target_latency_cross_counter;
 
     /* Various booleans */
     bool fixed_alsa_source;
@@ -281,10 +282,14 @@ static uint32_t rate_controller(
      * of 0.5 Hz needs to be applied by the controller when the latency
      * difference gets larger than the threshold. The weight follows
      * from the definition of the controller. The minimum will only
-     * be reached when one adjust threshold away from the target. */
+     * be reached when one adjust threshold away from the target. Start
+     * using the weight after the target latency has been reached for the
+     * second time to accelerate initial convergence. The second time has
+     * been chosen because it takes a while before the smoother returns
+     * reliable latencies. */
     controller_weight = 1;
     min_weight = PA_CLAMP(0.5 / (double)base_rate * (100.0 + (double)u->real_adjust_time / u->adjust_threshold), 0, 1.0);
-    if ((double)abs((int)(old_rate - base_rate_with_drift)) / base_rate_with_drift < 0.002)
+    if ((double)abs((int)(old_rate - base_rate_with_drift)) / base_rate_with_drift < 0.002 && u->target_latency_cross_counter >= 2)
         controller_weight = PA_CLAMP((double)abs(latency_difference_at_optimum_rate) / u->adjust_threshold * min_weight, min_weight, 1.0);
 
     /* Calculate next rate that is not more than 2‰ away from the last rate */
@@ -501,6 +506,10 @@ static void adjust_rates(struct userdata *u) {
                 u->drift_compensation_rate + base_rate,
                 (int32_t)(new_rate - base_rate));
 
+    /* If the latency difference changed sign, we have crossed the target latency. */
+    if ((int64_t)latency_difference * u->last_latency_difference < 0)
+        u->target_latency_cross_counter++;
+
     /* Save current latency difference at new rate for next cycle and reset flags */
     u->last_latency_difference = current_source_sink_latency + current_buffer_latency * old_rate / new_rate - final_latency;
 
@@ -856,10 +865,11 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     u->iteration_counter = 0;
     u->underrun_counter = 0;
 
-    /* Reset booleans and latency error */
+    /* Reset booleans, latency error and counter */
     u->source_sink_changed = true;
     u->underrun_occured = false;
     u->source_latency_offset_changed = false;
+    u->target_latency_cross_counter = 0;
     u->latency_error = 0;
 
     /* Send a mesage to the output thread that the source has changed.
@@ -1231,10 +1241,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     u->iteration_counter = 0;
     u->underrun_counter = 0;
 
-    /* Reset booleans and latency error */
+    /* Reset booleans, latency error and counter */
     u->source_sink_changed = true;
     u->underrun_occured = false;
     u->sink_latency_offset_changed = false;
+    u->target_latency_cross_counter = 0;
     u->latency_error = 0;
 
     /* Send a message to the output thread that the sink has changed */
@@ -1347,6 +1358,7 @@ static int loopback_process_msg_cb(pa_msgobject *o, int code, void *userdata, in
 
             u->underrun_counter++;
             u->underrun_occured = true;
+            u->target_latency_cross_counter = 0;
 
             return 0;
     }
@@ -1369,6 +1381,9 @@ static pa_hook_result_t sink_port_latency_offset_changed_cb(pa_core *core, pa_si
     u->sink_latency_offset = sink->port_latency_offset;
     update_minimum_latency(u, sink, true);
 
+    /* We might need to adjust again, reset counter */
+    u->target_latency_cross_counter = 0;
+
     return PA_HOOK_OK;
 }
 
@@ -1387,6 +1402,9 @@ static pa_hook_result_t source_port_latency_offset_changed_cb(pa_core *core, pa_
     u->source_latency_offset = source->port_latency_offset;
     update_minimum_latency(u, u->sink_input->sink, true);
 
+    /* We might need to adjust again, reset counter */
+    u->target_latency_cross_counter = 0;
+
     /* Update variable in output thread */
     if (u->sink_input->sink)
         pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SOURCE_LATENCY_OFFSET_CHANGED, NULL, u->source_latency_offset, NULL);
@@ -1514,6 +1532,7 @@ int pa__init(pa_module *m) {
     u->sink_latency_offset_changed = false;
     u->latency_error = 0;
     u->adjust_threshold = adjust_threshold;
+    u->target_latency_cross_counter = 0;
 
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
     if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
-- 
2.10.1



More information about the pulseaudio-discuss mailing list