Verified Commit a5f36231 authored by Sebastian Krzyszkowiak's avatar Sebastian Krzyszkowiak
Browse files

Pick upstream patches to minimize loopback latency

parent a11e0653
Pipeline #48717 failed with stages
From e794d0a21af24ae3d2afae000ad67b17ef56ada2 Mon Sep 17 00:00:00 2001
From: Georg Chini <georg@chini.tk>
Date: Fri, 1 Mar 2019 20:00:52 +0100
Subject: [PATCH] loopback: Add option fast_adjust_threshold_msec
After a suspend/resume cycle of a system, it may be possible that module-loopback
accumulates several seconds of audio in the memblockq before the alsa sink becomes
active again. Also it may be possible for other reasons that the actual loopback
latency is too different from the target latency to be adjusted in a reasonable
time by the normal rate controller.
This patch adds the option fast_adjust_threshold_msec to module-loopback. If set,
the latency will be forcefully adjusted to the target latency by dropping or
inserting samples if the actual latency differs more than fast_adjust_threshold_msec
from the target latency.
Also the calculation of the real adjust time would fail when the system was
suspended because that case was not considered. Now the real adjust time
calculation is skipped if the time passed between two calls of adjust_rates()
appears significantly too long.
---
src/modules/module-loopback.c | 41 +++++++++++++++++++++++++++++++----
1 file changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index e1ab34166..d6b8a3fb6 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -46,6 +46,7 @@ PA_MODULE_USAGE(
"adjust_time=<how often to readjust rates in s> "
"latency_msec=<latency in ms> "
"max_latency_msec=<maximum latency in ms> "
+ "fast_adjust_threshold_msec=<threshold for fast adjust in ms> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
@@ -92,6 +93,7 @@ struct userdata {
pa_usec_t latency;
pa_usec_t max_latency;
pa_usec_t adjust_time;
+ pa_usec_t fast_adjust_threshold;
/* Latency boundaries and current values */
pa_usec_t min_source_latency;
@@ -161,6 +163,7 @@ static const char* const valid_modargs[] = {
"adjust_time",
"latency_msec",
"max_latency_msec",
+ "fast_adjust_threshold_msec",
"format",
"rate",
"channels",
@@ -180,6 +183,7 @@ enum {
SINK_INPUT_MESSAGE_SOURCE_CHANGED,
SINK_INPUT_MESSAGE_SET_EFFECTIVE_SOURCE_LATENCY,
SINK_INPUT_MESSAGE_UPDATE_MIN_LATENCY,
+ SINK_INPUT_MESSAGE_FAST_ADJUST,
};
enum {
@@ -316,7 +320,7 @@ static void adjust_rates(struct userdata *u) {
int32_t latency_difference;
pa_usec_t current_buffer_latency, snapshot_delay;
int64_t current_source_sink_latency, current_latency, latency_at_optimum_rate;
- pa_usec_t final_latency, now;
+ pa_usec_t final_latency, now, time_passed;
pa_assert(u);
pa_assert_ctl_context();
@@ -351,11 +355,15 @@ static void adjust_rates(struct userdata *u) {
pa_log_info("Underrun counter: %u", u->underrun_counter);
}
- /* Calculate real adjust time */
+ /* Calculate real adjust time if source or sink did not change and if the system has
+ * not been suspended. If the time between two calls is more than 5% longer than the
+ * configured adjust time, we assume that the system has been sleeping and skip the
+ * calculation for this iteration. */
now = pa_rtclock_now();
- if (!u->source_sink_changed) {
+ time_passed = now - u->adjust_time_stamp;
+ if (!u->source_sink_changed && time_passed < u->adjust_time * 1.05) {
u->adjust_counter++;
- u->real_adjust_time_sum += now - u->adjust_time_stamp;
+ u->real_adjust_time_sum += time_passed;
u->real_adjust_time = u->real_adjust_time_sum / u->adjust_counter;
}
u->adjust_time_stamp = now;
@@ -391,6 +399,17 @@ static void adjust_rates(struct userdata *u) {
pa_log_debug("Loopback latency at base rate is %0.2f ms", (double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
+ /* Drop or insert samples if fast_adjust_threshold_msec was specified and the latency difference is too large. */
+ if (u->fast_adjust_threshold > 0 && abs(latency_difference) > u->fast_adjust_threshold) {
+ pa_log_debug ("Latency difference larger than %lu msec, skipping or inserting samples.", u->fast_adjust_threshold / PA_USEC_PER_MSEC);
+
+ pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_FAST_ADJUST, NULL, current_source_sink_latency, NULL);
+
+ /* Skip real adjust time calculation on next iteration. */
+ u->source_sink_changed = true;
+ return;
+ }
+
/* Calculate new rate */
new_rate = rate_controller(base_rate, u->real_adjust_time, latency_difference);
@@ -963,6 +982,12 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
u->output_thread_info.minimum_latency = (pa_usec_t)offset;
+ return 0;
+
+ case SINK_INPUT_MESSAGE_FAST_ADJUST:
+
+ memblockq_adjust(u, offset, true);
+
return 0;
}
@@ -1266,6 +1291,7 @@ int pa__init(pa_module *m) {
bool source_dont_move;
uint32_t latency_msec;
uint32_t max_latency_msec;
+ uint32_t fast_adjust_threshold;
pa_sample_spec ss;
pa_channel_map map;
bool format_set = false;
@@ -1350,6 +1376,12 @@ int pa__init(pa_module *m) {
goto fail;
}
+ fast_adjust_threshold = 0;
+ if (pa_modargs_get_value_u32(ma, "fast_adjust_threshold_msec", &fast_adjust_threshold) < 0 || (fast_adjust_threshold != 0 && fast_adjust_threshold < 100)) {
+ pa_log("Invalid fast adjust threshold specification");
+ goto fail;
+ }
+
max_latency_msec = 0;
if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec) < 0) {
pa_log("Invalid maximum latency specification");
@@ -1375,6 +1407,7 @@ int pa__init(pa_module *m) {
u->source_sink_changed = true;
u->real_adjust_time_sum = 0;
u->adjust_counter = 0;
+ u->fast_adjust_threshold = fast_adjust_threshold * PA_USEC_PER_MSEC;
adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
--
2.22.0
From 5864c4f9763c5c2113488fe1153e84fe0028f941 Mon Sep 17 00:00:00 2001
From: Raman Shyshniou <rommer@ibuffed.com>
Date: Tue, 20 Feb 2018 21:29:16 +0100
Subject: [PATCH] loopback: add max_latency_msec argument
Currently loopback module indefinitely increases latency if underruns
occur. This patch allows to set up the upper limit of latency.
---
src/modules/module-loopback.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index aecac0ab9..19005e8e6 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -45,6 +45,7 @@ PA_MODULE_USAGE(
"sink=<sink to connect to> "
"adjust_time=<how often to readjust rates in s> "
"latency_msec=<latency in ms> "
+ "max_latency_msec=<maximum latency in ms> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
@@ -89,6 +90,7 @@ struct userdata {
/* Values from command line configuration */
pa_usec_t latency;
+ pa_usec_t max_latency;
pa_usec_t adjust_time;
/* Latency boundaries and current values */
@@ -158,6 +160,7 @@ static const char* const valid_modargs[] = {
"sink",
"adjust_time",
"latency_msec",
+ "max_latency_msec",
"format",
"rate",
"channels",
@@ -325,10 +328,20 @@ static void adjust_rates(struct userdata *u) {
/* If we are seeing underruns then the latency is too small */
if (u->underrun_counter > 2) {
- u->underrun_latency_limit = PA_MAX(u->latency, u->minimum_latency) + 5 * PA_USEC_PER_MSEC;
- u->underrun_latency_limit = PA_CLIP_SUB((int64_t)u->underrun_latency_limit, u->sink_latency_offset + u->source_latency_offset);
+ pa_usec_t target_latency;
+
+ target_latency = PA_MAX(u->latency, u->minimum_latency) + 5 * PA_USEC_PER_MSEC;
+
+ if (u->max_latency == 0 || target_latency < u->max_latency) {
+ u->underrun_latency_limit = PA_CLIP_SUB((int64_t)target_latency, u->sink_latency_offset + u->source_latency_offset);
+ pa_log_warn("Too many underruns, increasing latency to %0.2f ms", (double)target_latency / PA_USEC_PER_MSEC);
+ } else {
+ u->underrun_latency_limit = PA_CLIP_SUB((int64_t)u->max_latency, u->sink_latency_offset + u->source_latency_offset);
+ pa_log_warn("Too many underruns, configured maximum latency of %0.2f ms is reached", (double)u->max_latency / PA_USEC_PER_MSEC);
+ pa_log_warn("Consider increasing the max_latency_msec");
+ }
+
update_minimum_latency(u, u->sink_input->sink, false);
- pa_log_warn("Too many underruns, increasing latency to %0.2f ms", (double)u->minimum_latency / PA_USEC_PER_MSEC);
u->underrun_counter = 0;
}
@@ -347,7 +360,7 @@ static void adjust_rates(struct userdata *u) {
}
u->adjust_time_stamp = now;
- /* Rates and latencies*/
+ /* Rates and latencies */
old_rate = u->sink_input->sample_spec.rate;
base_rate = u->source_output->sample_spec.rate;
@@ -1252,6 +1265,7 @@ int pa__init(pa_module *m) {
pa_source_output_new_data source_output_data;
bool source_dont_move;
uint32_t latency_msec;
+ uint32_t max_latency_msec;
pa_sample_spec ss;
pa_channel_map map;
bool format_set = false;
@@ -1336,10 +1350,22 @@ int pa__init(pa_module *m) {
goto fail;
}
+ max_latency_msec = 0;
+ if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec) < 0) {
+ pa_log("Invalid maximum latency specification");
+ goto fail;
+ }
+
+ if (max_latency_msec > 0 && max_latency_msec < latency_msec) {
+ pa_log_warn("Configured maximum latency is smaller than latency, using latency instead");
+ max_latency_msec = latency_msec;
+ }
+
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
+ u->max_latency = (pa_usec_t) max_latency_msec * PA_USEC_PER_MSEC;
u->output_thread_info.pop_called = false;
u->output_thread_info.pop_adjust = false;
u->output_thread_info.push_called = false;
--
2.22.0
......@@ -3,3 +3,5 @@ alsa-mixer-Update-to-support-Arctis-Pro-Wireless-headset.patch
alsa-mixer-Add-support-for-2018-Arctis-7.patch
Don-t-compile-with-ffast-math.patch
sink-source-Don-t-change-suspend-cause-when-unlinking.patch
loopback-add-max_latency_msec-argument.patch
loopback-Add-option-fast_adjust_threshold_msec.patch
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment