49template <
typename SampleType>
81 m_minimumSignedCorrelation (minimumSignedCorrelation),
82 m_maxLagSeconds (maxLagSeconds),
83 m_silencePolicy (silencePolicy)
85 if (m_minimumSignedCorrelation < 0 || m_minimumSignedCorrelation > 1.0)
88 "Signed correlation threshold should be in 0..1 range",
92 if (m_maxLagSeconds < 0)
95 "Max lag should be a non-negative number in seconds",
102 size_t numInputChannels,
103 size_t numOutputChannels,
107 hassert (numInputChannels == numOutputChannels);
108 m_sampleRateHz = sampleRateHz;
109 m_maxLagFrames =
static_cast<
long long int> (std::round (m_maxLagSeconds * m_sampleRateHz));
119 m_failureChannel = 0;
121 m_bestSignedCorrelation = 0.0;
123 m_hadValidData =
false;
128 return numInputChannels == numOutputChannels;
133 const AudioBuffer<SampleType>& inputAudio = context.inputAudio();
134 const AudioBuffer<SampleType>& observedOutputAudio = context.outputAudio();
136 hassert (inputAudio.getNumChannels() == observedOutputAudio.getNumChannels());
137 hassert (inputAudio.getNumFrames() == observedOutputAudio.getNumFrames());
138 hassert (inputAudio.getSampleRateHz() == observedOutputAudio.getSampleRateHz());
140 const size_t numFrames = inputAudio.getNumFrames();
144 m_hadValidData =
false;
148 const size_t numChannels = inputAudio.getNumChannels();
149 bool anyValidChannel =
false;
151 for (size_t channel = 0; channel < numChannels; ++channel)
153 if (!
this->appliesToChannel (channel))
156 const SampleType* x = inputAudio[channel];
157 const SampleType* y = observedOutputAudio[channel];
158 std::vector<
double> prefixSumsSqX (numFrames + 1, 0.0);
159 std::vector<
double> prefixSumsSqY (numFrames + 1, 0.0);
163 for (size_t frame = 0; frame < numFrames; ++frame)
165 const double xVal =
static_cast<
double> (x[frame]);
166 const double yVal =
static_cast<
double> (y[frame]);
168 runningSumSqX
+= xVal * xVal;
169 runningSumSqY
+= yVal * yVal;
170 prefixSumsSqX[frame + 1] = runningSumSqX;
171 prefixSumsSqY[frame + 1] = runningSumSqY;
175 double bestSignedCorrelation = 0.0;
176 long long int bestLag = 0;
177 bool channelValid =
false;
179 for (
long long int lag = -m_maxLagFrames; lag <= m_maxLagFrames; ++lag)
182 const bool lagShiftsOutputToTheLeft = lag < 0;
183 const size_t lagAbsFrames =
static_cast<size_t> (lagShiftsOutputToTheLeft ? -lag : lag);
185 if (lagAbsFrames >= numFrames)
190 const size_t inputOverlapBeginFrame = lagShiftsOutputToTheLeft ? lagAbsFrames : 0;
191 const size_t outputOverlapBeginFrame = lagShiftsOutputToTheLeft ? 0 : lagAbsFrames;
192 const size_t overlapSizeFrames = numFrames - lagAbsFrames;
193 const size_t inputOverlapEndFrame = inputOverlapBeginFrame + overlapSizeFrames;
194 const size_t outputOverlapEndFrame = outputOverlapBeginFrame + overlapSizeFrames;
195 const double sumSqX = prefixSumsSqX[inputOverlapEndFrame] - prefixSumsSqX[inputOverlapBeginFrame];
196 const double sumSqY = prefixSumsSqY[outputOverlapEndFrame] - prefixSumsSqY[outputOverlapBeginFrame];
198 for (size_t overlapFrame = 0; overlapFrame < overlapSizeFrames; ++overlapFrame)
200 const double inputValue =
static_cast<
double> (x[inputOverlapBeginFrame + overlapFrame]);
201 const double outputValue =
static_cast<
double> (y[outputOverlapBeginFrame + overlapFrame]);
202 dotProduct
+= inputValue * outputValue;
209 const double corr = dotProduct / std::sqrt (sumSqX * sumSqY);
210 const double absCorr = std::abs (corr);
212 if (absCorr > bestAbsCorrelation)
214 bestAbsCorrelation = absCorr;
215 bestSignedCorrelation = corr;
227 m_hadValidData =
false;
228 m_failureChannel = channel;
236 anyValidChannel =
true;
238 if (bestSignedCorrelation < m_minimumSignedCorrelation)
240 m_hadValidData =
true;
241 m_failureChannel = channel;
243 m_bestSignedCorrelation = bestSignedCorrelation;
244 m_bestLagFrames = bestLag;
249 if (! anyValidChannel)
251 m_hadValidData =
false;
252 m_failureChannel = 0;
264 details
.frame = m_failureFrame;
268 details
.description =
"Polarity could not be determined with sufficient confidence";
272 const double lagSeconds = m_bestLagFrames / m_sampleRateHz;
273 std::stringstream stream;
276 <<
"Detected signed correlation: "
278 <<
" at lag " << m_bestLagFrames <<
" frames ("
288 <<
"PolarityPreserved ("
297 const double m_minimumSignedCorrelation;
298 const double m_maxLagSeconds;
301 double m_sampleRateHz = 0.0;
302 long long int m_maxLagFrames = 0;
304 double m_bestSignedCorrelation = 0.0;
305 long long int m_bestLagFrames = 0;
307 size_t m_failureChannel = 0;
308 size_t m_failureFrame = 0;
309 bool m_hadValidData =
false;
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.
Contains audio-related artefacts useful for analysis by matchers.
Container for audio data.
Checks whether the output signal preserves the polarity of the input signal.
MatcherFailureDetails getFailureDetails() const override
Returns a description of why the match has failed.
void represent(std::ostream &stream) const override
Makes a text representation of this Matcher for test failure outputs.
PolarityPreserved(double minimumSignedCorrelation=0.5, double maxLagSeconds=0.01, SilencePolicy silencePolicy=SilencePolicy::strict)
Creates a polarity matcher with a minimum signed correlation threshold.
bool match(AnalysisContext< SampleType > context) override
Tells the host if the piece of audio satisfies Matcher's condition or not.
void prepare(double sampleRateHz, size_t numInputChannels, size_t numOutputChannels, size_t) override
Prepare for processing It is guaranteed that all subsequent process() calls will be in line with the ...
bool canOperatePerBlock() const override
Tells the host if it can operate on a block-by-block basis.
void reset() override
Resets the matcher to its initial state.
bool supportsChannelLayout(size_t numInputChannels, size_t numOutputChannels) const override
Tells the host whether this Matcher is capable of operating on audio with a specific number of channe...
Thrown when an inappropriate value is encountered.
#define hassert(condition)
Triggers a HartAssertException if the condition is false
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns a specified ...
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.
#define HART_MATCHER_DECLARE_ALIASES_FOR(ClassName)
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.