aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorg Chini <georg@chini.tk>2019-03-01 20:00:52 +0100
committerArun Raghavan <arun@arunraghavan.net>2019-03-25 04:46:07 +0000
commite794d0a21af24ae3d2afae000ad67b17ef56ada2 (patch)
tree8b45d737f79b44b7a100ce1e7eefa71158bb0ab5
parent4254d233eef03bea5593887221a514785ea9f044 (diff)
downloadpulseaudio-e794d0a21af24ae3d2afae000ad67b17ef56ada2.tar.gz
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.
-rw-r--r--src/modules/module-loopback.c41
1 files changed, 37 insertions, 4 deletions
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index e1ab3416..d6b8a3fb 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);
@@ -964,6 +983,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;
}
return pa_sink_input_process_msg(obj, code, data, offset, chunk);
@@ -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) {