[pulseaudio-discuss] [PATCH v6 24/25] loopback: Add adjust_threshold_usec parameter

Georg Chini georg at chini.tk
Sun Jun 5 19:05:27 UTC 2016


In most situations, the P-controller is too sensitive and therefore exhibits rate hunting.
To avoid rate hunting, the sensibility of the controller is set by the new parameter
adjust_threshold_usec. The parameter value is the deviation from the target latency in usec
which is needed to produce a 1 Hz deviation from the optimum sample rate.
The default is set to 200 usec, which should be sufficient in most cases.
For details of the implementation, refer to "rate_estimator.odt".

---
 src/modules/module-loopback.c | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 35a817b..f4e2c2f 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, values >= 100 are in ms> "
         "latency_msec=<latency in ms> "
+        "adjust_threshold_usec=<threshold for latency adjustment in usec> "
         "low_device_latency=<boolean, use half of the normal device latency> "
         "format=<sample format> "
         "rate=<sample rate> "
@@ -62,6 +63,8 @@ PA_MODULE_USAGE(
 
 #define FILTER_PARAMETER 0.125
 
+#define DEFAULT_ADJUST_THRESHOLD_USEC 200
+
 #define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
 
 #define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
@@ -97,6 +100,7 @@ struct userdata {
 
     /* Values from command line configuration */
     pa_usec_t latency;
+    uint32_t adjust_threshold;
     pa_usec_t adjust_time;
     bool low_device_latency;
 
@@ -151,6 +155,7 @@ static const char* const valid_modargs[] = {
     "sink",
     "adjust_time",
     "latency_msec",
+    "adjust_threshold_usec",
     "low_device_latency",
     "format",
     "rate",
@@ -213,10 +218,9 @@ static void teardown(struct userdata *u) {
 }
 
 /* rate controller
- * - maximum deviation from base rate is less than 1%
- * - controller step size is limited to 2.01‰
+ * - maximum deviation from optimum rate for P-controller is less than 1%
+ * - P-controller step size is limited to 2.01‰
  * - implements adaptive re-sampling
- * - exhibits hunting with USB or Bluetooth sources
  */
 static uint32_t rate_controller(
                 struct userdata *u,
@@ -225,7 +229,21 @@ static uint32_t rate_controller(
                 int32_t latency_difference_usec_2) {
 
     double new_rate_1, new_rate_2, new_rate;
-    double min_cycles_1, min_cycles_2, drift_rate, latency_drift;
+    double min_cycles_1, min_cycles_2, drift_rate, latency_drift, controller_weight, min_weight;
+    uint32_t base_rate_with_drift;
+
+    base_rate_with_drift = (int)(base_rate + u->drift_compensation_rate);
+
+    /* if we are less than 2‰ away from the base_rate, lower weight of the
+     * P-controller. The weight is determined by the fact that a correction
+     * 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. */
+    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)
+        controller_weight = PA_CLAMP((double)abs(latency_difference_usec) / u->adjust_threshold * min_weight, min_weight, 1.0);
 
     /* Calculate next rate that is not more than 2‰ away from the last rate */
     min_cycles_1 = (double)abs(latency_difference_usec) / u->real_adjust_time / 0.002 + 1;
@@ -234,10 +252,11 @@ static uint32_t rate_controller(
     /* Calculate best rate to correct the current latency offset, limit at
      * 1% difference from base_rate */
     min_cycles_2 = (double)abs(latency_difference_usec) / u->real_adjust_time / 0.01 + 1;
-    new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / min_cycles_2 / u->real_adjust_time);
+    new_rate_2 = (double)base_rate * (1.0 + controller_weight * latency_difference_usec / min_cycles_2 / u->real_adjust_time);
 
-    /* Choose the rate that is nearer to base_rate */
-    if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+    /* Choose the rate that is nearer to base_rate unless we are already near
+     * to the desired latency and rate */
+    if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate) && controller_weight > 0.99)
         new_rate = new_rate_1;
     else
         new_rate = new_rate_2;
@@ -1109,7 +1128,7 @@ 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, adjust_threshold;
     pa_sample_spec ss;
     pa_channel_map map;
     bool format_set = false;
@@ -1189,6 +1208,12 @@ 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;
 
+    adjust_threshold = DEFAULT_ADJUST_THRESHOLD_USEC;
+    if (pa_modargs_get_value_u32(ma, "adjust_threshold_usec", &adjust_threshold) < 0 || adjust_threshold < 1 || adjust_threshold > 10000) {
+        pa_log_info("Invalid adjust threshold specification");
+        goto fail;
+    }
+
     latency_msec = DEFAULT_LATENCY_MSEC;
     if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 30000) {
         pa_log("Invalid latency specification");
@@ -1212,6 +1237,7 @@ int pa__init(pa_module *m) {
     u->extra_latency = 0;
     u->latency_error = 0;
     u->low_device_latency = low_device_latency;
+    u->adjust_threshold = adjust_threshold;
 
     /* The default adjust time is set to 1s. The adjust time given on the
      * command line is considered to be in seconds if it is below 100,
-- 
2.8.1



More information about the pulseaudio-discuss mailing list