HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_centre_time.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vector>
4
8#include "metrics/hart_metric_query.hpp"
9#include "metrics/hart_metrics_common.hpp" // ChannelSubsets
10#include "hart_slice.hpp"
11#include "hart_units.hpp" // Unit
12#include "hart_utils.hpp" // nan(), floatsEqual()
13
14namespace hart
15{
16
17/// @brief Calculates the centre time of an impulse response.
18///
19/// Centre time (center time) is the energy-weighted mean arrival time of the
20/// impulse response. It describes where the energy of the response is centred
21/// in time: responses with more energy occurring later have a larger centre time.
22/// Can be used to verify shape of the decay, in combination with `hart::rt60()`.
23///
24/// Calculated as:
25///
26/// @f[
27/// T_s=\frac{\sum_{n=0}^{N-1} t_n h[n]^2}
28/// {\sum_{n=0}^{N-1} h[n]^2}
29/// @f]
30///
31/// (T_s = sum(t_n * h[n]^2) / sum(h[n]^2)),
32///
33/// where @f$h[n]@f$ is the impulse response sample, and @f$t_n@f$ is its
34/// time position (offset) in seconds.
35///
36/// Supported units are `Unit::seconds`, `Unit::native` (same as seconds) and
37/// `Unit::frames`. If `Unit::frames` is requested, the result will be a fractional
38/// value.
39///
40/// The result is NaN if the impulse response contains no energy.
41///
42/// @tparam SampleType Floating-point sample type of the impulse response.
43/// @param ir Impulse response to analyse.
44///
45/// @return A MetricQuery containing the centre time for each channel.
46/// Either in seconds, or in frames, depending on requested unit.
47/// May return `NaN`.
48/// @ingroup Metrics
49template <typename SampleType>
50MetricQuery<double> centreTime (const ImpulseResponse<SampleType>& ir)
51{
52 MetricQuery<double>::SingleChannelMetricEvaluator evaluator =
53 [&ir]
54 (size_t channel, Slice /* slice */, Unit requestedUnit)
55 -> double
56 {
57 hassert (channel < ir.getNumChannels());
58 const double nan = hart::nan<double>();
59
60 // TODO: Handle Slice for impulse responses in a way that makes sense
61
62 const size_t numFrames = ir.getNumFrames();
63
64 if (numFrames == 0)
65 return nan;
66
67 const double sampleRateHz = ir.getSampleRateHz();
68 hassert (sampleRateHz > 0.0);
69 const double samplePeriodSeconds = 1.0 / sampleRateHz;
70
71 const SampleType* irChannelData = ir[channel];
72
73 AccurateSum<double> numerator;
74 AccurateSum<double> denominator;
75
76 for (size_t i = 0; i < numFrames; ++i)
77 {
78 const double h = static_cast<double> (irChannelData[i]);
79 const double hSquared = h * h;
80 const double t = i * samplePeriodSeconds;
81
82 numerator += t * hSquared;
83 denominator += hSquared;
84 }
85
86 if (floatsEqual (denominator.getValue(), 0.0))
87 return nan;
88
89 const double centreTimeSeconds = numerator.getValue() / denominator.getValue();
90
91 switch (requestedUnit)
92 {
93 case Unit::native:
94 case Unit::seconds: return centreTimeSeconds;
95
96 case Unit::frames: return centreTimeSeconds * sampleRateHz;
97
98 default: HART_THROW_OR_RETURN (hart::UnitError, "Unsupported unit", hart::nan<double>());
99 }
100 };
101
102 const size_t numChannels = ir.getNumChannels();
103 return MetricQuery<double> (
104 std::move (evaluator),
105 numChannels,
107 );
108}
109
110} // 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.
Container for representing an impulse response (IR)
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.
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 > centreTime(const ImpulseResponse< SampleType > &ir)
Calculates the centre time of an impulse response.
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.
@ seconds
Time stamps, intervals, durations.
@ native
Default (native) unit of whatever returns some value.
@ frames
Value of something in frames (samples)
Helpers to generate common default channel subsets.
static std::vector< size_t > allChannels(size_t numChannels)
Represents a slice of analysis data.