29 static_assert(std::is_floating_point_v<SampleType>,
30 "SixBandEqT requires a floating-point sample type");
31 static_assert(Channels > 0,
"SixBandEqT requires at least one channel");
40 static constexpr SampleType
min_q = SampleType{0.1};
41 static constexpr SampleType
max_q = SampleType{12};
51 SampleType
q = SampleType{1};
61 apply_all_parameters_immediate();
69 if (samples == transition_samples_)
return;
70 transition_samples_ = samples;
75 return channel < Channels && transition_remaining_[channel] != 0;
78 return std::any_of(transition_remaining_.begin(), transition_remaining_.end(),
79 [](std::size_t remaining) { return remaining != 0; });
86 parameters = sanitize(
band, parameters);
87 if (parameters_equal(parameters_[
band], parameters))
return true;
88 parameters_[
band] = parameters;
89 design_parameters(
band);
97 void set_bands(
const std::array<Parameters, band_count>& parameters) {
98 std::array<Parameters, band_count> sanitized{};
102 changed = changed || !parameters_equal(parameters_[
band], sanitized[
band]);
104 if (!changed)
return;
106 parameters_[
band] = sanitized[
band];
107 design_parameters(
band);
109 schedule_all_bands();
114 Parameters
band(std::size_t index)
const {
115 return index <
band_count ? parameters_[index] : Parameters{};
120 return index <
band_count ? types_[index] : Type::peaking;
124 return index <
band_count ? defaults_[index] : Parameters{};
135 SampleType
process(SampleType input, std::size_t channel = 0) {
136 if (channel >= Channels)
return input;
138 for (
auto& section : filters_[channel]) input = section.process(input);
141 SampleType from = input;
142 SampleType to = input;
143 for (
auto& section : filters_[channel]) from = section.process(from);
144 for (
auto& section : transition_filters_[channel]) to = section.process(to);
145 const std::size_t completed = transition_total_[channel] - transition_remaining_[channel] + 1;
146 const SampleType mix =
static_cast<SampleType
>(completed) /
147 static_cast<SampleType
>(transition_total_[channel]);
148 const SampleType output = from + (to - from) * mix;
149 if (--transition_remaining_[channel] == 0) {
150 filters_[channel] = transition_filters_[channel];
151 if (queued_transition_[channel]) {
152 const std::size_t queued_samples = queued_transition_samples_[channel];
153 queued_transition_[channel] =
false;
154 queued_transition_samples_[channel] = 0;
155 begin_transition(channel, queued_samples);
168 bool process_block(SampleType* samples, std::size_t frames, std::size_t channel = 0) {
169 if (channel >= Channels || (samples ==
nullptr && frames != 0))
return false;
172 [&](std::size_t
i) { samples[
i] =
process(samples[
i], channel); },
173 [&](std::size_t
first, std::size_t count) {
181 for (
auto& channel : filters_)
182 for (
auto& section : channel) section.reset();
183 for (
auto& channel : transition_filters_)
184 for (
auto& section : channel) section.reset();
190 const double omega = query_omega(frequency_hz);
191 const std::complex<double> z1 = std::polar(1.0, -omega);
192 const std::complex<double> z2 = z1 * z1;
196 const auto numerator =
static_cast<double>(
c.b0) +
static_cast<double>(
c.b1) * z1 +
197 static_cast<double>(
c.b2) * z2;
198 const auto denominator = 1.0 +
static_cast<double>(
c.a1) * z1 +
199 static_cast<double>(
c.a2) * z2;
200 const double denominator_magnitude = std::abs(denominator);
201 if (!(denominator_magnitude > 0.0))
return 0.0;
202 result *= std::abs(numerator) / denominator_magnitude;
210 if (!(
linear > 0.0))
return -200.0;
211 const double db = 20.0 * std::log10(
linear);
212 return std::isfinite(db) ? std::max(db, -200.0) : -200.0;
218 if (output.empty())
return;
219 const double nyquist =
static_cast<double>(sample_rate_) * 0.5;
220 const double lo = sanitize_query(min_hz, nyquist);
221 const double hi = std::max(lo, sanitize_query(max_hz, nyquist));
222 for (std::size_t
i = 0;
i < output.size(); ++
i) {
223 const double t = output.size() == 1
225 :
static_cast<double>(
i) /
static_cast<double>(output.size() - 1);
226 const double frequency = lo > 0.0 && hi > 0.0
227 ? std::exp(std::log(lo) + t * (std::log(hi) - std::log(lo)))
229 output[
i] =
static_cast<SampleType
>(
magnitude_db(frequency));
234 static constexpr std::array<Type, band_count> types_ = {
235 Type::low_shelf, Type::peaking, Type::peaking,
236 Type::peaking, Type::peaking, Type::high_shelf};
238 static constexpr std::array<Parameters, band_count> defaults_ = {{
239 {SampleType{80}, SampleType{0}, SampleType{0.707}},
240 {SampleType{250}, SampleType{0}, SampleType{1}},
241 {SampleType{700}, SampleType{0}, SampleType{1.2}},
242 {SampleType{2000}, SampleType{0}, SampleType{1.2}},
243 {SampleType{5000}, SampleType{0}, SampleType{1}},
244 {SampleType{12000}, SampleType{0}, SampleType{0.707}},
247 static bool valid_sample_rate(SampleType
sample_rate) {
254 const SampleType frequency_ceiling =
256 if (!std::isfinite(parameters.frequency_hz)) parameters.frequency_hz =
fallback.frequency_hz;
257 if (!std::isfinite(parameters.gain_db)) parameters.gain_db =
fallback.gain_db;
258 if (!std::isfinite(parameters.q)) parameters.q =
fallback.q;
259 parameters.frequency_hz =
262 parameters.q = std::clamp(parameters.q,
min_q,
max_q);
267 return lhs.frequency_hz == rhs.frequency_hz && lhs.gain_db == rhs.gain_db &&
271 void design_parameters(std::size_t
band) {
273 const auto& parameters = parameters_[
band];
274 designer.set_coefficients(types_[
band], parameters.frequency_hz, parameters.q,
275 sample_rate_, parameters.gain_db);
276 requested_coefficients_[
band] = designer.coefficients();
279 void apply_all_parameters_immediate() {
282 design_parameters(
band);
284 for (std::size_t channel = 0; channel < Channels; ++channel) {
286 filters_[channel][
band].set_coefficients(requested_coefficients_[
band]);
287 transition_remaining_[channel] = 0;
288 transition_total_[channel] = 0;
289 queued_transition_[channel] =
false;
290 queued_transition_samples_[channel] = 0;
294 void load_requested(std::array<Filter, band_count>& cascade) {
296 cascade[
band].set_coefficients(requested_coefficients_[
band]);
299 void begin_transition(std::size_t channel, std::size_t samples) {
300 transition_filters_[channel] = filters_[channel];
301 load_requested(transition_filters_[channel]);
302 transition_total_[channel] = samples;
303 transition_remaining_[channel] = samples;
306 void queue_transition(std::size_t channel) {
307 if (queued_transition_[channel])
return;
308 queued_transition_[channel] =
true;
312 queued_transition_samples_[channel] =
313 transition_samples_ != 0 ? transition_samples_ : transition_total_[channel];
316 void schedule_band(std::size_t
band) {
317 for (std::size_t channel = 0; channel < Channels; ++channel) {
319 if (transition_samples_ == 0)
320 filters_[channel][
band].set_coefficients(requested_coefficients_[
band]);
322 begin_transition(channel, transition_samples_);
324 else if (transition_remaining_[channel] == transition_total_[channel])
325 transition_filters_[channel][
band].set_coefficients(requested_coefficients_[
band]);
327 queue_transition(channel);
331 void schedule_all_bands() {
332 for (std::size_t channel = 0; channel < Channels; ++channel) {
334 if (transition_samples_ == 0)
335 load_requested(filters_[channel]);
337 begin_transition(channel, transition_samples_);
339 else if (transition_remaining_[channel] == transition_total_[channel])
340 load_requested(transition_filters_[channel]);
342 queue_transition(channel);
346 static double sanitize_query(
double frequency_hz,
double nyquist) {
347 if (!std::isfinite(frequency_hz))
return 0.0;
348 return std::clamp(frequency_hz, 0.0, nyquist);
351 double query_omega(
double frequency_hz)
const {
352 const double nyquist =
static_cast<double>(sample_rate_) * 0.5;
353 const double frequency = sanitize_query(frequency_hz, nyquist);
354 return std::acos(-1.0) * frequency / nyquist;
358 std::array<Parameters, band_count> parameters_ = defaults_;
359 std::array<Coefficients, band_count> requested_coefficients_{};
360 std::array<std::array<Filter, band_count>, Channels> filters_{};
361 std::array<std::array<Filter, band_count>, Channels> transition_filters_{};
362 std::array<std::size_t, Channels> transition_total_{};
363 std::array<std::size_t, Channels> transition_remaining_{};
364 std::array<bool, Channels> queued_transition_{};
365 std::array<std::size_t, Channels> queued_transition_samples_{};
366 std::size_t transition_samples_ = 0;