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

Georg Chini georg at chini.tk
Wed Nov 11 12:13:35 PST 2015


On 11.11.2015 19:36, Tanu Kaskinen wrote:
> 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()?
>
Hi Tanu,

the comment regarding Ziegler-Nichols method was added by
Alexander Patrakov. I had a long discussion with him back in
February which explains most of the background, so I would like
to point you to the following thread:

http://thread.gmane.org/gmane.comp.audio.pulseaudio.general/22753

I also forwarded you some math with more details on Friday.

I hope this will answer your questions.

BTW: u(t) in the equation above is not the new control value but the
difference between the control value and the ideal value, in our case
u(t) = new_rate - base_rate and e(t) = latency_difference_usec.
If you now set min_cycles = 1, my equation is the classical P-controller.


Regards
              Georg





More information about the pulseaudio-discuss mailing list