9#include "matchers/hart_matcher.hpp"
23template<
typename SampleType>
32 m_expectedFundamentalHz (expectedFundamentalHz),
33 m_toleranceCents (toleranceCents)
35 if (expectedFundamentalHz <= 0.0)
39 void prepare (
double sampleRateHz, size_t , size_t )
override
41 m_sampleRateHz = sampleRateHz;
46 const size_t numFrames = observedAudio.getNumFrames();
51 if (!
this->m_channelsToMatch.anyTrue())
56 std::vector<
double> observedAudioMono (numFrames, 0.0);
57 const double numChannelsSelected =
static_cast<
double> (
this->m_channelsToMatch.numTrue());
59 for (size_t channel = 0; channel < observedAudio.getNumChannels(); ++channel)
61 if (!
this->appliesToChannel (channel))
64 for (size_t frame = 0; frame < numFrames; ++frame)
65 observedAudioMono[frame] +=
static_cast<
double> (observedAudio[channel][frame]) / numChannelsSelected;
71 while (fftSize < numFrames)
75 std::vector<std::complex<
double>> spectrum (fftSize, 0.0);
77 for (size_t i = 0; i < numFrames; ++i)
78 spectrum[i] = observedAudioMono[i];
80 calculateFFTInPlace (spectrum);
83 double maxPower = -1.0;
84 size_t strongestBin = 0;
86 for (size_t bin = 2; bin < fftSize / 2; ++bin)
88 const double power = std::norm (spectrum[bin]);
98 const double ym1 = std::abs (spectrum[strongestBin - 1]);
99 const double y0 = std::abs (spectrum[strongestBin]);
100 const double yp1 = std::abs (spectrum[strongestBin + 1]);
102 const double delta = 0.5 * (ym1 - yp1) / (ym1 - 2.0 * y0 + yp1 + 1e-30);
103 const double preciseBin =
static_cast<
double> (strongestBin) + delta;
105 m_observedHz = preciseBin * m_sampleRateHz /
static_cast<
double> (fftSize);
106 const double deviationCents = 1200.0 * std::log2 (m_observedHz / m_expectedFundamentalHz);
108 if (std::abs (deviationCents) > m_toleranceCents)
110 m_centsError = deviationCents;
126 std::stringstream stream;
127 stream <<
"Observed fundamental "
140 s <<
"FundamentalEquals ("
141 <<
hzPrecision << m_expectedFundamentalHz <<
"_Hz, "
146 const double m_expectedFundamentalHz;
147 const double m_toleranceCents;
148 double m_sampleRateHz = 44100.0;
149 double m_observedHz = 0.0;
150 double m_centsError = 0.0;
152 static void calculateFFTInPlace (std::vector<std::complex<
double>>& spectrum)
157 for (size_t temp = spectrum.size(); temp > 1; temp >>= 1)
160 for (size_t i = 0; i < spectrum.size(); ++i)
164 for (size_t j = 0; j < log2n; ++j)
165 if (i & (size_t (1) << j))
166 rev |= size_t (1) << (log2n - 1 - j);
169 std::swap (spectrum[i], spectrum[rev]);
173 for (size_t len = 2; len <= spectrum.size(); len <<= 1)
176 const std::complex<
double> wlen (std::cos (angleRadians), std::sin (angleRadians));
178 for (size_t i = 0; i < spectrum.size(); i += len)
180 std::complex<
double> w (1.0);
181 for (size_t j = 0; j < len / 2; ++j)
183 std::complex<
double> u = spectrum[i + j];
184 std::complex<
double> v = spectrum[i + j + len / 2] * w;
185 spectrum[i + j] = u + v;
186 spectrum[i + j + len / 2] = u - v;
Checks the fundamental frequency of the signal.
void represent(std::ostream &s) const override
Makes a text representation of this Matcher for test failure outputs.
MatcherFailureDetails getFailureDetails() const override
Returns a description of why the match has failed.
bool match(const AudioBuffer< SampleType > &observedAudio) override
Tells the host if the piece of audio satisfies Matcher's condition or not.
FundamentalEquals(double expectedFundamentalHz, double toleranceCents=1.0)
Creates a matcher for a specific fundamental frequency.
void prepare(double sampleRateHz, size_t, size_t) override
Prepare for processing It is guaranteed that all subsequent process() calls will be in line with the ...
bool canOperatePerBlock() override
Tells the host if it can operate on a block-by-block basis.
void reset() override
Resets the matcher to its initial state.
std::ostream & centsPrecision(std::ostream &stream)
Sets number of decimal places for values in cents (frequency deviation)
std::ostream & hzPrecision(std::ostream &stream)
Sets number of decimal places for values in hertz.
constexpr double twoPi
2 * pi
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)
#define HART_THROW(ExceptionType, message)
#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.