HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_spectral_centroid.hpp
Go to the documentation of this file.
1#pragma once
2
5#include "metrics/hart_metric_query.hpp"
6#include "metrics/hart_metrics_common.hpp" // ChannelSubsets
7#include "hart_slice.hpp"
9#include "hart_units.hpp" // Unit
10#include "hart_utils.hpp" // nan(), floatsEqual()
11
12// TODO: Add more detailed description
13
14namespace hart
15{
16
18{
19 enum class Weighting
20 {
22 power
23 };
24
25} // namespace SpectralCentroid
26
27/// @brief Calculates spectral centroid
28/// @details Commonly used to numerically express amound of "brightness" of a sound.
29///
30/// You have an option to pick one of two common weighting methods:
31///
32/// 1. Magnitude-weighted:
33/// @f[
34/// C_{mag}=\frac{\sum_{k=0}^{N-1} f_k |X_k|}{\sum_{k=0}^{N-1} |X_k|}
35/// @f]
36///
37/// (C_mag = sum(f_k * abs(X_k)) / sum(abs(X_k))),
38///
39/// 2. Power-weighted:
40/// @f[
41/// C_{pow}=\frac{\sum_{k=0}^{N-1} f_k |X_k|^2}{\sum_{k=0}^{N-1} |X_k|^2}
42/// @f]
43///
44/// C_pow = sum(f_k * abs(X_k)^2) / sum(abs(X_k)^2)
45///
46/// Where f<sub>k</sub> is a center frequency of each bin, and X<sub>k</sub> is a magnitude of this bin.
47/// In both cases the result is measured in Hertz, so accepted units are Unit::native and Unit::Hz,
48/// which are the same.
49///
50/// See @ref UsingMetricsAndReducers for how to use metrics that return a `MetricQuery` object line this one.
51/// @param spectrum Spectrum of a single to operate on
52/// @param weighting Type of weighting (see description above)
53/// @return Chainable `MetricQuery`, which calculates per-channel spectral centroid values in Hz
54/// @ingroup Metrics
56{
57 typename MetricQuery<double>::SingleChannelMetricEvaluator evaluator =
58 [&spectrum, weighting]
59 (size_t channel, const Slice& slice, Unit requestedUnit)
60 -> double
61 {
62 hassert (channel < spectrum.getNumChannels());
63 hassert (! std::isnan (spectrum.getSampleRateHz()));
64
65 if (requestedUnit != Unit::native && requestedUnit != Unit::Hz)
66 HART_THROW_OR_RETURN (hart::UnitError, "Unsupported unit", hart::nan<double>());
67
68 const std::pair<size_t, size_t> binIndices = spectrum.getBinIndices (slice);
69 const size_t startBin = binIndices.first;
70 const size_t stopBin = binIndices.second;
71
72 if (slice.isEmpty() || stopBin - startBin == 0)
73 return hart::nan<double>();
74
75 hassert (startBin < stopBin);
76 hassert (stopBin <= spectrum.getNumBins());
77
78 AccurateSum<double> numerator;
79 AccurateSum<double> denominator;
80
82 {
83 for (size_t bin = startBin; bin < stopBin; ++bin)
84 {
85 const double magnitudeLinear = spectrum.getBinMagnitude (channel, bin);
86 const double frequencyHz = spectrum.getBinFrequencyHz (bin);
87
88 numerator += magnitudeLinear * frequencyHz;
89 denominator += magnitudeLinear;
90 }
91 }
92 else // weighting == SpectralCentroid::Weighting::power
93 {
94 for (size_t bin = startBin; bin < stopBin; ++bin)
95 {
96 const double magnitudeLinear = spectrum.getBinMagnitude (channel, bin);
97 const double powerLinear = magnitudeLinear * magnitudeLinear;
98 const double frequencyHz = spectrum.getBinFrequencyHz (bin);
99
100 numerator += powerLinear * frequencyHz;
101 denominator += powerLinear;
102 }
103 }
104
105 if (floatsEqual (denominator.getValue(), 0.0))
106 return hart::nan<double>();
107
108 return numerator.getValue() / denominator.getValue();
109 };
110
111 const size_t numChannels = spectrum.getNumChannels();
112 return MetricQuery<double> (
113 std::move (evaluator),
114 numChannels,
116 );
117}
118
119} // namespace hart
Implements Kahan algorithm for floating point accumulations.
SampleType getValue() const
AccurateSum & operator+=(SampleType value)
Adds a value to a sum, tracking the potential floating point error.
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.
double getSampleRateHz() const
Returns sample rate in Hz.
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.
double getBinMagnitude(size_t channel, size_t binIndex) const
Returns magnitude of a frequency bin, by 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 > spectralCentroid(const Spectrum &spectrum, SpectralCentroid::Weighting weighting=SpectralCentroid::Weighting::magnitude)
Calculates spectral centroid.
FloatType nan()
Returns a quiet NaN value for the given floating-point type.
static SampleType floatsEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
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