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