On Wed, 2015-11-11 at 21:13 +0100, Georg Chini wrote: > 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 Thanks, I'll read it. > I also forwarded you some math with more details on Friday. I don't think I received that message. -- Tanu