33#include <pulp/signal/detail/simd_lanes.hpp>
46namespace lanes = signal::detail::lanes;
60 std::array<EqBand, kNumEqBands> bands{};
61 bands[0] = {120.0, 0.0, 0.7};
62 for (
int i = 1;
i <= 8; ++
i) {
64 const double t =
static_cast<double>(
i - 1) / 7.0;
65 bands[
static_cast<std::size_t
>(
i)] = {250.0 * std::pow(32.0, t), 0.0, 1.0};
67 bands[9] = {9000.0, 0.0, 0.7};
73template <
typename SampleType =
float>
80 bands_[
static_cast<std::size_t
>(index)] =
band;
84 return bands_[
static_cast<std::size_t
>(std::clamp(index, 0,
kNumEqBands - 1))];
92 worst_case_boost_ = 1.0;
94 bool shelf_can_overshoot =
false;
97 const EqBand&
band = bands_[
static_cast<std::size_t
>(b)];
98 if (std::abs(
band.gain_db) < kFlatBandDb)
continue;
106 coeffs_[
static_cast<std::size_t
>(num_active_)] = designer.
coefficients();
108 if (
band.gain_db > 0.0)
109 worst_case_boost_ *= std::pow(10.0,
band.gain_db / 20.0);
111 shelf_can_overshoot =
true;
117 if (shelf_can_overshoot)
118 worst_case_boost_ = std::max(worst_case_boost_, measured_peak(
tank_rate));
123 for (
auto& s : state_) s = {};
137 SampleType
process(
int channel, SampleType x) {
138 for (
int k = 0; k < num_active_; ++k) {
139 const auto&
c = coeffs_[
static_cast<std::size_t
>(k)];
140 x =
static_cast<SampleType
>(
141 state_[state_index(k, channel)].process(
c,
static_cast<double>(x)));
149 static constexpr double kFlatBandDb = 0.01;
151 static constexpr double kMonotonicShelfQ = 0.7071067811865476;
157 double measured_peak(
double tank_rate)
const {
158 const std::span<const BiquadCoefficientsT<double>> sos(
159 coeffs_.data(),
static_cast<std::size_t
>(num_active_));
161 constexpr int kGrid = 512;
162 constexpr double kLowest = 1.0e-4;
163 const double pi = 3.14159265358979323846;
164 double best = std::max(mag(0.0), mag(pi));
165 double best_omega = 0.0;
166 double step = std::pow(pi / kLowest, 1.0 / (kGrid - 1));
167 double omega = kLowest;
168 for (
int i = 0;
i < kGrid; ++
i, omega *= step) {
169 const double m = mag(omega);
176 const double centre =
178 const double m = mag(centre);
184 if (best_omega > 0.0) {
185 double lo = std::max(best_omega / step, 0.0);
186 double hi = std::min(best_omega * step, pi);
187 constexpr double kRatio = 0.6180339887498949;
188 for (
int i = 0;
i < 40; ++
i) {
189 const double a = hi -
kRatio * (hi - lo);
190 const double c = lo +
kRatio * (hi - lo);
196 best = std::max(best, mag(0.5 * (lo + hi)));
201 static std::size_t state_index(
int active_slot,
int channel) {
202 return static_cast<std::size_t
>(active_slot) *
204 static_cast<std::size_t
>(channel);
207 std::array<EqBand, kNumEqBands> bands_{};
208 std::array<BiquadCoefficientsT<double>,
kNumEqBands> coeffs_{};
209 std::array<SanitizedBiquadState,
212 double worst_case_boost_ = 1.0;
232template <
typename SampleType =
float>
239 rng_[
i].reset(kSeedBase +
static_cast<std::uint32_t
>(
i) * kSeedStride);
240 target_hz_[
i] = base_hz_[
i];
241 current_hz_[
i] = base_hz_[
i];
243 current_db_[
i] = 0.0;
244 countdown_[
i] = interval_samples(
i);
256 depth_db_ = std::clamp(depth_db, 0.0,
kFluxMaxDb);
260 ?
static_cast<double>(
i) /
263 base_hz_[
i] = std::min(
278 countdown_[
i] -= samples_elapsed;
279 if (countdown_[
i] <= 0) {
281 target_hz_[
i] = base_hz_[
i] * std::pow(2.0, octaves);
282 target_db_[
i] = -rng_[
i].next_unit() * depth_db_;
283 countdown_[
i] = interval_samples(
i);
287 current_hz_[
i] += (target_hz_[
i] - current_hz_[
i]) * (1.0 - glide_);
288 current_db_[
i] += (target_db_[
i] - current_db_[
i]) * (1.0 - glide_);
299 const auto c =
static_cast<std::size_t
>(channel);
300 return {b0_[
c], b1_[
c], b2_[
c], a1_[
c], a2_[
c]};
305 SampleType
process(
int channel, SampleType x) {
306 const auto c =
static_cast<std::size_t
>(channel);
311 return static_cast<SampleType
>(out);
328 for (std::size_t p = 0; p < static_cast<std::size_t>(
kNumChannels / 2); ++p) {
329 l.b0[p] = lanes::load(&b0_[2 * p]);
330 l.b1[p] = lanes::load(&b1_[2 * p]);
331 l.b2[p] = lanes::load(&b2_[2 * p]);
332 l.a1[p] = lanes::load(&a1_[2 * p]);
333 l.a2[p] = lanes::load(&a2_[2 * p]);
334 l.s1[p] = lanes::load(&s1_[2 * p]);
335 l.s2[p] = lanes::load(&s2_[2 * p]);
340 for (std::size_t p = 0; p < static_cast<std::size_t>(
kNumChannels / 2); ++p) {
341 lanes::store(&s1_[2 * p], l.s1[p]);
342 lanes::store(&s2_[2 * p], l.s2[p]);
353 for (std::size_t j = 0; j < static_cast<std::size_t>(
kNumChannels / 4); ++j) {
354 const D2 in[2] = {lanes::widen_low(v[j]), lanes::widen_high(v[j])};
356 for (std::size_t h = 0; h < 2; ++h) {
357 const std::size_t p = 2 * j + h;
358 const D2 y = l.b0[p] * in[h] + l.s1[p];
359 l.s1[p] = l.b1[p] * in[h] - l.a1[p] * y + l.s2[p];
360 l.s2[p] = l.b2[p] * in[h] - l.a2[p] * y;
363 v[j] = lanes::narrow(out[0], out[1]);
371 if (!std::isfinite(s1_[
c]) || !std::isfinite(s2_[
c])) {
381 static constexpr std::uint32_t kSeedBase = 0x5EED1u;
382 static constexpr std::uint32_t kSeedStride = 0x85EBCA6Bu;
384 int interval_samples(
int channel) {
386 rng_[channel].next_unit() *
388 return std::max(1,
static_cast<int>(ms * 0.001 * tank_rate_));
394 static void sin_cos_0_pi(
double w,
double& s,
double&
c) {
395 const double y = w - 1.5707963267948966;
396 const double y2 = y * y;
397 double cy = 1.0 / 6402373705728000.0;
398 cy = cy * y2 - 1.0 / 20922789888000.0;
399 cy = cy * y2 + 1.0 / 87178291200.0;
400 cy = cy * y2 - 1.0 / 479001600.0;
401 cy = cy * y2 + 1.0 / 3628800.0;
402 cy = cy * y2 - 1.0 / 40320.0;
403 cy = cy * y2 + 1.0 / 720.0;
404 cy = cy * y2 - 1.0 / 24.0;
407 double sy = 1.0 / 355687428096000.0;
408 sy = sy * y2 - 1.0 / 1307674368000.0;
409 sy = sy * y2 + 1.0 / 6227020800.0;
410 sy = sy * y2 - 1.0 / 39916800.0;
411 sy = sy * y2 + 1.0 / 362880.0;
412 sy = sy * y2 - 1.0 / 5040.0;
413 sy = sy * y2 + 1.0 / 120.0;
414 sy = sy * y2 - 1.0 / 6.0;
415 sy = y + y * y2 * sy;
423 static double peaking_amplitude(
double gain_db) {
424 const double x = gain_db * (2.302585092994046 / 40.0);
425 double e = 1.0 / 3628800.0;
426 e =
e * x + 1.0 / 362880.0;
427 e =
e * x + 1.0 / 40320.0;
428 e =
e * x + 1.0 / 5040.0;
429 e =
e * x + 1.0 / 720.0;
430 e =
e * x + 1.0 / 120.0;
431 e =
e * x + 1.0 / 24.0;
432 e =
e * x + 1.0 / 6.0;
441 const double to_w = 6.283185307179586 / tank_rate_;
443 const double w0 = std::clamp(current_hz_[
i], 20.0, max_hz) * to_w;
446 sin_cos_0_pi(w0, sin_w0, cos_w0);
447 const double alpha = sin_w0 / (2.0 *
kFluxQ);
448 const double A = peaking_amplitude(std::clamp(current_db_[
i], -
kFluxMaxDb, 0.0));
449 const double inv_a0 = 1.0 / (1.0 + alpha / A);
450 b0_[
i] = (1.0 + alpha * A) * inv_a0;
451 b1_[
i] = -2.0 * cos_w0 * inv_a0;
452 b2_[
i] = (1.0 - alpha * A) * inv_a0;
454 a2_[
i] = (1.0 - alpha / A) * inv_a0;
458 std::array<XorShift32, kNumChannels> rng_{};
459 std::array<double, kNumChannels> b0_{};
460 std::array<double, kNumChannels> b1_{};
461 std::array<double, kNumChannels> b2_{};
462 std::array<double, kNumChannels> a1_{};
463 std::array<double, kNumChannels> a2_{};
464 std::array<double, kNumChannels> s1_{};
465 std::array<double, kNumChannels> s2_{};
466 std::array<double, kNumChannels> base_hz_{};
467 std::array<double, kNumChannels> target_hz_{};
468 std::array<double, kNumChannels> current_hz_{};
469 std::array<double, kNumChannels> target_db_{};
470 std::array<double, kNumChannels> current_db_{};
471 std::array<int, kNumChannels> countdown_{};
472 double tank_rate_ = 48000.0;
@ peaking
Definition biquad.hpp:58
@ high_shelf
Definition biquad.hpp:58
@ low_shelf
Definition biquad.hpp:58
void set_coefficients(const BiquadCoefficientsT< U > &c)
Definition biquad.hpp:69
Coefficients coefficients() const
Definition biquad.hpp:81
void store_lanes(const Lanes &l)
Definition loop_eq.hpp:339
SampleType process(int channel, SampleType x)
Definition loop_eq.hpp:305
void tick_control(int samples_elapsed)
Definition loop_eq.hpp:276
BiquadCoefficientsT< double > coefficients(int channel) const
Definition loop_eq.hpp:298
void configure(double tank_rate, double depth_db)
Definition loop_eq.hpp:254
void load_lanes(Lanes &l) const
Definition loop_eq.hpp:327
FluxBank()
Definition loop_eq.hpp:235
void heal()
Definition loop_eq.hpp:369
static void process_lanes(Lanes &l, lanes::F4(&v)[kNumChannels/4])
Definition loop_eq.hpp:351
double worst_case_boost() const
Definition loop_eq.hpp:295
void reset()
Definition loop_eq.hpp:237
void reset()
Definition loop_eq.hpp:122
double worst_case_boost() const
Definition loop_eq.hpp:130
void configure(double tank_rate)
Definition loop_eq.hpp:90
SampleType process(int channel, SampleType x)
Definition loop_eq.hpp:137
void set_band(int index, const EqBand &band)
Definition loop_eq.hpp:78
LoopEq()
Definition loop_eq.hpp:76
int active_bands() const
Definition loop_eq.hpp:133
const EqBand & band(int index) const
Definition loop_eq.hpp:83
Evaluate what a biquad (or a cascade of them) does to the spectrum.
constexpr state::ParamID kRatio
Definition forge_dynamics_catalog.hpp:83
constexpr int kControlRateSamples
Definition config.hpp:187
constexpr double kFluxDefaultDb
Definition config.hpp:120
T finite_or_zero(T value)
Definition sanitize.hpp:12
constexpr double kFluxBaseMinHz
Definition config.hpp:121
constexpr double kFluxQ
Definition config.hpp:130
constexpr int kNumEqBands
Definition config.hpp:20
constexpr double kFluxBaseMaxHz
Definition config.hpp:122
constexpr int kNumChannels
Definition config.hpp:18
@ tank_rate
Definition config.hpp:226
constexpr double kAaCutoffFraction
Definition config.hpp:170
constexpr double kFluxIntervalMinMs
Definition config.hpp:124
constexpr double kFluxSpreadOctaves
Definition config.hpp:123
constexpr double kFluxGlideMs
Definition config.hpp:126
constexpr double kFluxMaxDb
Definition config.hpp:115
constexpr double kFluxIntervalMaxMs
Definition config.hpp:125
std::array< EqBand, kNumEqBands > default_eq_bands()
Definition loop_eq.hpp:59
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
@ c
Definition spectrum_trace.hpp:35
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
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
Definition loop_eq.hpp:51
double freq_hz
Definition loop_eq.hpp:52
double q
Definition loop_eq.hpp:54
double gain_db
Definition loop_eq.hpp:53
Definition loop_eq.hpp:317
lanes::D2 s2[kNumChannels/2]
Definition loop_eq.hpp:324
lanes::D2 a2[kNumChannels/2]
Definition loop_eq.hpp:322
lanes::D2 a1[kNumChannels/2]
Definition loop_eq.hpp:321
lanes::D2 s1[kNumChannels/2]
Definition loop_eq.hpp:323
lanes::D2 b1[kNumChannels/2]
Definition loop_eq.hpp:319
lanes::D2 b0[kNumChannels/2]
Definition loop_eq.hpp:318
lanes::D2 b2[kNumChannels/2]
Definition loop_eq.hpp:320