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
_______________________________________________
pulseaudio-discuss mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss