[pulseaudio-discuss] [PATCH v6 24/25] loopback: Add adjust_threshold_usec parameter
Georg Chini
georg at chini.tk
Sun Jun 5 19:05:27 UTC 2016
In most situations, the P-controller is too sensitive and therefore exhibits rate hunting.
To avoid rate hunting, the sensibility of the controller is set by the new parameter
adjust_threshold_usec. The parameter value is the deviation from the target latency in usec
which is needed to produce a 1 Hz deviation from the optimum sample rate.
The default is set to 200 usec, which should be sufficient in most cases.
For details of the implementation, refer to "rate_estimator.odt".
---
src/modules/module-loopback.c | 42 ++++++++++++++++++++++++++++++++++--------
1 file changed, 34 insertions(+), 8 deletions(-)
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 35a817b..f4e2c2f 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -47,6 +47,7 @@ PA_MODULE_USAGE(
"sink=<sink to connect to> "
"adjust_time=<how often to readjust rates in s, values >= 100 are in ms> "
"latency_msec=<latency in ms> "
+ "adjust_threshold_usec=<threshold for latency adjustment in usec> "
"low_device_latency=<boolean, use half of the normal device latency> "
"format=<sample format> "
"rate=<sample rate> "
@@ -62,6 +63,8 @@ PA_MODULE_USAGE(
#define FILTER_PARAMETER 0.125
+#define DEFAULT_ADJUST_THRESHOLD_USEC 200
+
#define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
#define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
@@ -97,6 +100,7 @@ struct userdata {
/* Values from command line configuration */
pa_usec_t latency;
+ uint32_t adjust_threshold;
pa_usec_t adjust_time;
bool low_device_latency;
@@ -151,6 +155,7 @@ static const char* const valid_modargs[] = {
"sink",
"adjust_time",
"latency_msec",
+ "adjust_threshold_usec",
"low_device_latency",
"format",
"rate",
@@ -213,10 +218,9 @@ static void teardown(struct userdata *u) {
}
/* rate controller
- * - maximum deviation from base rate is less than 1%
- * - controller step size is limited to 2.01‰
+ * - maximum deviation from optimum rate for P-controller is less than 1%
+ * - P-controller step size is limited to 2.01‰
* - implements adaptive re-sampling
- * - exhibits hunting with USB or Bluetooth sources
*/
static uint32_t rate_controller(
struct userdata *u,
@@ -225,7 +229,21 @@ static uint32_t rate_controller(
int32_t latency_difference_usec_2) {
double new_rate_1, new_rate_2, new_rate;
- double min_cycles_1, min_cycles_2, drift_rate, latency_drift;
+ double min_cycles_1, min_cycles_2, drift_rate, latency_drift, controller_weight, min_weight;
+ uint32_t base_rate_with_drift;
+
+ base_rate_with_drift = (int)(base_rate + u->drift_compensation_rate);
+
+ /* if we are less than 2‰ away from the base_rate, lower weight of the
+ * P-controller. The weight is determined by the fact that a correction
+ * of 0.5 Hz needs to be applied by the controller when the latency
+ * difference gets larger than the threshold. The weight follows
+ * from the definition of the controller. The minimum will only
+ * be reached when one adjust threshold away from the target. */
+ controller_weight = 1;
+ min_weight = PA_CLAMP(0.5 / (double)base_rate * (100.0 + (double)u->real_adjust_time / u->adjust_threshold), 0, 1.0);
+ if ((double)abs((int)(old_rate - base_rate_with_drift)) / base_rate_with_drift < 0.002)
+ controller_weight = PA_CLAMP((double)abs(latency_difference_usec) / u->adjust_threshold * min_weight, min_weight, 1.0);
/* Calculate next rate that is not more than 2‰ away from the last rate */
min_cycles_1 = (double)abs(latency_difference_usec) / u->real_adjust_time / 0.002 + 1;
@@ -234,10 +252,11 @@ static uint32_t rate_controller(
/* Calculate best rate to correct the current latency offset, limit at
* 1% difference from base_rate */
min_cycles_2 = (double)abs(latency_difference_usec) / u->real_adjust_time / 0.01 + 1;
- new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / min_cycles_2 / u->real_adjust_time);
+ new_rate_2 = (double)base_rate * (1.0 + controller_weight * latency_difference_usec / min_cycles_2 / u->real_adjust_time);
- /* Choose the rate that is nearer to base_rate */
- if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+ /* Choose the rate that is nearer to base_rate unless we are already near
+ * to the desired latency and rate */
+ if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate) && controller_weight > 0.99)
new_rate = new_rate_1;
else
new_rate = new_rate_2;
@@ -1109,7 +1128,7 @@ int pa__init(pa_module *m) {
pa_source *source = NULL;
pa_source_output_new_data source_output_data;
bool source_dont_move;
- uint32_t latency_msec;
+ uint32_t latency_msec, adjust_threshold;
pa_sample_spec ss;
pa_channel_map map;
bool format_set = false;
@@ -1189,6 +1208,12 @@ int pa__init(pa_module *m) {
if (pa_modargs_get_value(ma, "channels", NULL) || pa_modargs_get_value(ma, "channel_map", NULL))
channels_set = true;
+ adjust_threshold = DEFAULT_ADJUST_THRESHOLD_USEC;
+ if (pa_modargs_get_value_u32(ma, "adjust_threshold_usec", &adjust_threshold) < 0 || adjust_threshold < 1 || adjust_threshold > 10000) {
+ pa_log_info("Invalid adjust threshold specification");
+ goto fail;
+ }
+
latency_msec = DEFAULT_LATENCY_MSEC;
if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 30000) {
pa_log("Invalid latency specification");
@@ -1212,6 +1237,7 @@ int pa__init(pa_module *m) {
u->extra_latency = 0;
u->latency_error = 0;
u->low_device_latency = low_device_latency;
+ u->adjust_threshold = adjust_threshold;
/* The default adjust time is set to 1s. The adjust time given on the
* command line is considered to be in seconds if it is below 100,
--
2.8.1
More information about the pulseaudio-discuss
mailing list