[07/10] loopback: Only use controller weight after target latency has been crossed twice

Submitted by Georg Chini on April 9, 2018, 5:17 p.m.

Details

Message ID 20180409171707.17696-8-georg@chini.tk
State New
Headers show
Series "loopback: Optimize latency stabilization" ( rev: 1 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Georg Chini April 9, 2018, 5:17 p.m.
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(-)

Patch hide | download patch | download mbox

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 9f18ad1f..eeb93264 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 */
@@ -512,6 +517,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;
 
@@ -883,10 +892,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.
@@ -1278,10 +1288,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;
 
     u->output_thread_info.pop_called = false;
@@ -1398,6 +1409,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;
             pa_log_debug("Underrun detected, counter incremented to %u", u->underrun_counter);
 
             return 0;
@@ -1425,6 +1437,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;
 }
 
@@ -1440,6 +1455,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;
+
     return PA_HOOK_OK;
 }
 
@@ -1575,6 +1593,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;
     u->initial_adjust_pending = true;
 
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;