]> git.sesse.net Git - nageru/commitdiff
Make it possible to adjust the delay without resetting the resampler.
authorSteinar H. Gunderson <steinar+nageru@gunderson.no>
Sun, 11 Aug 2019 15:20:00 +0000 (17:20 +0200)
committerSteinar H. Gunderson <steinar+nageru@gunderson.no>
Sun, 25 Aug 2019 22:39:08 +0000 (00:39 +0200)
nageru/audio_mixer.cpp
nageru/resampling_queue.cpp
nageru/resampling_queue.h

index 4d7632fad80a8e643840991284728ea61bb353cc..07d10dad3650a9ca037263d661b4d5deca4adbf2 100644 (file)
@@ -228,6 +228,14 @@ void deinterleave_samples(const vector<float> &in, vector<float> *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));
                }
        }
 
index 711c1c5239f44aad7df9000711556bc2ff39c2d6..167909d26d4f7f02afd63da999ccbbfe2448705b 100644 (file)
@@ -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<int>(-samples_to_add, buffer.size());
+               buffer.erase(buffer.begin(), buffer.begin() + samples_to_remove);
+               adjustment_samples -= samples_to_remove;
+       }
+       expected_delay = new_delay;
+}
index f0e2499cbf3300e9b4340430b85bf33f25862006..6288415a8cae453bcf55fbf233b86f65384de0d0 100644 (file)
@@ -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.