HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_loudest_bin_frequency.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // max()
4#include <complex> // complex, norm()
5
7#include "metrics/hart_metric_query.hpp"
8#include "metrics/hart_metrics_common.hpp" // ChannelSubsets
9#include "hart_slice.hpp"
11#include "hart_units.hpp" // Unit
12#include "hart_utils.hpp" // nan()
13
14namespace hart
15{
16
17/// @brief Returns the center frequency of the loudest FFT bin
18/// @details
19/// Finds the maximum-magnitude FFT bin independently for each channel.
20/// Use reducers to combine multi-channel results (see @ref Reducers).
21///
22/// Supports `Unit::Hz` (native/default) unit, so requesting a unit
23/// explicitly via `MetricQuery::as()` is not required.
24///
25/// This metric operates on FFT bins exactly as stored in the Spectrum.
26/// In a not-so-likely event where multiple bins have exactly the same
27/// magnitube, the lowest frequency will be returned.
28/// This is not intended for precise pitch tracking, but still useful
29/// for some types of tests, based around looking for peaks in a band
30/// of frequencies, but not a specific frequency.
31///
32/// For more precise peak frequency estimation, consider using
33/// Quinn's second estimator (`hart::quinns2()` metric) instead.
34///
35/// Usage examples:
36/// @code
37/// // Loudest bin frequency in Hz
38/// const double loudestFreqHz = loudestBinFrequency (monoSpectrum).get();
39///
40/// // Loudest bin frequency, but only inside a frequency range
41/// const double loudestMidBanFreqHz =
42/// loudestBinFrequency (monoSpectrum)
43/// .at (Slice::frequency (500_Hz, 2000_Hz))
44/// .get();
45///
46/// // Loudest bin frequency, but only inside a specific channel subset.
47/// Calculated per chanel, then averaged.
48/// const double loudestFreqHz =
49/// loudestBinMagnitude (multiChannelSpectrum)
50/// .ch ({0, 2, 4})
51/// .get (mean());
52///
53/// @endcode
54///
55/// @param spectrum Input frequency-domain spectrum
56/// @throws hart::UnitError if unsupported unit is requested
57/// @ingroup Metrics
58inline MetricQuery<double> loudestBinFrequency (const Spectrum& spectrum)
59{
60 typename MetricQuery<double>::SingleChannelMetricEvaluator evaluator =
61 [&spectrum]
62 (size_t channel, const Slice& slice, Unit requestedUnit)
63 -> double
64 {
65 hassert (channel < spectrum.getNumChannels());
66
67 if (requestedUnit != Unit::native && requestedUnit != Unit::Hz)
68 HART_THROW_OR_RETURN (hart::UnitError, "Unsupported unit", hart::nan<double>());
69
70 const std::pair<size_t, size_t> binIndices = spectrum.getBinIndices (slice);
71 const size_t startBin = binIndices.first;
72 const size_t stopBin = binIndices.second;
73
74 if (slice.isEmpty() || stopBin - startBin == 0)
75 return hart::nan<double>();
76
77 hassert (startBin < stopBin);
78 hassert (stopBin <= spectrum.getNumBins());
79
80 const std::complex<double>* bins = spectrum[channel];
81 double maxSquaredMagnitude = 0.0;
82 size_t binOfMaxSquaredMagnitude = 0;
83
84 for (size_t currentBin = startBin; currentBin < stopBin; ++currentBin)
85 {
86 const double currentSquaredMagnitude = std::norm (bins[currentBin]);
87
88 if (currentSquaredMagnitude > maxSquaredMagnitude)
89 {
90 maxSquaredMagnitude = currentSquaredMagnitude;
91 binOfMaxSquaredMagnitude = currentBin;
92 }
93 }
94
95 return spectrum.getBinFrequencyHz (binOfMaxSquaredMagnitude);
96 };
97
98 const size_t numChannels = spectrum.getNumChannels();
99 return MetricQuery<double> (
100 std::move (evaluator),
101 numChannels,
103 );
104}
105
106} // namespace hart
Manages the metrics calculations.
MetricQuery(SingleChannelMetricEvaluator evaluator, size_t totalNumChannels, std::vector< size_t > &&defaultChannelsToProcess)
Create a metric query object for a metric that operates on one channel at a time.
Frequency-domain representation of a multi-channel audio signal.
const std::complex< double > * operator[](size_t channel) const
Returns pointer to read-only magnitudes of a specific channel.
std::pair< size_t, size_t > getBinIndices(const Slice &slice) const
Returns a pair of indices representing a provided slice.
double getBinFrequencyHz(size_t binIndex) const
Returns frequency corresponding to a bin index.
size_t getNumBins() const
Returns number of frequency bins per channel.
size_t getNumChannels() const
Returns number of channels.
Thrown when some metric is requested to return a value in an unsupported unit.
#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 ...
MetricQuery< double > loudestBinFrequency(const Spectrum &spectrum)
Returns the center frequency of the loudest FFT bin.
FloatType nan()
Returns a quiet NaN value for the given floating-point type.
Unit
Represents a physical unit.
@ native
Default (native) unit of whatever returns some value.
@ Hz
Hertz.
Helpers to generate common default channel subsets.
static std::vector< size_t > allChannels(size_t numChannels)
Represents a slice of analysis data.
bool isEmpty() const