66 void prepare(
double sample_rate,
int max_block_size) {
67 host_rate_ = sample_rate > 0.0 ? sample_rate : 48000.0;
68 max_block_ = std::max(max_block_size, 1);
69 bridge_.prepare(host_rate_, max_block_);
72 const int predelay_cap =
73 static_cast<int>(0.001 *
77 for (
auto& line : predelay_) line.prepare(predelay_cap);
78 for (
auto& cascade : pre_diffusion_)
86 smoothed_[p] = target_[p];
88 apply_tank_rate(rate_index());
89 classic_.prepare(host_rate_, max_block_);
97 const auto param =
static_cast<Param>(p);
98 if (param == Param::predelay || param == Param::width)
continue;
107 const auto index =
static_cast<std::size_t
>(p);
109 double v = std::clamp(value, spec.
min, spec.
max);
110 if (spec.
stepped) v = std::floor(v + 0.5);
112 classic_.set_parameter(p, v);
121 const bool rate_changed = apply_tank_rate(rate_index());
126 if (rate_changed) reset_rate_domain_state();
127 classic_.snap_parameters();
135 tank_.eq().set_band(index, band);
136 tank_.eq().configure(bridge_.tank_rate());
149 requested_tier_ =
tier;
152 return requested_tier_;
157 reset_rate_domain_state();
159 active_tier_ = requested_tier_;
169 return bridge_.interpolation_skew_samples();
172 double tank_rate()
const {
return bridge_.tank_rate(); }
187 SampleType* out_left, SampleType* out_right,
int n) {
188 if constexpr (std::is_same_v<SampleType, Engine>) {
189 process_engine(in_left, in_right, out_left, out_right, n);
195 for (
int i = 0;
i < m; ++
i) {
196 l[
i] =
static_cast<Engine>(in_left[offset +
i]);
197 r[
i] =
static_cast<Engine>(in_right[offset +
i]);
199 process_engine(l, r, l, r, m);
200 for (
int i = 0;
i < m; ++
i) {
201 out_left[offset +
i] =
static_cast<SampleType
>(l[
i]);
202 out_right[offset +
i] =
static_cast<SampleType
>(r[
i]);
210 Engine* out_right,
int n) {
212 if (requested_tier_ != active_tier_) {
213 active_tier_ = requested_tier_;
217 reset_rate_domain_state();
220 classic_.
process_block(in_left, in_right, out_left, out_right, n);
225 const int chunk = std::min(n - offset, max_block_);
226 process_chunk(in_left + offset, in_right + offset, out_left + offset,
227 out_right + offset, chunk);
234 int rate_index()
const {
235 const auto raw =
static_cast<int>(
236 target_[
static_cast<std::size_t
>(Param::tank_rate)] + 0.5);
240 void reset_rate_domain_state() {
242 for (
auto& line : predelay_)
line.reset();
243 for (
auto& cascade : pre_diffusion_) cascade.reset();
250 control_countdown_ = 0;
267 bool apply_tank_rate(
int index) {
268 if (index == active_rate_index_)
return false;
269 active_rate_index_ =
index;
271 bridge_.configure(rate);
272 tank_.configure(rate);
273 ducker_.configure(rate);
274 ensemble_.configure(rate);
275 for (
int c = 0;
c < kNumRails; ++
c) {
279 design_input_lp(rate);
282 makeup_gain_ = std::pow(
284 reset_rate_domain_state();
294 if (!input_lp_active_)
return;
295 BiquadT<double> designer;
305 void design_output_lp() {
306 BiquadT<double> designer;
308 std::min(output_lp_hz_, 0.45 * host_rate_),
309 0.7071067811865476, host_rate_);
313 void push_controls() {
314 controls_.decay = smoothed_[
static_cast<std::size_t
>(Param::decay)];
315 controls_.size = smoothed_[
static_cast<std::size_t
>(Param::size)];
316 controls_.damp_hi = smoothed_[
static_cast<std::size_t
>(Param::damp_hi)];
317 controls_.damp_lo = smoothed_[
static_cast<std::size_t
>(Param::damp_lo)];
318 controls_.diffusion = smoothed_[
static_cast<std::size_t
>(Param::diffusion)];
319 controls_.mod = smoothed_[
static_cast<std::size_t
>(Param::mod)];
320 controls_.shimmer = smoothed_[
static_cast<std::size_t
>(Param::shimmer)];
321 controls_.drive = smoothed_[
static_cast<std::size_t
>(Param::drive)];
322 controls_.bloom = smoothed_[
static_cast<std::size_t
>(Param::bloom)];
323 tank_.update_controls(controls_);
325 const double tank_rate = bridge_.tank_rate();
327 std::max(2.0, smoothed_[
static_cast<std::size_t
>(Param::predelay)] *
329 pre_g_ =
static_cast<Engine>(smoothed_[
static_cast<std::size_t
>(Param::diffusion)] *
331 width_ = smoothed_[
static_cast<std::size_t
>(Param::width)];
332 const double drive = smoothed_[
static_cast<std::size_t
>(Param::drive)];
333 if (drive != makeup_drive_) {
334 makeup_drive_ =
drive;
339 void tick_controls() {
340 const double coeff = smoothing_coeff_;
343 smoothed_[
p] = target_[
p];
345 smoothed_[
p] += (target_[
p] - smoothed_[
p]) * coeff;
351 Engine* out_right,
int n) {
353 apply_tank_rate(rate_index());
354 const int tank_n = bridge_.host_to_tank(in_left, in_right, n);
355 Engine* tl = bridge_.tank_input(0);
357 Engine* ol = bridge_.tank_output(0);
358 Engine* or_ = bridge_.tank_output(1);
364 while (done < tank_n) {
365 if (control_countdown_ <= 0) {
369 const int segment = std::min(tank_n - done, control_countdown_);
370 control_countdown_ -= segment;
371 process_segment(tl + done, tr + done, ol + done, or_ + done, segment);
375 if (control_countdown_ == 0)
379 bridge_.tank_to_host(tank_n, out_left, out_right, n);
383 const auto makeup = fdn::lanes::splat(makeup_gain_);
385 auto output_state = output_lp_;
386 for (
int i = 0;
i <
n; ++
i) {
387 const auto y = output_state.process(
388 &output_lp, fdn::lanes::make(
static_cast<double>(out_left[
i]),
389 static_cast<double>(out_right[
i])));
390 const auto scaled =
y * makeup;
391 out_left[
i] =
static_cast<Engine>(fdn::lanes::lane(scaled, 0));
392 out_right[
i] =
static_cast<Engine>(fdn::lanes::lane(scaled, 1));
394 output_lp_ = output_state;
407 predelay_[0].push_block(tl, n);
408 predelay_[1].push_block(tr, n);
409 predelay_[0].read_hermite_span(predelay_samples_, l, n, n - 1);
410 predelay_[1].read_hermite_span(predelay_samples_, r, n, n - 1);
411 duck_gain_ = ducker_.process_block(l, r, n);
412 if (input_lp_active_) {
414 auto input_state = input_lp_;
415 for (
int k = 0;
k <
n; ++
k) {
417 input_state.process(&input_lp, fdn::lanes::make(
static_cast<double>(l[k]),
418 static_cast<double>(r[k])));
419 l[
k] =
static_cast<Engine>(fdn::lanes::lane(y, 0));
420 r[
k] =
static_cast<Engine>(fdn::lanes::lane(y, 1));
422 input_lp_ = input_state;
427 pre_diffusion_[0].process_block(l, n, pre_g_);
428 pre_diffusion_[1].process_block(r, n,
static_cast<Engine>(-pre_g_));
432 tank_.process_block(l, r, wet_l, wet_r, n);
434 const auto makeup =
static_cast<Engine>(drive_makeup_gain_);
436 for (
int k = 0;
k <
n; ++
k) {
437 const Engine dl = wet_l[
k] * makeup;
438 const Engine dr = wet_r[
k] * makeup;
441 wet_l[
k] = mid + side;
442 wet_r[
k] = mid - side;
444 ensemble_.process_block(wet_l, wet_r, n);
445 for (
int k = 0;
k <
n; ++
k) {
451 static constexpr int kNumRails = 2;
453 fdn::MultirateBridge<Engine> bridge_{};
454 std::array<fdn::FracDelayT<Engine>, kNumRails> predelay_{};
455 std::array<fdn::AllpassCascade<Engine>, kNumRails> pre_diffusion_{};
456 fdn::FdnTank<Engine> tank_{};
457 fdn::TransientDucker ducker_{};
458 fdn::EnsembleChorus<Engine> ensemble_{};
461 fdn::StereoBiquadState<1> input_lp_{};
462 fdn::StereoBiquadState<1> output_lp_{};
463 fdn::StereoSection input_lp_coefficients_{};
464 fdn::StereoSection output_lp_coefficients_{};
466 std::array<double, fdn::kNumParams> target_{};
467 std::array<double, fdn::kNumParams> smoothed_{};
468 fdn::TankControls controls_{};
470 double host_rate_ = 48000.0;
471 double predelay_samples_ = 2.0;
473 double makeup_gain_ = 1.0;
474 double drive_makeup_gain_ = 1.0;
475 double makeup_drive_ = 0.0;
476 double smoothing_coeff_ = 1.0;
477 double duck_gain_ = 1.0;
478 double output_lp_hz_ = 14000.0;
480 int max_block_ = 512;
481 int control_countdown_ = 0;
482 int active_rate_index_ = -1;
483 ClassicReverbT<Engine> classic_{};
486 bool input_lp_active_ =
false;