]> git.sesse.net Git - nageru/blob - resampling_queue.h
Fix an issue where the mixer lagging too much behind CEF would cause us to display...
[nageru] / resampling_queue.h
1 #ifndef _RESAMPLING_QUEUE_H
2 #define _RESAMPLING_QUEUE_H 1
3
4 // Takes in samples from an input source, possibly with jitter, and outputs a fixed number
5 // of samples every iteration. Used to a) change sample rates if needed, and b) deal with
6 // input sources that don't have audio locked to video. For every input video
7 // frame, you call add_input_samples() with the received time point of the video frame,
8 // taken to be the _end_ point of the frame's audio. When you want to _output_ a finished
9 // frame with audio, you get_output_samples() with the number of samples you want, and will
10 // get exactly that number of samples back. If the input and output clocks are not in sync,
11 // the audio will be stretched for you. (If they are _very_ out of sync, this will come through
12 // as a pitch shift.) Of course, the process introduces some delay; you specify a target delay
13 // (typically measured in milliseconds, although more is fine) and the algorithm works to
14 // provide exactly that.
15 //
16 // A/V sync is a much harder problem than one would intuitively assume. This implementation
17 // is based on a 2012 paper by Fons Adriaensen, “Controlling adaptive resampling”
18 // (http://kokkinizita.linuxaudio.org/papers/adapt-resamp.pdf). The paper gives an algorithm
19 // that converges to jitter of <100 ns; the basic idea is to measure the _rate_ the input
20 // queue fills and is drained (as opposed to the length of the queue itself), and smoothly
21 // adjust the resampling rate so that it reaches steady state at the desired delay.
22 //
23 // Parts of the code is adapted from Adriaensen's project Zita-ajbridge (based on the same
24 // algorithm), although it has been heavily reworked for this use case. Original copyright follows:
25 //
26 //  Copyright (C) 2012-2015 Fons Adriaensen <fons@linuxaudio.org>
27 //    
28 //  This program is free software; you can redistribute it and/or modify
29 //  it under the terms of the GNU General Public License as published by
30 //  the Free Software Foundation; either version 3 of the License, or
31 //  (at your option) any later version.
32 //
33 //  This program is distributed in the hope that it will be useful,
34 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
35 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36 //  GNU General Public License for more details.
37 //
38 //  You should have received a copy of the GNU General Public License
39 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
40
41 #include <sys/types.h>
42 #include <zita-resampler/vresampler.h>
43 #include <chrono>
44 #include <deque>
45 #include <memory>
46
47 #include "defs.h"
48
49 class ResamplingQueue {
50 public:
51         // card_num is for debugging outputs only.
52         ResamplingQueue(unsigned card_num, unsigned freq_in, unsigned freq_out, unsigned num_channels, double expected_delay_seconds);
53
54         // If policy is DO_NOT_ADJUST_RATE, the resampling rate will not be changed.
55         // This is primarily useful if you have an extraordinary situation, such as
56         // dropped frames.
57         enum RateAdjustmentPolicy {
58                 DO_NOT_ADJUST_RATE,
59                 ADJUST_RATE
60         };
61
62         void add_input_samples(std::chrono::steady_clock::time_point ts, const float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy);
63         // Returns false if underrun.
64         bool get_output_samples(std::chrono::steady_clock::time_point ts, float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy);
65
66 private:
67         void init_loop_filter(double bandwidth_hz);
68
69         VResampler vresampler;
70
71         unsigned card_num;
72         unsigned freq_in, freq_out, num_channels;
73
74         bool first_output = true;
75
76         struct InputPoint {
77                 // Equivalent to t_a0 or t_a1 in the paper.
78                 std::chrono::steady_clock::time_point ts;
79
80                 // Number of samples that have been written to the queue (in total)
81                 // at this time point. Equivalent to k_a0 or k_a1 in the paper.
82                 size_t input_samples_received = 0;
83
84                 // Set to false if we should not use the timestamp from this sample
85                 // (e.g. if it is from a dropped frame and thus bad). In particular,
86                 // we will not use it for updateing current_estimated_freq_in.
87                 bool good_sample = false;
88         };
89         InputPoint a0, a1;
90
91         // The current rate at which we seem to get input samples, in Hz.
92         // For an ideal input, identical to freq_in.
93         double current_estimated_freq_in;
94
95         ssize_t total_consumed_samples = 0;
96
97         // Filter state for the loop filter.
98         double z1 = 0.0, z2 = 0.0, z3 = 0.0;
99
100         // Ratio between the two frequencies.
101         const double ratio;
102
103         // Current correction ratio. ratio * rcorr gives the true ratio,
104         // so values above 1.0 means to pitch down (consume input samples slower).
105         double rcorr = 1.0;
106
107         // How much delay we are expected to have, in input samples.
108         // If actual delay drifts too much away from this, we will start
109         // changing the resampling ratio to compensate.
110         const double expected_delay;
111
112         // Input samples not yet fed into the resampler.
113         // TODO: Use a circular buffer instead, for efficiency.
114         std::deque<float> buffer;
115 };
116
117 #endif  // !defined(_RESAMPLING_QUEUE_H)