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