For USB devices the latency jitter strongly depends on device latency. Therefore a boolean low_device_latency parameter is introduced to half the device latency. Normally 1/3 of the configured end-to-end latency is used, with the parameter this is changed to 1/6. In many situations the parameter can improve latency stability but it will also lead to significantly higher CPU consumption. --- src/modules/module-loopback.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index a9b1b04..4d85ee5 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> " "latency_msec=<latency in ms> " + "low_device_latency=<boolean, use half of the normal device latency> " "adjust_threshold_usec=<threshold for latency adjustment in usec> " "format=<sample format> " "rate=<sample rate> " @@ -98,6 +99,7 @@ struct userdata { pa_usec_t latency; pa_usec_t adjust_time; uint32_t adjust_threshold; + bool low_device_latency; /* Latency boundaries and current values */ pa_usec_t min_source_latency; @@ -190,6 +192,7 @@ static const char* const valid_modargs[] = { "sink", "adjust_time", "latency_msec", + "low_device_latency", "adjust_threshold_usec", "format", "rate", @@ -771,11 +774,14 @@ static void get_effective_source_latency(struct userdata *u, pa_source *source, * The choice of one third is rather arbitrary somewhere between the minimum * possible latency (which would cause a lot of CPU load) and half the configured * latency (which would lead to an empty memblockq if the sink is configured - * likewise). */ + * likewise). In low device latency mode set source to one sixth of the overall + * latency*/ static void set_source_output_latency(struct userdata *u, pa_source *source) { pa_usec_t latency, requested_latency; requested_latency = u->latency / 3; + if (u->low_device_latency) + requested_latency = u->latency / 6; latency = PA_CLAMP(requested_latency , u->min_source_latency, u->max_source_latency); u->configured_source_latency = pa_source_output_set_requested_latency(u->source_output, latency); @@ -1151,11 +1157,14 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in * The choice of one third is rather arbitrary somewhere between the minimum * possible latency (which would cause a lot of CPU load) and half the configured * latency (which would lead to an empty memblockq if the source is configured - * likewise). */ + * likewise). In low device latency mode set sink to one sixth of the overall + * latency. */ static void set_sink_input_latency(struct userdata *u, pa_sink *sink) { pa_usec_t latency, requested_latency; requested_latency = u->latency / 3; + if (u->low_device_latency) + requested_latency = u->latency / 6; latency = PA_CLAMP(requested_latency , u->min_sink_latency, u->max_sink_latency); u->configured_sink_latency = pa_sink_input_set_requested_latency(u->sink_input, latency); @@ -1484,6 +1493,7 @@ int pa__init(pa_module *m) { uint32_t adjust_time_sec; const char *n; bool remix = true; + bool low_device_latency = false; pa_assert(m); @@ -1565,6 +1575,11 @@ int pa__init(pa_module *m) { goto fail; } + if (pa_modargs_get_value_boolean(ma, "low_device_latency", &low_device_latency) < 0) { + pa_log("Invalid boolean device latency parameter"); + goto fail; + } + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -1584,6 +1599,7 @@ int pa__init(pa_module *m) { u->latency_error = 0; u->adjust_threshold = adjust_threshold; u->target_latency_cross_counter = 0; + u->low_device_latency = low_device_latency; u->initial_adjust_pending = true; adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; -- 2.10.1