1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Stefano Tronci <stefano.tronci@protonmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 5 Apr 2017
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #ifndef CORE_UTIL_LATENCYDETECTOR_H_
23 #define CORE_UTIL_LATENCYDETECTOR_H_
24 
25 #include <core/types.h>
26 #include <math.h>
27 
28 #define DEFAULT_ABS_THRESHOLD   0.01f
29 #define DEFAULT_PEAK_THRESHOLD  0.5f
30 
31 namespace lsp
32 {
33     class LatencyDetector
34     {
35         protected:
36 
37             // Input processor state enumerator
38             enum ip_state_t
39             {
40                 IP_BYPASS,  // Bypassing the signal
41                 IP_WAIT,    // Bypassing while the Output Processor fades out and emits zeros
42                 IP_DETECT   // Receiving input samples and attempting latency detection
43             };
44 
45             // Output processor state enumerator
46             enum op_state_t
47             {
48                 OP_BYPASS,  // Bypassing the signal
49                 OP_FADEOUT, // Fading out the signal
50                 OP_PAUSE,   // Emitting zeros
51                 OP_EMIT,    // Emitting the chirp samples
52                 OP_FADEIN   // Fading in the signal
53             };
54 
55             // Chirp System parameters
56             typedef struct chirp_t
57             {
58                 float       fDuration;              // Chirp Duration [seconds]
59                 float       fDelayRatio;            // Fraction of fChirpDuration defining 0 Hz group delay of the chirp system
60                 bool        bModified;              // If any of the parameters above is modified, mark for Chirp/Antichirp recalculation
61 
62                 size_t      nDuration;              // Chirp Duration [samples] (not of the whole FIR response, only the part containing most of the chirp)
63 
64                 size_t      n2piMult;               // Integer multiplier of 2 * M_PI. Identifies the value of phase at Nyquist frequency
65                 float       fAlpha;                 // Coefficient of the linear term of the phase response
66                 float       fBeta;                  // Coefficient of the quadratic term of the phase response
67                 size_t      nLength;                // Length of the FIR (number of samples). Equals Order + 1
68                 size_t      nOrder;                 // Order of the FIR
69                 size_t      nFftRank;               // Rank of the inverse FFT to obtain time domain samples
70 
71                 float       fConvScale;             // Scale factor to normalise convolution values
72             } chirp_t;
73 
74             // Input Processor parameters
75             typedef struct ip_t
76             {
77                 ip_state_t  nState;                 // State
78                 size_t      ig_time;                // Global Time counter
79                 size_t      ig_start;               // Fix instant at which detection starts
80                 size_t      ig_stop;                // Fix instant at which detection ends
81 
82                 float       fDetect;                // Detection duration
83                 size_t      nDetect;                // Detection length
84                 size_t      nDetectCounter;         // Count samples in input when in IP_DETECT state
85             } ip_t;
86 
87             // Output Processor parameters
88             typedef struct op_t
89             {
90                 op_state_t  nState;                 // State
91                 size_t      og_time;                // Global Time counter
92                 size_t      og_start;               // Fix instant at which detection starts
93 
94                 float       fGain;                  // Fading gain
95                 float       fGainDelta;             // Fading gain delta
96 
97                 float       fFade;                  // Fade time [seconds]
98                 size_t      nFade;                  // Fade time [samples]
99 
100                 float       fPause;                 // Pause duration [seconds]
101                 size_t      nPause;                 // Pause duration [samples]
102                 size_t      nPauseCounter;          // Count samples in output when in OP_PAUSE state
103 
104                 size_t      nEmitCounter;           // Count samples in output when in OP_EMIT state
105             } op_t;
106 
107             // Peak Detection parameters
108             typedef struct peak_t
109             {
110                 float       fAbsThreshold;          // Absolute detection threshold
111                 float       fPeakThreshold;         // Relative threshold between peaks (higher delta between recorded peaks will trigger early detection)
112                 float       fValue;                 // Value of the detected peak (absolute)
113                 size_t      nPosition;              // Position of the detected peak (referenced to sample counters)
114                 size_t      nTimeOrigin;            // This should be the sample at which the convolution peak as in case 0 delay.
115                 bool        bDetected;              // True if the peak was detected.
116             } peak_t;
117 
118         private:
119             size_t          nSampleRate;            // Sample Rate [Hz]
120 
121             chirp_t         sChirpSystem;
122 
123             ip_t            sInputProcessor;
124             op_t            sOutputProcessor;
125 
126             peak_t          sPeakDetector;          // Object tracking the peak of convolution.
127 
128             float          *vChirp;                 // Samples of the chirp system impulse response
129             float          *vAntiChirp;             // Samples of the anti-chirp system impulse response
130             float          *vCapture;               // Hold samples captured from audio input
131             float          *vBuffer;                // Temporary buffer to apply convolution
132             float          *vChirpConv;             // Chirp fast convolution image
133             float          *vConvBuf;               // Temporary convolution buffer
134             uint8_t        *pData;
135 
136             bool            bCycleComplete;         // True if the machine operated a whole measurement cycle
137             bool            bLatencyDetected;       // True if latency was detected
138             ssize_t         nLatency;               // Value of latency in samples. Signed so that -1 is meaningful
139 
140             bool            bSync;
141 
142         protected:
143             void detect_peak(float *buf, size_t count);
144 
145         public:
146             LatencyDetector();
147             ~LatencyDetector();
148 
149         public:
150             /** Initialise LatencyDetector
151              *
152              */
153             void init();
154 
155             /** Destroy LatencyDetector
156              *
157              */
158             void destroy();
159 
160             /** Check that LatencyDetector needs settings update
161              *
162              * @return true if LatencyDetector needs setting update
163              */
needs_update()164             inline bool needs_update() const
165             {
166                 return bSync;
167             }
168 
169             /** Update LatencyDetector stateful settings
170              *
171              */
172             void update_settings();
173 
174             /** Set sample rate for the LatencyDetector
175              *
176              * @param sr sample rate
177              */
set_sample_rate(size_t sr)178             inline void set_sample_rate(size_t sr)
179             {
180                 if (nSampleRate == sr)
181                     return;
182 
183                 nSampleRate = sr;
184                 bSync       = true;
185             }
186 
187             /** Set chirp duration in seconds
188              *
189              * @param duration chirp duration in seconds
190              */
set_duration(float duration)191             inline void set_duration(float duration)
192             {
193                 if (sChirpSystem.fDuration == duration)
194                     return;
195 
196                 sChirpSystem.fDuration = duration;
197                 sChirpSystem.bModified = true;
198                 bSync     = true;
199             }
200 
201             /** Set 0 Hz Group Delay for chirp
202              *
203              * @param ratio 0 Hz Group Delay as fraction of duration
204              */
set_delay_ratio(float ratio)205             inline void set_delay_ratio(float ratio)
206             {
207                 if ((sChirpSystem.fDelayRatio == ratio) || (ratio <= 0))
208                     return;
209 
210                 // This condition is needed for causality of direct chirp system
211                 sChirpSystem.fDelayRatio = (ratio < 4.0f) ? ratio : 4.0f;
212                 sChirpSystem.bModified = true;
213                 bSync       = true;
214             }
215 
216             /** Set chirp pause in seconds
217              *
218              * @param pause pause duration in seconds
219              */
set_op_pause(float pause)220             inline void set_op_pause(float pause)
221             {
222                 if (sOutputProcessor.fPause == pause)
223                     return;
224 
225                 sOutputProcessor.fPause = pause;
226                 bSync     = true;
227             }
228 
229             /** Set chirp fading in seconds
230              *
231              * @param fading fading duration in seconds
232              */
set_op_fading(float fading)233             inline void set_op_fading(float fading)
234             {
235                 if (sOutputProcessor.fFade == fading)
236                     return;
237 
238                 sOutputProcessor.fFade = fading;
239                 bSync     = true;
240             }
241 
242             /** Set chirp detection in seconds
243              *
244              * @param detect overall detection time in seconds
245              */
set_ip_detection(float detect)246             inline void set_ip_detection(float detect)
247             {
248                 if (sInputProcessor.fDetect == detect)
249                     return;
250 
251                 sInputProcessor.fDetect = detect;
252                 bSync     = true;
253             }
254 
255             /** Set peak detector absolute detection threshold
256              *
257              * @param threshold absolute threshold
258              */
set_abs_threshold(float threshold)259             inline void set_abs_threshold(float threshold)
260             {
261                 if (sPeakDetector.fAbsThreshold == threshold)
262                     return;
263 
264                 sPeakDetector.fAbsThreshold = ((threshold > 0.0f) && (threshold <= 1.0f)) ? threshold : DEFAULT_ABS_THRESHOLD;
265             }
266 
267             /** Set peak detector relative threshold
268              *
269              * @param threshold relative threshold
270              */
set_peak_threshold(float threshold)271             inline void set_peak_threshold(float threshold)
272             {
273                 if (sPeakDetector.fPeakThreshold == threshold)
274                     return;
275 
276                 sPeakDetector.fPeakThreshold = ((threshold > 0.0f) && (threshold <= 1.0f)) ? threshold : DEFAULT_PEAK_THRESHOLD;
277             }
278 
279             /** Start latency detection process
280              *
281              */
start_capture()282             inline void start_capture()
283             {
284                 sInputProcessor.nState              = IP_WAIT;
285                 sInputProcessor.ig_time             = 0;
286                 sInputProcessor.ig_start            = 0;
287                 sInputProcessor.ig_stop             = -1;
288                 sInputProcessor.nDetectCounter      = 0;
289 
290                 sOutputProcessor.nState             = OP_FADEOUT;
291                 sOutputProcessor.og_time            = 0;
292                 sOutputProcessor.og_start           = 0;
293                 sOutputProcessor.nPauseCounter      = 0;
294                 sOutputProcessor.nEmitCounter       = 0;
295 
296                 sPeakDetector.fValue                = 0.0f;
297                 sPeakDetector.nPosition             = 0;
298                 sPeakDetector.nTimeOrigin           = 0;
299                 sPeakDetector.bDetected             = false;
300 
301                 bCycleComplete                      = false;
302                 bLatencyDetected                    = false;
303                 nLatency                            = 0;
304             }
305 
306             /** Force the chirp system to reset it's state
307              *
308              */
reset_capture()309             inline void reset_capture()
310             {
311                 sInputProcessor.nState              = IP_BYPASS;
312                 sInputProcessor.ig_time             = 0;
313                 sInputProcessor.ig_start            = 0;
314                 sInputProcessor.ig_stop             = -1;
315                 sInputProcessor.nDetectCounter      = 0;
316 
317                 sOutputProcessor.nState             = OP_BYPASS;
318                 sOutputProcessor.og_time            = 0;
319                 sOutputProcessor.og_start           = 0;
320                 sOutputProcessor.nPauseCounter      = 0;
321                 sOutputProcessor.nEmitCounter       = 0;
322 
323                 sPeakDetector.fValue                = 0.0f;
324                 sPeakDetector.nPosition             = 0;
325                 sPeakDetector.nTimeOrigin           = 0;
326                 sPeakDetector.bDetected             = false;
327 
328                 bCycleComplete                      = false;
329                 bLatencyDetected                    = false;
330                 nLatency                            = 0;
331             }
332 
333             /** Get chirp duration in samples
334              *
335              * @return chirp duration in samples
336              */
get_duration_samples()337             inline size_t get_duration_samples() const
338             {
339                 return sChirpSystem.nDuration;
340             }
341 
342             /** Get chirp duration in seconds
343              *
344              * @return chirp duration in seconds
345              */
get_duration_seconds()346             inline float get_duration_seconds() const
347             {
348                 return samples_to_seconds(nSampleRate, sChirpSystem.nDuration);
349             }
350 
351             /** Return true if the measurement cycle was completed
352              *
353              * @return bCycleComplete value
354              */
cycle_complete()355             inline bool cycle_complete() const
356             {
357                 return bCycleComplete;
358             }
359 
360             /** Return true if the latency was detected
361              *
362              * @return bLatencyDetected value
363              */
latency_detected()364             inline bool latency_detected() const
365             {
366                 return bLatencyDetected;
367             }
368 
369             /** Get latency in samples
370              *
371              * @return latency in samples
372              */
get_latency_samples()373             inline ssize_t get_latency_samples() const
374             {
375                 if (!bCycleComplete)
376                     return -1;
377 
378                 return nLatency;
379             }
380 
381             /** Get latency in seconds
382              *
383              * @return latency in seconds
384              */
get_latency_seconds()385             inline float get_latency_seconds() const
386             {
387                 if (!bLatencyDetected)
388                     return 0.0f;
389 
390                 return samples_to_seconds(nSampleRate, nLatency);
391             }
392 
393         public:
394             /** Stream direct chirp while recording response
395              *
396              * @param dst samples destination
397              * @param src input source, allowed to be NULL
398              * @param count number of samples to process
399              */
400             void process(float *dst, const float *src, size_t count);
401 
402             /** Collect input samples:
403              *
404              * @param dst samples destination
405              * @param src input source, allowed to be NULL
406              * @param count number of samples to process
407              */
408             void process_in(float *dst, const float *src, size_t count);
409 
410             /** Stream output samples:
411              *
412              * @param dst samples destination
413              * @param src input source, allowed to be NULL
414              * @param count number of samples to process
415              */
416             void process_out(float *dst, const float *src, size_t count);
417     };
418 
419 }
420 
421 #endif /* CORE_UTIL_LATENCYDETECTOR_H_ */
422