39 template<std::
size_t N>
requires(N > 1)
42 std::span<
const float, N - 1>
Splits;
43 std::span<const float, N>
G;
49 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
57 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
58 class ShelfEQ : public Topology::Cascade<NumEQBands - 1>
61 static constexpr std::size_t NumBands = NumEQBands;
62 static constexpr std::size_t NumFilters = NumBands - 1;
68 using GainVec = std::array<float, NumBands>;
69 using GainMat = std::array<float, NumBands * NumBands>;
74 JPL_INLINE
bool Prepare(
const Params& params);
77 bool SetFrequencies(std::span<const float, NumFilters> frequencies);
80 void SetGainsDb(std::span<const float, NumBands> gainsDb);
85#ifdef JPL_ENABLE_ASSERTS
86 void ValidateFrequencies(std::span<const float, NumFilters> frequencies)
const;
87 void ValidateBandCentres(std::span<const float, NumBands> centres)
const;
92 float mInvSampleRate = -1.0f;
95 std::array<Cache::BiquadShelf, NumFilters> mCache;
107#ifdef JPL_ENABLE_ASSERTS
108 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
109 inline void ShelfEQ<NumEQBands>::ValidateFrequencies(std::span<const float, NumFilters> frequencies)
const
112 std::isfinite(mInvSampleRate) and
113 mInvSampleRate > 0.0f,
114 "Prepare must supply a valid sample rate.");
116 static constexpr double minBandHz = 20.0;
117 static constexpr double maxBandHz = 22050.0;
121 double previousHz = minBandHz;
123 for (
float frequency : frequencies)
126 std::isfinite(frequency) and
127 static_cast<double>(frequency) > previousHz and
128 static_cast<double>(frequency) < maxBandHz,
129 "Splits must be finite, strictly increasing, "
130 "and inside the propagation frequency range.");
132 const double normalized =
133 static_cast<double>(frequency) *
static_cast<double>(mInvSampleRate);
136 normalized > 0.0 and normalized < 0.5,
137 "Shelf frequencies must be below Nyquist.");
139 previousHz = frequency;
143 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
144 inline void ShelfEQ<NumEQBands>::ValidateBandCentres(std::span<const float, NumBands> centres)
const
146 float previousCentre = 0.0f;
147 for (
float centre : centres)
150 std::isfinite(centre) and
151 centre > previousCentre and
153 "Target centres must be finite, strictly increasing, "
154 "and below Nyquist.");
156 previousCentre = centre;
162 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
168 "Sample rate must be finite and positive.");
170 std::ranges::copy(params.
G, mG.begin());
172 return SetFrequencies(params.
Splits);
175 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
178#ifdef JPL_ENABLE_ASSERTS
179 ValidateFrequencies(frequencies);
182 std::array<Cache::BiquadShelf, NumFilters> newCache;
183 std::array<Topology::Biquad, NumFilters> sh;
186 for (
uint32 i = 0; i < frequencies.size(); ++i)
193 for (
float& c : centres)
196#ifdef JPL_ENABLE_ASSERTS
197 ValidateBandCentres(centres);
201 for (
uint32 row = 0; row < NumBands; ++row)
203 A[row * NumBands] = 1.0f;
204 for (
uint32 col = 1; col < NumBands; ++col)
205 A[row * NumBands + col] =
static_cast<float>(-
GainTodB(std::abs(sh[col - 1].CalculateResponse(centres[row]))));
209 const bool bInverted =
Inverse(A, mGinv);
211 if (not
JPL_ENSURE(bInverted,
"Gain conversion matrix doesn't have inverse."))
223 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
226 static constexpr uint32 N = NumBands;
227 static constexpr uint32 Width = N * 2;
229 std::array<double, N * Width> augmented{};
231 auto At = [&augmented](
uint32 row,
uint32 col) ->
double&
233 return augmented[row * Width + col];
238 for (
uint32 row = 0; row < N; ++row)
240 for (
uint32 col = 0; col < N; ++col)
242 const double value = A[row * N + col];
243 if (not std::isfinite(value))
246 At(row, col) = value;
247 scale = std::max(scale, std::abs(value));
250 At(row, N + row) = 1.0;
256 const double pivotTolerance =
257 64.0 *
static_cast<double>(N) * std::numeric_limits<double>::epsilon() * scale;
259 for (
uint32 col = 0; col < N; ++col)
263 double pivotMagnitude = std::abs(At(col, col));
265 for (
uint32 row = col + 1; row < N; ++row)
267 const double magnitude = std::abs(At(row, col));
269 if (magnitude > pivotMagnitude)
271 pivotMagnitude = magnitude;
276 if (not std::isfinite(pivotMagnitude) or pivotMagnitude <= pivotTolerance)
282 for (
uint32 j = 0; j < Width; ++j)
283 std::swap(At(col, j), At(pivotRow, j));
286 const double inversePivot = 1.0 / At(col, col);
288 for (
uint32 j = 0; j < Width; ++j)
289 At(col, j) *= inversePivot;
293 for (
uint32 row = 0; row < N; ++row)
298 const double factor = At(row, col);
300 for (
uint32 j = 0; j < Width; ++j)
301 At(row, j) -= factor * At(col, j);
309 for (
uint32 row = 0; row < N; ++row)
311 for (
uint32 col = 0; col < N; ++col)
313 const double value = At(row, N + col);
315 if (not std::isfinite(value) or std::abs(value) > std::numeric_limits<float>::max())
318 candidate[row * N + col] =
static_cast<float>(value);
326 template<std::
size_t NumEQBands>
requires(NumEQBands > 1)
331 std::ranges::copy(gainsDb, mG.begin());
335 for (
uint32 col = 0, invIdx = 0; col < NumBands; ++col)
337 for (
uint32 row = 0; row < NumBands; ++row)
338 Ks[col] += mGinv[invIdx++] * mG[row];
341 const float broadbandGain =
dBToGain(Ks[0]);
343 for (
uint32 i = 0; i < Base::Stages.size(); ++i)
347 auto& firstStage = Base::Stages[0];
348 firstStage.
b0 *= broadbandGain;
349 firstStage.b1 *= broadbandGain;
350 firstStage.b2 *= broadbandGain;
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:80
#define JPL_ENSURE(inExpression,...)
Define ENSURE.
Definition ErrorReporting.h:94
Multi-Shelf 2nd-order graphic equalizer.
Definition ShelfEQ.h:59
std::array< float, NumBands > GainVec
Definition ShelfEQ.h:68
std::array< float, NumBands *NumBands > GainMat
Definition ShelfEQ.h:69
Definition AcousticMaterial.h:36
@ Inverse
Definition DistanceAttenuation.h:35
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE T dBToGain(const T &dB) noexcept
Definition DecibelsAndGain.h:40
JPL_INLINE T GainTodB(const T &gainFactor) noexcept
Definition DecibelsAndGain.h:75
JPL_INLINE FreqBandCenters ComputeBandCenters(const SplitFrequencies &splitFrequenciesHz, float nyquist=22050.0f)
Definition FrequencyBands.h:56
static JPL_INLINE BiquadShelf From(double normalizedFrequency)
Definition FilterComponents.h:495
std::span< const float, N > G
Definition ShelfEQ.h:43
float SampleRate
Definition ShelfEQ.h:44
std::span< const float, N - 1 > Splits
Definition ShelfEQ.h:42
Definition FilterComponents.h:196
static JPL_INLINE Biquad MakeHighShelf(double normalizedFrequency, double gainDb)
Definition FilterComponents.h:490
Sample b0
Definition FilterComponents.h:104