[pulseaudio-discuss] [PATCH 11/13] loopback: Don't change rate abruptly

Georg Chini georg at chini.tk
Wed Feb 25 10:43:23 PST 2015


If a big latency difference forms (due to underrun or source/sink
switching), the controller has to change the rate by a big step. This may
be noticeable for a specially trained ear of a musician, so avoid it.
---
 src/modules/module-loopback.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 6b48fc6..d22afb5 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -182,24 +182,35 @@ static void teardown(struct userdata *u) {
 
 /* rate controller
  * - maximum deviation from base rate is less than 1%
- * - can create audible artifacts by changing the rate too quickly
+ * - maximum rate step size is less than 2‰
  * - deadband to handle error of latency measurement
  */
 static uint32_t rate_controller(
-                uint32_t base_rate,
+                uint32_t base_rate, uint32_t old_rate,
                 pa_usec_t adjust_time,
                 int32_t latency_difference_usec, pa_usec_t latency_error_usec) {
 
-    uint32_t new_rate;
-    double min_cycles;
+    uint32_t new_rate, new_rate_1, new_rate_2;
+    double min_cycles_1, min_cycles_2;
+
+    /* Calculate next rate that is not more than 2‰ away from the last rate */
+    min_cycles_1 = (double)abs(latency_difference_usec) / adjust_time / 0.002 + 1;
+    new_rate_1 = old_rate + base_rate * (double)latency_difference_usec / min_cycles_1 / adjust_time;
 
     /* Calculate best rate to correct the current latency offset, limit at
      * slightly below 1% difference from base_rate */
-    min_cycles = (double)abs(latency_difference_usec) / adjust_time / 0.0095 + 1;
-    new_rate = base_rate * (1.0 + (double)latency_difference_usec / min_cycles / adjust_time);
+    min_cycles_2 = (double)abs(latency_difference_usec) / adjust_time / 0.0095 + 1;
+    new_rate_2 = base_rate * (1.0 + (double)latency_difference_usec / min_cycles_2 / adjust_time);
+
+    /* Choose the rate that is nearer to base_rate */
+    if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+       new_rate = new_rate_1;
+    else
+       new_rate = new_rate_2;
 
-    /* Adjust as good as physics allows (with some safety margin) */
-    if (abs(latency_difference_usec) <= 2.5 * latency_error_usec + adjust_time / 2 / base_rate + 100)
+    /* Adjust as good as physics allows (with some safety margin)
+     * make sure the rate is near enough to the base rate before clipping */
+    if (abs(latency_difference_usec) <= 2.5 * latency_error_usec + adjust_time / 2 / base_rate + 100 && abs(old_rate - base_rate) < 0.002 * base_rate)
        new_rate = base_rate;
 
     return new_rate;
@@ -280,7 +291,7 @@ static void adjust_rates(struct userdata *u) {
     u->source_sink_changed = false;
 
     /* Calculate new rate */
-    new_rate = rate_controller(base_rate, u->adjust_time, latency_difference, u->latency_error * final_latency);
+    new_rate = rate_controller(base_rate, old_rate, u->adjust_time, latency_difference, u->latency_error * final_latency);
 
     /* Predictor */
     u->next_latency = (corrected_latency * base_rate + (int32_t)(base_rate - new_rate) * (int64_t)u->adjust_time) / new_rate;
-- 
2.1.4



More information about the pulseaudio-discuss mailing list