X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/11dbe30bfae09235307115f413fb6172df04a895..2f7eb35f370bb79c12f229d109fc4dd2b74c9f47:/src/modules/module-loopback.c diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index dfd5e766..9a8640b1 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -57,7 +57,9 @@ PA_MODULE_USAGE( "sink_input_name= " "source_output_name= " "sink_input_role= " - "source_output_role="); + "source_output_role= " + "source_dont_move= " + "sink_dont_move="); #define DEFAULT_LATENCY_MSEC 200 @@ -116,6 +118,8 @@ static const char* const valid_modargs[] = { "source_output_name", "sink_input_role", "source_output_role", + "source_dont_move", + "sink_dont_move", NULL, }; @@ -175,13 +179,13 @@ static void adjust_rates(struct userdata *u) { buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); - pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", + pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); - pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes", + pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); @@ -194,9 +198,21 @@ static void adjust_rates(struct userdata *u) { else new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; - pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate); + if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) { + pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate); + new_rate = base_rate; + } else { + if (base_rate < new_rate + 20 && new_rate < base_rate + 20) + new_rate = base_rate; + /* Do the adjustment in small steps; 2‰ can be considered inaudible */ + if (new_rate < (uint32_t) (old_rate*0.998) || new_rate > (uint32_t) (old_rate*1.002)) { + pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, old_rate); + new_rate = PA_CLAMP(new_rate, (uint32_t) (old_rate*0.998), (uint32_t) (old_rate*1.002)); + } + } pa_sink_input_set_rate(u->sink_input, new_rate); + pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate); pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); } @@ -421,9 +437,9 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { - pa_usec_t *r = data; + pa_usec_t *r = data; - pa_sink_input_assert_io_context(u->sink_input); + pa_sink_input_assert_io_context(u->sink_input); *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec); @@ -619,8 +635,10 @@ int pa__init(pa_module *m) { struct userdata *u; pa_sink *sink; pa_sink_input_new_data sink_input_data; + pa_bool_t sink_dont_move; pa_source *source; pa_source_output_new_data source_output_data; + pa_bool_t source_dont_move; uint32_t latency_msec; pa_sample_spec ss; pa_channel_map map; @@ -697,6 +715,15 @@ int pa__init(pa_module *m) { pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE; + sink_dont_move = FALSE; + if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) { + pa_log("sink_dont_move= expects a boolean argument."); + goto fail; + } + + if (sink_dont_move) + sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE; + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); @@ -738,6 +765,16 @@ int pa__init(pa_module *m) { pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + source_output_data.flags = (pa_source_output_flags_t)0; + + source_dont_move = FALSE; + if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) { + pa_log("source_dont_move= expects a boolean argument."); + goto fail; + } + + if (source_dont_move) + source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE; pa_source_output_new(&u->source_output, m->core, &source_output_data); pa_source_output_new_data_done(&source_output_data);