]> git.sesse.net Git - nageru/blobdiff - resampling_queue.cpp
Release Nageru 1.7.2.
[nageru] / resampling_queue.cpp
index 188bf7d2d57ccdb98f249f86c59e0360e65b470b..ef7b73511ff596f7e93894fd703038ad370c8841 100644 (file)
@@ -30,8 +30,8 @@
 using namespace std;
 using namespace std::chrono;
 
-ResamplingQueue::ResamplingQueue(unsigned card_num, unsigned freq_in, unsigned freq_out, unsigned num_channels, double expected_delay_seconds)
-       : card_num(card_num), freq_in(freq_in), freq_out(freq_out), num_channels(num_channels),
+ResamplingQueue::ResamplingQueue(DeviceSpec device_spec, unsigned freq_in, unsigned freq_out, unsigned num_channels, double expected_delay_seconds)
+       : device_spec(device_spec), freq_in(freq_in), freq_out(freq_out), num_channels(num_channels),
          current_estimated_freq_in(freq_in),
          ratio(double(freq_out) / double(freq_in)), expected_delay(expected_delay_seconds * OUTPUT_FREQUENCY)
 {
@@ -49,6 +49,8 @@ void ResamplingQueue::add_input_samples(steady_clock::time_point ts, const float
                return;
        }
 
+       assert(duration<double>(ts.time_since_epoch()).count() >= 0.0);
+
        bool good_sample = (rate_adjustment_policy == ADJUST_RATE);
        if (good_sample && a1.good_sample) {
                a0 = a1;
@@ -58,7 +60,11 @@ void ResamplingQueue::add_input_samples(steady_clock::time_point ts, const float
        a1.good_sample = good_sample;
        if (a0.good_sample && a1.good_sample) {
                current_estimated_freq_in = (a1.input_samples_received - a0.input_samples_received) / duration<double>(a1.ts - a0.ts).count();
-               assert(current_estimated_freq_in >= 0.0);
+               if (!(current_estimated_freq_in >= 0.0)) {
+                       fprintf(stderr, "%s: PANIC: Input audio clock going backwards, ignoring.\n",
+                               spec_to_string(device_spec).c_str());
+                       current_estimated_freq_in = freq_in;
+               }
 
                // Bound the frequency, so that a single wild result won't throw the filter off guard.
                current_estimated_freq_in = min(current_estimated_freq_in, 1.2 * freq_in);
@@ -77,12 +83,23 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam
                return true;
        }
 
+       // This can happen when we get dropped frames on the master card.
+       if (duration<double>(ts.time_since_epoch()).count() <= 0.0) {
+               rate_adjustment_policy = DO_NOT_ADJUST_RATE;
+       }
+
        if (rate_adjustment_policy == ADJUST_RATE && (a0.good_sample || a1.good_sample)) {
                // Estimate the current number of input samples produced at
                // this instant in time, by extrapolating from the last known
                // good point. Note that we could be extrapolating backward or
                // forward, depending on the timing of the calls.
                const InputPoint &base_point = a1.good_sample ? a1 : a0;
+               assert(duration<double>(base_point.ts.time_since_epoch()).count() >= 0.0);
+
+               // NOTE: Due to extrapolation, input_samples_received can
+               // actually go negative here the few first calls (ie., we asked
+               // about a timestamp where we hadn't actually started producing
+               // samples yet), but that is harmless.
                const double input_samples_received = base_point.input_samples_received +
                        current_estimated_freq_in * duration<double>(ts - base_point.ts).count();
 
@@ -93,15 +110,22 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam
                double actual_delay = input_samples_received - input_samples_consumed;
                actual_delay += vresampler.inpdist();    // Delay in the resampler itself.
                double err = actual_delay - expected_delay;
-               if (first_output && err < 0.0) {
+               if (first_output) {
                        // Before the very first block, insert artificial delay based on our initial estimate,
                        // so that we don't need a long period to stabilize at the beginning.
-                       int delay_samples_to_add = lrintf(-err);
-                       for (ssize_t i = 0; i < delay_samples_to_add * num_channels; ++i) {
-                               buffer.push_front(0.0f);
+                       if (err < 0.0) {
+                               int delay_samples_to_add = lrintf(-err);
+                               for (ssize_t i = 0; i < delay_samples_to_add * num_channels; ++i) {
+                                       buffer.push_front(0.0f);
+                               }
+                               total_consumed_samples -= delay_samples_to_add;  // Equivalent to increasing input_samples_received on a0 and a1.
+                               err += delay_samples_to_add;
+                       } else if (err > 0.0) {
+                               int delay_samples_to_remove = min<int>(lrintf(err), buffer.size() / num_channels);
+                               buffer.erase(buffer.begin(), buffer.begin() + delay_samples_to_remove * num_channels);
+                               total_consumed_samples += delay_samples_to_remove;
+                               err -= delay_samples_to_remove;
                        }
-                       total_consumed_samples -= delay_samples_to_add;  // Equivalent to increasing input_samples_received on a0 and a1.
-                       err += delay_samples_to_add;
                }
                first_output = false;
 
@@ -136,8 +160,6 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam
                if (rcorr < 0.95) rcorr = 0.95;
                assert(!isnan(rcorr));
                vresampler.set_rratio(rcorr);
-       } else {
-               assert(rate_adjustment_policy == DO_NOT_ADJUST_RATE);
        }
 
        // Finally actually resample, producing exactly <num_samples> output samples.
@@ -147,9 +169,13 @@ bool ResamplingQueue::get_output_samples(steady_clock::time_point ts, float *sam
                if (buffer.empty()) {
                        // This should never happen unless delay is set way too low,
                        // or we're dropping a lot of data.
-                       fprintf(stderr, "Card %u: PANIC: Out of input samples to resample, still need %d output samples! (correction factor is %f)\n",
-                               card_num, int(vresampler.out_count), rcorr);
+                       fprintf(stderr, "%s: PANIC: Out of input samples to resample, still need %d output samples! (correction factor is %f)\n",
+                               spec_to_string(device_spec).c_str(), int(vresampler.out_count), rcorr);
                        memset(vresampler.out_data, 0, vresampler.out_count * num_channels * sizeof(float));
+
+                       // Reset the loop filter.
+                       z1 = z2 = z3 = 0.0;
+
                        return false;
                }