"sink_input_name=<custom name for the sink input> "
"source_output_name=<custom name for the source output> "
"sink_input_role=<media.role for the sink input> "
- "source_output_role=<media.role for the source output>");
+ "source_output_role=<media.role for the source output> "
+ "source_dont_move=<boolean> "
+ "sink_dont_move=<boolean>");
#define DEFAULT_LATENCY_MSEC 200
"source_output_name",
"sink_input_role",
"source_output_role",
+ "source_dont_move",
+ "sink_dont_move",
NULL,
};
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);
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);
}
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);
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;
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);
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);