25 SampleType
q = SampleType{1};
58template <
typename SampleType =
float, std::
size_t MaxBands = 31>
class ParametricEqT {
59 static_assert(std::is_floating_point_v<SampleType>,
"ParametricEqT requires floating point");
60 static_assert(MaxBands > 0,
"ParametricEqT requires non-zero band storage");
73 static constexpr SampleType
minimum_q = SampleType{0.1};
74 static constexpr SampleType
maximum_q = SampleType{12};
77 std::size_t band_capacity = MaxBands)
noexcept {
81 if (band_capacity == 0 || band_capacity > MaxBands)
83 (void)cascades_[0].
prepare(band_capacity);
84 (void)cascades_[1].
prepare(band_capacity);
86 capacity_ = band_capacity;
88 active_count_ = requested_count_ = 0;
89 transition_total_ = transition_remaining_ = 0;
90 requested_bands_ = {};
92 requested_coefficients_ = {};
97 std::size_t transition_samples = 0) noexcept {
102 if (bands.size() > capacity_)
104 if (transition_samples == std::numeric_limits<std::size_t>::max())
107 std::array<Coefficients, MaxBands> candidate{};
108 std::array<Band, MaxBands> selected{};
109 std::size_t selected_count = 0;
110 bool has_solo =
false;
111 SampleType previous_frequency = SampleType{0};
112 const SampleType ceiling = supported_frequency_ceiling_hz();
113 for (std::size_t
i = 0;
i < bands.size(); ++
i) {
117 if (!(std::isfinite(
band.frequency_hz) && std::isfinite(
band.gain_db) &&
118 std::isfinite(
band.q)))
122 if (
i != 0 && !(
band.frequency_hz > previous_frequency))
128 has_solo = has_solo ||
band.solo;
129 previous_frequency =
band.frequency_hz;
132 if (!
band.enabled || (has_solo && !
band.solo))
134 selected[selected_count] =
band;
135 candidate[selected_count] = design_band(
band);
136 if (!finite(candidate[selected_count]))
143 const std::size_t staging = 1 - active_index_;
144 const auto installed = cascades_[staging].set_coefficients(
145 std::span<const Coefficients>(candidate.data(), selected_count),
156 requested_bands_ = selected;
157 requested_coefficients_ = candidate;
158 requested_count_ = selected_count;
159 if (transition_samples == 0) {
160 active_index_ = staging;
161 active_count_ = selected_count;
162 active_bands_ = selected;
163 cascades_[1 - active_index_].reset();
165 transition_total_ = transition_samples;
166 transition_remaining_ = transition_samples;
171 SampleType
process(SampleType input)
noexcept {
174 const SampleType from = cascades_[active_index_].process(input);
176 return std::isfinite(from) ? from : SampleType{};
177 const SampleType to = cascades_[1 - active_index_].process(input);
178 const std::size_t completed = transition_total_ - transition_remaining_ + 1;
179 const SampleType mix =
180 static_cast<SampleType
>(completed) /
static_cast<SampleType
>(transition_total_);
181 const SampleType output = transition_remaining_ == 1 ? to : std::lerp(from, to, mix);
182 if (--transition_remaining_ == 0) {
183 active_index_ = 1 - active_index_;
184 active_count_ = requested_count_;
185 active_bands_ = requested_bands_;
186 cascades_[1 - active_index_].reset();
188 return std::isfinite(output) ? output : SampleType{};
196 if (samples ==
nullptr && frames != 0)
202 [&](std::size_t
i) { samples[
i] =
process(samples[
i]); },
203 [&](std::size_t
first, std::size_t count) {
204 cascades_[active_index_].process(samples +
first, count);
206 samples[
i] = std::isfinite(samples[
i]) ? samples[
i] : SampleType{};
212 cascades_[0].reset();
213 cascades_[1].reset();
215 active_index_ = 1 - active_index_;
216 active_count_ = requested_count_;
217 active_bands_ = requested_bands_;
219 transition_total_ = transition_remaining_ = 0;
223 return capacity_ != 0;
226 return transition_remaining_ != 0;
232 return requested_count_;
241 return index < requested_count_ ? requested_bands_[index] :
Band{};
244 return index < requested_count_ ? requested_coefficients_[index] :
Coefficients{};
249 if (!
prepared() || !std::isfinite(frequency_hz) || frequency_hz < 0.0 ||
250 frequency_hz >
static_cast<double>(sample_rate_) * 0.5)
251 return std::numeric_limits<double>::quiet_NaN();
253 std::span<const Coefficients>(requested_coefficients_.data(), requested_count_),
261 std::span<const Coefficients>(requested_coefficients_.data(), requested_count_), min_hz,
262 max_hz,
static_cast<double>(sample_rate_), out);
267 return std::isfinite(
c.b0) && std::isfinite(
c.b1) && std::isfinite(
c.b2) &&
268 std::isfinite(
c.a1) && std::isfinite(
c.a2);
270 SampleType supported_frequency_ceiling_hz() const noexcept {
274 BiquadT<double> designer;
279 designer.set_coefficients(type,
static_cast<double>(
band.frequency_hz),
281 static_cast<double>(
band.gain_db));
282 const auto c = designer.coefficients();
283 return {
static_cast<SampleType
>(
c.b0),
static_cast<SampleType
>(
c.b1),
284 static_cast<SampleType
>(
c.b2),
static_cast<SampleType
>(
c.a1),
285 static_cast<SampleType
>(
c.a2)};
288 return design_band(
band, sample_rate_);
291 std::array<SosCascadeT<SampleType, MaxBands>, 2> cascades_{};
292 std::array<Band, MaxBands> requested_bands_{};
293 std::array<Band, MaxBands> active_bands_{};
294 std::array<Coefficients, MaxBands> requested_coefficients_{};
295 SampleType sample_rate_ = SampleType{};
296 std::size_t capacity_ = 0;
297 std::size_t active_index_ = 0;
298 std::size_t active_count_ = 0;
299 std::size_t requested_count_ = 0;
300 std::size_t transition_total_ = 0;
301 std::size_t transition_remaining_ = 0;
@ peaking
Definition biquad.hpp:58
@ high_shelf
Definition biquad.hpp:58
@ low_shelf
Definition biquad.hpp:58
A bounded series cascade of arbitrary parametric bands.
Definition parametric_eq.hpp:58
static constexpr float maximum_sample_rate_hz
Definition parametric_eq.hpp:68
bool process_block(SampleType *samples, std::size_t frames) noexcept
Process a block in place.
Definition parametric_eq.hpp:195
float sample_rate() const noexcept
Definition parametric_eq.hpp:234
ParametricEqPrepareStatus prepare(SampleType sample_rate, std::size_t band_capacity=MaxBands) noexcept
Definition parametric_eq.hpp:76
static constexpr float minimum_sample_rate_hz
Definition parametric_eq.hpp:67
std::size_t band_count() const noexcept
Definition parametric_eq.hpp:231
ParametricEqConfigureStatus configure(std::span< const Band > bands, std::size_t transition_samples=0) noexcept
Definition parametric_eq.hpp:96
static constexpr float maximum_gain_db
Definition parametric_eq.hpp:72
static constexpr float minimum_gain_db
Definition parametric_eq.hpp:71
double magnitude(double frequency_hz) const noexcept
Stationary response of the requested endpoint, in linear magnitude.
Definition parametric_eq.hpp:248
SampleType Sample
Definition parametric_eq.hpp:63
void reset() noexcept
Definition parametric_eq.hpp:211
Coefficients coefficients(std::size_t index) const noexcept
Definition parametric_eq.hpp:243
bool prepared() const noexcept
Definition parametric_eq.hpp:222
BiquadCoefficientsT< SampleType > Coefficients
Definition parametric_eq.hpp:65
static constexpr float maximum_frequency_hz
Definition parametric_eq.hpp:70
static constexpr std::size_t storage_capacity() noexcept
Definition parametric_eq.hpp:237
SampleType process(SampleType input) noexcept
Definition parametric_eq.hpp:171
float magnitude_db(double frequency_hz) const noexcept
Definition parametric_eq.hpp:256
static constexpr float minimum_frequency_hz
Definition parametric_eq.hpp:69
static constexpr float minimum_q
Definition parametric_eq.hpp:73
bool transitioning() const noexcept
Definition parametric_eq.hpp:225
void response_curve_db(double min_hz, double max_hz, std::span< float > out) const noexcept
Definition parametric_eq.hpp:259
static constexpr float maximum_q
Definition parametric_eq.hpp:74
ParametricEqBandT< SampleType > Band
Definition parametric_eq.hpp:64
std::size_t capacity() const noexcept
Definition parametric_eq.hpp:228
Band band(std::size_t index) const noexcept
Definition parametric_eq.hpp:240
Evaluate what a biquad (or a cascade of them) does to the spectrum.
void transition_then_block(std::size_t frames, Transitioning &&transitioning, Sample &&sample, Block &&block)
The block path the cascade EQs share: a design transition (crossfade or coefficient ramp) runs sample...
Definition sos_cascade.hpp:39
Definition additive_bank.hpp:218
float magnitude_to_db(double magnitude)
Convert a linear magnitude to dB, floored at min_response_db.
Definition frequency_response.hpp:35
@ first
Definition dither.hpp:23
double cascade_magnitude(std::span< const BiquadCoefficientsT< SampleType >, Extent > sos, double omega)
Linear magnitude |H| of a cascade at angular frequency ω.
Definition frequency_response.hpp:80
@ reset_state
Definition sos_cascade.hpp:27
@ c
Definition spectrum_trace.hpp:35
ParametricEqBandType
Definition parametric_eq.hpp:19
@ peaking
Definition parametric_eq.hpp:19
@ high_shelf
Definition parametric_eq.hpp:19
@ low_shelf
Definition parametric_eq.hpp:19
@ not_prepared
Definition comb_filter.hpp:33
@ non_finite
Definition sos_cascade.hpp:20
@ over_capacity
Definition sos_cascade.hpp:19
@ installed
Definition sos_cascade.hpp:17
@ unstable
Definition sos_cascade.hpp:21
@ not_prepared
Definition sos_cascade.hpp:18
@ invalid_sample_rate
Definition auto_ducked_send.hpp:18
ParametricEqBandT< float > ParametricEqBand
Definition parametric_eq.hpp:32
bool biquad_is_stable(const BiquadCoefficientsT< SampleType > &c) noexcept
Return whether a normalized real biquad's poles are strictly inside the unit circle.
Definition biquad.hpp:34
@ frequency_out_of_range
Definition graphic_eq.hpp:31
@ over_capacity
Definition graphic_eq.hpp:28
@ q_out_of_range
Definition graphic_eq.hpp:34
@ frequencies_not_strictly_increasing
Definition graphic_eq.hpp:32
@ invalid_transition
Definition graphic_eq.hpp:29
@ transition_in_progress
Definition graphic_eq.hpp:36
@ gain_out_of_range
Definition graphic_eq.hpp:33
@ i
Definition formant_filter_bank.hpp:20
double angular_frequency(double freq_hz, double sample_rate)
Angular frequency (radians/sample) of freq_hz at sample_rate, clamped to [0, π].
Definition frequency_response.hpp:97
@ prepared
Definition finite_stretch_builder.hpp:19
@ invalid_capacity
Definition graphic_eq.hpp:22
ParametricEqT< float > ParametricEq
Definition parametric_eq.hpp:304
ParametricEqPrepareStatus
Definition parametric_eq.hpp:34
@ invalid_sample_rate
Definition parametric_eq.hpp:34
@ prepared
Definition parametric_eq.hpp:34
@ invalid_capacity
Definition parametric_eq.hpp:34
ParametricEqConfigureStatus
Definition parametric_eq.hpp:36
@ frequency_out_of_range
Definition parametric_eq.hpp:42
@ invalid_solo
Definition parametric_eq.hpp:46
@ non_finite
Definition parametric_eq.hpp:41
@ over_capacity
Definition parametric_eq.hpp:39
@ configured
Definition parametric_eq.hpp:37
@ q_out_of_range
Definition parametric_eq.hpp:45
@ unstable
Definition parametric_eq.hpp:47
@ frequencies_not_strictly_increasing
Definition parametric_eq.hpp:43
@ invalid_transition
Definition parametric_eq.hpp:40
@ not_prepared
Definition parametric_eq.hpp:38
@ transition_in_progress
Definition parametric_eq.hpp:48
@ gain_out_of_range
Definition parametric_eq.hpp:44
ParametricEqT< double > ParametricEq64
Definition parametric_eq.hpp:305
@ non_finite
Definition formant_filter_bank.hpp:26
@ configured
Definition formant_filter_bank.hpp:23
@ unstable
Definition formant_filter_bank.hpp:29
@ type
Built-in / common type name (e.g. int, Number).
Definition code_editor_tokenizer.hpp:39
Fixed-capacity runtime executor for cascades of second-order IIR sections.
Definition parametric_eq.hpp:21
float q
Definition parametric_eq.hpp:25
float gain_db
Definition parametric_eq.hpp:24
friend constexpr bool operator==(const ParametricEqBandT &, const ParametricEqBandT &)=default
bool solo
Definition parametric_eq.hpp:27
ParametricEqBandType type
Definition parametric_eq.hpp:22
bool enabled
Definition parametric_eq.hpp:26
float frequency_hz
Definition parametric_eq.hpp:23