From 5b1f88aa6f4e44f497e951f572a0fe2987389330 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 11 Aug 2019 17:20:00 +0200 Subject: [PATCH] Make it possible to adjust the delay without resetting the resampler. --- nageru/audio_mixer.cpp | 44 +++++++++++++++++++++++++------------ nageru/resampling_queue.cpp | 24 +++++++++++++++++++- nageru/resampling_queue.h | 10 ++++++++- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/nageru/audio_mixer.cpp b/nageru/audio_mixer.cpp index 4d7632f..07d10da 100644 --- a/nageru/audio_mixer.cpp +++ b/nageru/audio_mixer.cpp @@ -228,6 +228,14 @@ void deinterleave_samples(const vector &in, vector *out_l, vector< } } +double get_delay_seconds(double extra_delay_ms) +{ + // Make sure we never get negative delay. Even 1 ms is probably way less than we + // could ever hope to actually have; this is just a failsafe. + double delay_ms = max(global_flags.audio_queue_length_ms + extra_delay_ms, 1.0); + return delay_ms * 0.001; +} + } // namespace AudioMixer::AudioMixer(unsigned num_capture_cards, unsigned num_ffmpeg_inputs) @@ -301,13 +309,9 @@ void AudioMixer::reset_resampler_mutex_held(DeviceSpec device_spec) if (device->interesting_channels.empty()) { device->resampling_queue.reset(); } else { - // Make sure we never get negative delay. Even 1 ms is probably way less than we - // could ever hope to actually have; this is just a failsafe. - double delay_ms = max(global_flags.audio_queue_length_ms + device->extra_delay_ms, 1.0); - device->resampling_queue.reset(new ResamplingQueue( device_spec, device->capture_frequency, OUTPUT_FREQUENCY, device->interesting_channels.size(), - delay_ms * 0.001)); + get_delay_seconds(device->extra_delay_ms))); } } @@ -1237,37 +1241,49 @@ void AudioMixer::set_input_mapping_lock_held(const InputMapping &new_input_mappi for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) { const DeviceSpec device_spec{InputSourceType::CAPTURE_CARD, card_index}; AudioDevice *device = find_audio_device(device_spec); - if (device->interesting_channels != interesting_channels[device_spec] || - device->extra_delay_ms != new_extra_delay_ms[device_spec]) { + double extra_delay_ms = new_extra_delay_ms[device_spec]; + if (device->interesting_channels != interesting_channels[device_spec]) { device->interesting_channels = interesting_channels[device_spec]; - device->extra_delay_ms = new_extra_delay_ms[device_spec]; + device->extra_delay_ms = extra_delay_ms; reset_resampler_mutex_held(device_spec); + } else if (device->extra_delay_ms != extra_delay_ms && + device->resampling_queue != nullptr) { + device->extra_delay_ms = extra_delay_ms; + device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms)); } } for (unsigned card_index = 0; card_index < MAX_ALSA_CARDS; ++card_index) { const DeviceSpec device_spec{InputSourceType::ALSA_INPUT, card_index}; AudioDevice *device = find_audio_device(device_spec); + double extra_delay_ms = new_extra_delay_ms[device_spec]; if (interesting_channels[device_spec].empty()) { alsa_pool.release_device(card_index); } else { alsa_pool.hold_device(card_index); } - if (device->interesting_channels != interesting_channels[device_spec] || - device->extra_delay_ms != new_extra_delay_ms[device_spec]) { + if (device->interesting_channels != interesting_channels[device_spec]) { device->interesting_channels = interesting_channels[device_spec]; - device->extra_delay_ms = new_extra_delay_ms[device_spec]; + device->extra_delay_ms = extra_delay_ms; alsa_pool.reset_device(device_spec.index); reset_resampler_mutex_held(device_spec); + } else if (device->extra_delay_ms != extra_delay_ms && + device->resampling_queue != nullptr) { + device->extra_delay_ms = extra_delay_ms; + device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms)); } } for (unsigned card_index = 0; card_index < num_ffmpeg_inputs; ++card_index) { const DeviceSpec device_spec{InputSourceType::FFMPEG_VIDEO_INPUT, card_index}; AudioDevice *device = find_audio_device(device_spec); - if (device->interesting_channels != interesting_channels[device_spec] || - device->extra_delay_ms != new_extra_delay_ms[device_spec]) { + double extra_delay_ms = new_extra_delay_ms[device_spec]; + if (device->interesting_channels != interesting_channels[device_spec]) { device->interesting_channels = interesting_channels[device_spec]; - device->extra_delay_ms = new_extra_delay_ms[device_spec]; + device->extra_delay_ms = extra_delay_ms; reset_resampler_mutex_held(device_spec); + } else if (device->extra_delay_ms != extra_delay_ms && + device->resampling_queue != nullptr) { + device->extra_delay_ms = extra_delay_ms; + device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms)); } } diff --git a/nageru/resampling_queue.cpp b/nageru/resampling_queue.cpp index 711c1c5..167909d 100644 --- a/nageru/resampling_queue.cpp +++ b/nageru/resampling_queue.cpp @@ -107,7 +107,7 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam const double input_samples_consumed = total_consumed_samples + num_samples / (ratio * rcorr); - double actual_delay = input_samples_received - input_samples_consumed; + double actual_delay = input_samples_received - input_samples_consumed + adjustment_samples; actual_delay += vresampler.inpdist(); // Delay in the resampler itself. double err = actual_delay - expected_delay; if (first_output) { @@ -198,3 +198,25 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam } return true; } + +void ResamplingQueue::change_expected_delay(double expected_delay_seconds) +{ + double new_delay = expected_delay_seconds * OUTPUT_FREQUENCY; + + if (a1.input_samples_received == 0) { + // Before the first block, so no need to adjust the queue. + expected_delay = new_delay; + return; + } + + int samples_to_add = lrint(new_delay - expected_delay); + if (samples_to_add > 0) { + adjustment_samples += samples_to_add; + buffer.insert(buffer.end(), samples_to_add, 0.0f); + } else if (samples_to_add < 0) { + int samples_to_remove = min(-samples_to_add, buffer.size()); + buffer.erase(buffer.begin(), buffer.begin() + samples_to_remove); + adjustment_samples -= samples_to_remove; + } + expected_delay = new_delay; +} diff --git a/nageru/resampling_queue.h b/nageru/resampling_queue.h index f0e2499..6288415 100644 --- a/nageru/resampling_queue.h +++ b/nageru/resampling_queue.h @@ -63,6 +63,11 @@ public: void add_input_samples(std::chrono::steady_clock::time_point ts, const float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy); // Returns false if underrun. bool get_output_samples(std::chrono::steady_clock::time_point ts, float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy); + // Instantly change delay by removing samples, or adding zeros. + // Does not correct a queue that is in bad shape; e.g., if you change + // delay from 100 to 110 ms but your queue had a real delay of 200 ms, + // 10 ms worth of zeros will be inserted nevertheless. + void change_expected_delay(double expected_delay_seconds); private: void init_loop_filter(double bandwidth_hz); @@ -89,6 +94,9 @@ private: }; InputPoint a0, a1; + // If nonzero, how many samples we've added or removed as part of change_expected_delay(). + int adjustment_samples = 0; + // The current rate at which we seem to get input samples, in Hz. // For an ideal input, identical to freq_in. double current_estimated_freq_in; @@ -108,7 +116,7 @@ private: // How much delay we are expected to have, in input samples. // If actual delay drifts too much away from this, we will start // changing the resampling ratio to compensate. - const double expected_delay; + double expected_delay; // Input samples not yet fed into the resampler. // TODO: Use a circular buffer instead, for efficiency. -- 2.39.2