18template<
typename SampleType>
38 m_oversamplingRatio (oversamplingRatio),
39 m_filterQuality (filterQuality),
40 m_maximumUnderReadLinear (calculateMaximumUnderReadLinear())
42 hassert (! floatsEqual (m_maximumUnderReadLinear, (SampleType) 0));
43 hassert (m_maximumUnderReadLinear <= (SampleType) 1);
48 void prepare (
double sampleRateHz, size_t numActiveChannels)
53 m_history.assign (numActiveChannels, std::vector<SampleType> (getTapsPerPhase(), (SampleType) 0));
54 m_historyTapIndex = 0;
56 m_TruePeakLinear = (SampleType) 0;
57 buildPhaseCoefficients();
59 m_sampleRateHz = sampleRateHz;
64 for (std::vector<SampleType>& channelHistory : m_history)
65 std::fill (channelHistory.begin(), channelHistory.end(), (SampleType) 0);
67 m_historyTapIndex = 0;
69 m_TruePeakLinear = (SampleType) 0;
77 hassert (m_history.size() == channels.size());
78 hassert (m_history[0].size() == getTapsPerPhase());
80 const auto sliceFrameIndices = observedOutputAudio.getFrameIndices (slice);
81 const size_t sliceStart = sliceFrameIndices.first;
82 const size_t sliceStop = sliceFrameIndices.second;
83 hassert (sliceStop > sliceStart);
84 hassert (sliceStop - sliceStart != 0);
85 hassert (sliceStop <= observedOutputAudio.getNumFrames());
87 const size_t ratio = getRatio();
89 SampleType truePeakValueLinear = (SampleType) 0;
90 size_t truePeakChannel = 0;
91 double truePeakFrame = 0.0;
93 for (size_t frame = sliceStart; frame < sliceStop; ++frame)
95 size_t historyChannelIndex = 0;
97 for (size_t channel : channels)
99 hassert (channel < observedOutputAudio.getNumChannels());
100 hassert (historyChannelIndex < m_history.size())
102 m_history[historyChannelIndex][m_historyTapIndex] = observedOutputAudio[channel][frame];
104 for (size_t phase = 0; phase < ratio; ++phase)
106 const SampleType oversampledPeakLinear = evaluatePolyphaseFIR (historyChannelIndex, phase);
107 const SampleType rectifiedPeakLinear = std::abs (oversampledPeakLinear);
109 if (rectifiedPeakLinear > truePeakValueLinear)
111 truePeakValueLinear = rectifiedPeakLinear;
112 truePeakChannel = channel;
114 static_cast<
double> (m_offsetFrames) +
115 static_cast<
double> (phase) /
static_cast<
double> (ratio);
120 ++historyChannelIndex;
123 m_historyTapIndex = (m_historyTapIndex + 1) % getTapsPerPhase();
127 return {truePeakValueLinear, truePeakChannel, truePeakFrame};
130 SampleType getMaximumUnderReadLinear()
const
132 return m_maximumUnderReadLinear;
135 friend std::ostream& operator<< (std::ostream& os, FilterQuality filterQuality)
137 os <<
"FilterQuality::";
139 switch (filterQuality)
141 case FilterQuality::low : os <<
"low";
break;
142 case FilterQuality::medium : os <<
"medium";
break;
143 case FilterQuality::high : os <<
"high";
break;
150 static constexpr double m_fNorm = 0.45;
152 const FilterQuality m_filterQuality;
153 const SampleType m_maximumUnderReadLinear;
155 SampleType m_TruePeakLinear =
static_cast<SampleType>(0);
160 std::vector<std::vector<SampleType>> m_history;
161 size_t m_historyTapIndex = 0;
162 size_t m_offsetFrames = 0;
165 std::vector<std::vector<SampleType>> m_phaseCoefficients;
167 inline SampleType calculateMaximumUnderReadLinear()
const
172 return static_cast<SampleType> (std::cos (
pi * m_fNorm / getRatio()));
175 inline size_t getRatio()
const
177 return static_cast<size_t> (m_oversamplingRatio);
180 inline size_t getTapsPerPhase()
const
182 return static_cast<size_t> (m_filterQuality);
185 SampleType evaluatePolyphaseFIR (size_t historyChannelIndex, size_t phase)
const
187 const auto& history = m_history[historyChannelIndex];
188 const auto& coeffs = m_phaseCoefficients[phase];
190 AccurateSum<SampleType> sum;
191 const size_t size = history.size();
193 for (size_t tap = 0; tap < coeffs.size(); ++tap)
195 const size_t index = (m_historyTapIndex + size - tap) % size;
196 sum += history[index] * coeffs[tap];
199 return sum.getValue();
202 void buildPhaseCoefficients()
207 const size_t ratio = getRatio();
208 const size_t tapsPerPhase = getTapsPerPhase();
209 m_phaseCoefficients.assign (ratio, std::vector<SampleType> (tapsPerPhase, (SampleType) 0));
211 const double center =
static_cast<
double> (tapsPerPhase - 1) / 2.0;
213 for (size_t phase = 0; phase < ratio; ++phase)
215 AccurateSum<SampleType> norm;
216 const double frac =
static_cast<
double> (phase) /
static_cast<
double> (ratio);
218 for (size_t tap = 0; tap < tapsPerPhase; ++tap)
220 const double x =
static_cast<
double> (tap) - center - frac;
224 const double window =
225 0.5 - 0.5 * std::cos (2.0 *
pi *
static_cast<
double> (tap) /
static_cast<
double> (tapsPerPhase - 1));
227 const SampleType coeff =
static_cast<SampleType> (sinc * window);
228 m_phaseCoefficients[phase][tap] = coeff;
232 const SampleType normValue = norm.getValue();
235 for (SampleType& c : m_phaseCoefficients[phase])
264template <
typename SampleType>
268 typename TruePeak<SampleType>::FilterQuality filterQuality = TruePeak<SampleType>::FilterQuality::low
274 auto truePeakEstimator = std::make_shared<TruePeak<SampleType>> (oversamplingRatio, filterQuality);
275 truePeakEstimator->prepare (
276 audioBuffer.getSampleRateHz(),
280 MetricQuery<
double>::SingleChannelMetricEvaluator evaluator =
281 [&audioBuffer, truePeakEstimator]
282 (size_t channel, Slice slice, Unit requestedUnit)
285 hassert (truePeakEstimator !=
nullptr);
286 truePeakEstimator->reset();
287 const typename TruePeak<SampleType>::Result estimatorResult = truePeakEstimator->estimate (audioBuffer, {{channel}}, slice);
288 const double truePeakLinear =
static_cast<
double> (estimatorResult.valueLinear);
290 switch (requestedUnit)
292 case Unit::linear:
return truePeakLinear;
295 case Unit::dB:
return hart::ratioToDecibels (truePeakLinear);
301 const size_t numChannels = audioBuffer.getNumChannels();
302 return MetricQuery<
double> (
303 std::move (evaluator),
305 ChannelSubsets::allChannels (numChannels)
Container for audio data.
#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 > truePeak(const AudioBuffer< SampleType > &audioBuffer, Oversampling oversamplingRatio=Oversampling::x4, typename TruePeak< SampleType >::FilterQuality filterQuality=TruePeak< SampleType >::FilterQuality::low)
Estimates true peak (inter-sample peak) level.
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.
Oversampling
Oversampling ratio.
Represents a slice of analysis data.
const SampleType valueLinear