]> git.sesse.net Git - nageru/blobdiff - resampling_queue.h
Fix an issue where the mixer lagging too much behind CEF would cause us to display...
[nageru] / resampling_queue.h
index e43e0ea073767e45b787b2d5c9913df36eb4b22e..b46086df9d0954a4aebd3af0acb3937ca6e314de 100644 (file)
@@ -4,8 +4,8 @@
 // Takes in samples from an input source, possibly with jitter, and outputs a fixed number
 // of samples every iteration. Used to a) change sample rates if needed, and b) deal with
 // input sources that don't have audio locked to video. For every input video
-// frame, you call add_input_samples() with the pts (measured in seconds) of the video frame,
-// taken to be the start point of the frame's audio. When you want to _output_ a finished
+// frame, you call add_input_samples() with the received time point of the video frame,
+// taken to be the _end_ point of the frame's audio. When you want to _output_ a finished
 // frame with audio, you get_output_samples() with the number of samples you want, and will
 // get exactly that number of samples back. If the input and output clocks are not in sync,
 // the audio will be stretched for you. (If they are _very_ out of sync, this will come through
 //  You should have received a copy of the GNU General Public License
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-#include <stdint.h>
-#include <stdlib.h>
 #include <sys/types.h>
 #include <zita-resampler/vresampler.h>
+#include <chrono>
 #include <deque>
 #include <memory>
 
+#include "defs.h"
+
 class ResamplingQueue {
 public:
        // card_num is for debugging outputs only.
-       ResamplingQueue(unsigned card_num, unsigned freq_in, unsigned freq_out, unsigned num_channels = 2);
+       ResamplingQueue(unsigned card_num, unsigned freq_in, unsigned freq_out, unsigned num_channels, double expected_delay_seconds);
+
+       // If policy is DO_NOT_ADJUST_RATE, the resampling rate will not be changed.
+       // This is primarily useful if you have an extraordinary situation, such as
+       // dropped frames.
+       enum RateAdjustmentPolicy {
+               DO_NOT_ADJUST_RATE,
+               ADJUST_RATE
+       };
 
-       // Note: pts is always in seconds.
-       void add_input_samples(double pts, const float *samples, ssize_t num_samples);
-       bool get_output_samples(double pts, float *samples, ssize_t num_samples);  // Returns false if underrun.
+       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);
 
 private:
        void init_loop_filter(double bandwidth_hz);
@@ -62,28 +71,43 @@ private:
        unsigned card_num;
        unsigned freq_in, freq_out, num_channels;
 
-       bool first_input = true, first_output = true;
-       double last_input_pts;   // Start of last input block, in seconds.
-       double last_output_pts;
+       bool first_output = true;
 
-       ssize_t k_a0 = 0;  // Total amount of samples inserted _before_ the last call to add_input_samples().
-       ssize_t k_a1 = 0;  // Total amount of samples inserted _after_ the last call to add_input_samples().
+       struct InputPoint {
+               // Equivalent to t_a0 or t_a1 in the paper.
+               std::chrono::steady_clock::time_point ts;
 
-       ssize_t total_consumed_samples = 0;
+               // Number of samples that have been written to the queue (in total)
+               // at this time point. Equivalent to k_a0 or k_a1 in the paper.
+               size_t input_samples_received = 0;
+
+               // Set to false if we should not use the timestamp from this sample
+               // (e.g. if it is from a dropped frame and thus bad). In particular,
+               // we will not use it for updateing current_estimated_freq_in.
+               bool good_sample = false;
+       };
+       InputPoint a0, a1;
 
-       // Duration of last input block, in seconds.
-       double last_input_len;
+       // 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;
+
+       ssize_t total_consumed_samples = 0;
 
        // Filter state for the loop filter.
        double z1 = 0.0, z2 = 0.0, z3 = 0.0;
 
        // Ratio between the two frequencies.
-       double ratio;
+       const double ratio;
+
+       // Current correction ratio. ratio * rcorr gives the true ratio,
+       // so values above 1.0 means to pitch down (consume input samples slower).
+       double rcorr = 1.0;
 
        // 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.
-       double expected_delay = 4800.0;
+       const double expected_delay;
 
        // Input samples not yet fed into the resampler.
        // TODO: Use a circular buffer instead, for efficiency.