[pulseaudio-discuss] [PATCH 04/13] loopback: Adjust rates based on latency difference

Tanu Kaskinen tanuk at iki.fi
Wed Nov 11 10:36:23 PST 2015


On Wed, 2015-02-25 at 19:43 +0100, Georg Chini wrote:
> For small values of latency_difference, this forms a classical
> P-controller between the observed value of latency and the controlled
> sample rate of the sink input. The coefficient aims for the full
> correction of the observed difference to the next cycle - i.e. the
> controller is tuned optimally according to
> https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method
> For higher latency values the controller limits the deviation from
> the base rate to 0.95%.
> ---
>  src/modules/module-loopback.c | 44 ++++++++++++++++++++++++-------------------
>  1 file changed, 25 insertions(+), 19 deletions(-)
> 
> diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
> index 3532728..372c3ed 100644
> --- a/src/modules/module-loopback.c
> +++ b/src/modules/module-loopback.c
> @@ -169,9 +169,30 @@ 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
> + * - exhibits hunting with USB or Bluetooth sources
> + */
> +static uint32_t rate_controller(
> +                uint32_t base_rate,
> +                pa_usec_t adjust_time,
> +                int32_t latency_difference_usec) {
> +
> +    uint32_t new_rate;
> +    double min_cycles;
> +
> +    /* 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);
> +
> +    return new_rate;
> +}

Sorry for being obtuse, but I don't follow what this simple bit of code
is doing. You mentioned "P-controller" and the "Ziegler-Nichols
method". I followed the Wikipedia link, and found that a P-controller
is a very simple thing:

u(t) = Kp * e(t)

where

u(t): the new control variable value (the new sink input rate)

Kp: a tunable parameter (a magic number)

e(t): the error value, i.e. the difference between the current process
variable value and the target value (current latency minus configured
latency)

The Ziegler-Nichols method can be used to choose Kp. For a P-controller 
Kp is defined as

Kp = 0.5 * Ku

where

Ku: a number that, when used in place of Kp, makes u(t) oscillate in a
stable manner

(A sidenote: I probably have understood something wrong, because Kp is
a plain number, and u(t) and e(t) have different units, so there
appears to be a unit mismatch. u(t) is a frequency and e(t) is a time
amount.)

Figuring out Ku seems to require having an initial calibration phase
where various Ku values are tried and the oscillation of u(t) is
measured. The code doesn't seem to do this. Could you explain how you
have derived the formula in rate_controller()?

-- 
Tanu


More information about the pulseaudio-discuss mailing list