18template <
typename SampleType>
19class CorrelationLatencyDetector :
20 public LatencyDetector<SampleType>
23 CorrelationLatencyDetector (
double maxLatencySeconds,
SilencePolicy silencePolicy,
double absCorrelationThreshold) :
24 m_maxLatencySeconds (maxLatencySeconds),
25 m_silencePolicy (silencePolicy),
26 m_absCorrelationThreshold (absCorrelationThreshold)
28 if (absCorrelationThreshold > 1.0)
31 if (absCorrelationThreshold < 0.0)
39 std::unique_ptr<LatencyDetector<SampleType>> copy()
const override
41 return hart::make_unique<CorrelationLatencyDetector<SampleType>> (*
this);
44 void prepare (
double sampleRateHz, size_t , size_t )
override
46 m_sampleRateHz = sampleRateHz;
51 m_hadValidData =
false;
54 m_detectedLatencyFrames = 0;
55 m_bestCorrelation = 0.0;
61 const std::function<
bool (size_t)>& appliesToChannel
64 const size_t numFrames = inputAudio.getNumFrames();
68 m_hadValidData =
false;
72 const size_t maxLagFrames = numFrames - 1;
73 bool anyValidChannel =
false;
74 size_t worstLatencyFrames = 0;
75 size_t worstChannel = 0;
77 for (size_t channel = 0; channel < inputAudio.getNumChannels(); ++channel)
79 if (! appliesToChannel (channel))
82 const SampleType* x = inputAudio[channel];
83 const SampleType* y = observedOutputAudio[channel];
84 std::vector<
double> prefixSumsSqX (numFrames + 1, 0.0);
85 std::vector<
double> prefixSumsSqY (numFrames + 1, 0.0);
89 for (size_t frame = 0; frame < numFrames; ++frame)
91 const double xVal =
static_cast<
double> (x[frame]);
92 const double yVal =
static_cast<
double> (y[frame]);
94 runningSumSqX
+= xVal * xVal;
95 runningSumSqY
+= yVal * yVal;
96 prefixSumsSqX[frame + 1] = runningSumSqX;
97 prefixSumsSqY[frame + 1] = runningSumSqY;
102 bool channelValid =
false;
104 for (size_t lag = 0; lag <= maxLagFrames; ++lag)
107 const size_t inputOverlapBeginFrame = 0;
108 const size_t outputOverlapBeginFrame = lag;
109 const size_t overlapSizeFrames = numFrames - lag;
110 const size_t inputOverlapEndFrame = inputOverlapBeginFrame + overlapSizeFrames;
111 const size_t outputOverlapEndFrame = outputOverlapBeginFrame + overlapSizeFrames;
112 const double sumSqX = prefixSumsSqX[inputOverlapEndFrame] - prefixSumsSqX[inputOverlapBeginFrame];
113 const double sumSqY = prefixSumsSqY[outputOverlapEndFrame] - prefixSumsSqY[outputOverlapBeginFrame];
115 for (size_t overlapFrame = 0; overlapFrame < overlapSizeFrames; ++overlapFrame)
117 const double inputValue =
static_cast<
double> (x[inputOverlapBeginFrame + overlapFrame]);
118 const double outputValue =
static_cast<
double> (y[outputOverlapBeginFrame + overlapFrame]);
119 dotProduct
+= inputValue * outputValue;
126 const double correlation = dotProduct / std::sqrt (sumSqX * sumSqY);
127 const double absCorrelation = std::abs (correlation);
129 if (absCorrelation > bestAbsCorrelation)
131 bestAbsCorrelation = absCorrelation;
139 if (! channelValid || bestAbsCorrelation < m_absCorrelationThreshold)
143 m_hadValidData =
false;
144 m_failureChannel = channel;
151 anyValidChannel =
true;
153 if (bestLag > worstLatencyFrames)
155 worstLatencyFrames = bestLag;
156 worstChannel = channel;
157 m_bestCorrelation = bestAbsCorrelation;
161 if (!anyValidChannel)
163 m_hadValidData =
false;
167 m_hadValidData =
true;
168 m_detectedLatencyFrames = worstLatencyFrames;
169 const double latencySeconds = m_detectedLatencyFrames / m_sampleRateHz;
171 if (latencySeconds <= m_maxLatencySeconds)
174 m_failureChannel = worstChannel;
182 details
.frame = m_failureFrame;
184 if (! m_hadValidData)
186 details
.description =
"Latency could not be determined with sufficient correlation";
190 const double latencySeconds = m_detectedLatencyFrames / m_sampleRateHz;
192 std::stringstream descriptionStream;
194 <<
"Detected latency: "
196 << m_detectedLatencyFrames <<
" frames), "
204 const double m_maxLatencySeconds;
206 const double m_absCorrelationThreshold;
207 double m_sampleRateHz = 0.0;
209 bool m_hadValidData =
false;
210 size_t m_detectedLatencyFrames = 0;
211 double m_bestCorrelation = 0.0;
212 size_t m_failureChannel = 0;
213 size_t m_failureFrame = 0;
Implements Kahan algorithm for floating point accumulations.
AccurateSum(SampleType initialSum=(SampleType) 0)
Inits AccurateSum with a specific value.
AccurateSum & operator+=(SampleType value)
Adds a value to a sum, tracking the potential floating point error.
Container for audio data.
Thrown when an inappropriate value is encountered.
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns otherwise.
std::ostream & secPrecision(std::ostream &stream)
Sets number of decimal places for values in seconds.
static std::ostream & correlationPrecision(std::ostream &stream)
Sets number of decimal places for correlation values.
SilencePolicy
Defines how silence in various algorithms.
constexpr double inf
Infinity.
static SampleType floatsEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
Details about matcher failure.
size_t channel
Index of channel at which the failure was detected.
std::string description
Readable description of why the match has failed.
size_t frame
Index of frame at which the match has failed.