120 static_assert(std::is_same_v<SampleType, float>,
121 "the tank's block kernels are single precision");
133 for (std::size_t
i = 0;
i < lines_.size(); ++
i)
135 for (std::size_t
i = 0;
i < loop_diffusion_.size(); ++
i)
138 shimmer_.prepare(max_tank_rate);
140 flutter_lfo_[
i].reset(
static_cast<double>(
i) /
149 flux_.configure(
tank_rate, flux_depth_db_);
157 ?
static_cast<double>(
i) /
160 flutter_lfo_[
i].set_rate(
166 derive_lengths(controls_.size);
171 cached_.damp_hi = -1.0;
172 cached_.damp_lo = -1.0;
177 for (
auto& line : lines_) line.reset();
178 for (
auto& cascade : loop_diffusion_) cascade.reset();
186 current_delay_[
i] = target_delay_[
i];
187 flutter_lfo_[
i].reset(
static_cast<double>(
i) /
193 recompute_derived_state(controls_,
true);
202 tap_scale_ =
static_cast<SampleType
>(
203 1.0 / std::sqrt(
static_cast<double>(active_n_)));
204 in_gain_ =
static_cast<SampleType
>(
205 1.0 / std::sqrt(
static_cast<double>(active_n_) * 0.5));
207 active_[j] = lanes::make(
208 4 * j < active_n_ ? 1.0f : 0.0f, 4 * j + 1 < active_n_ ? 1.0f : 0.0f,
209 4 * j + 2 < active_n_ ? 1.0f : 0.0f, 4 * j + 3 < active_n_ ? 1.0f : 0.0f);
219 flux_depth_db_ = std::clamp(db, 0.0,
kFluxMaxDb);
220 flux_.configure(tank_rate_, flux_depth_db_);
228 return gain_[
static_cast<std::size_t
>(channel)];
238 return target_delay_[
static_cast<std::size_t
>(
246 if (
c.size != cached_.size) derive_lengths(
c.size);
248 current_delay_[
i] += (target_delay_[
i] - current_delay_[
i]) * (1.0 - glide_);
250 recompute_derived_state(
c,
false);
252 modulation_.step_walks();
259 void process(SampleType in_l, SampleType in_r, SampleType& out_l, SampleType& out_r) {
266 void process_block(
const SampleType* in_l,
const SampleType* in_r, SampleType* out_l,
267 SampleType* out_r,
int n) {
272 process_chunk(in_l + offset, in_r + offset, out_l + offset, out_r + offset, chunk);
274 heal_phase_ += chunk;
283 using Lane = std::array<SampleType, kNumChannels>;
286 void process_chunk(
const SampleType* in_l,
const SampleType* in_r, SampleType* out_l,
287 SampleType* out_r,
int n) {
289 alignas(64) std::array<Lane, kMaxBlockSamples> x;
296 diffuse_lines(span, n);
302 for (; k0 + 4 <= n; k0 += 4) {
304 lanes::F4
a = lanes::load(&span[4 * j][k0]);
305 lanes::F4 b = lanes::load(&span[4 * j + 1][k0]);
306 lanes::F4
c = lanes::load(&span[4 * j + 2][k0]);
307 lanes::F4 d = lanes::load(&span[4 * j + 3][k0]);
308 lanes::transpose(
a, b,
c, d);
309 const auto col = 4 *
static_cast<std::size_t
>(j);
310 lanes::store(&x[
static_cast<std::size_t
>(k0)][col],
a);
311 lanes::store(&x[
static_cast<std::size_t
>(k0) + 1][col], b);
312 lanes::store(&x[
static_cast<std::size_t
>(k0) + 2][col],
c);
313 lanes::store(&x[
static_cast<std::size_t
>(k0) + 3][col], d);
317 for (
int k = k0; k < n; ++k) {
318 for (
int i = 0;
i < active_n_; ++
i)
319 x[
static_cast<std::size_t
>(k)][
static_cast<std::size_t
>(
i)] = span[
i][k];
321 x[
static_cast<std::size_t
>(k)][
static_cast<std::size_t
>(
i)] = SampleType{0};
327 F4 lp_state[kVectors];
328 F4 hp_state[kVectors];
329 for (
int j = 0;
j < kVectors; ++
j) {
330 lp_state[
j] = lanes::load(&lp_state_[4 *
static_cast<std::size_t
>(j)]);
331 hp_state[
j] = lanes::load(&hp_state_[4 *
static_cast<std::size_t
>(j)]);
333 const F4 ceiling = lanes::splat(
static_cast<float>(
kSanityCeil));
334 const F4 floor = lanes::splat(-
static_cast<float>(
kSanityCeil));
335 const bool shimmer = shimmer_weight_ > 0.0;
336 const bool eq = eq_.active_bands() > 0;
337 typename FluxBank<SampleType>::Lanes flux;
338 flux_.load_lanes(flux);
342 const std::size_t
write = lines_[0].write_index();
345 ring[
i] = lines_[
i].data() +
write;
346 for (
int k = 0;
k <
n; ++
k) {
347 float*
row =
x[
static_cast<std::size_t
>(
k)].data();
349 for (
int j = 0;
j < kVectors; ++
j) {
357 const F4 in = lanes::clamp(lanes::load(row + 4 * j), floor, ceiling);
358 const F4 lp = in + (lp_state[
j] - in) * lp_coeff_[j];
360 const F4
y = lanes::select(lp_on_[j], lp, in);
361 const F4 hp =
y + (hp_state[
j] -
y) * hp_coeff_[j];
363 v[
j] = lanes::select(hp_on_[j], y - hp, y);
366 for (
int j = 0;
j < kVectors; ++
j)
367 lanes::store(row + 4 * j, v[j]);
368 for (
int i = 0;
i < active_n_; ++
i)
369 row[
i] = eq_.process(
i, row[
i]);
370 for (
int j = 0;
j < kVectors; ++
j)
371 v[j] = lanes::load(row + 4 * j);
374 for (
int j = 0;
j < kVectors; ++
j)
378 const F4 taps = (
v[0] +
v[1]) + (v[2] + v[3]);
379 out_l[
k] = (lanes::lane(taps, 0) + lanes::lane(taps, 2)) * tap_scale_;
380 out_r[
k] = (lanes::lane(taps, 1) + lanes::lane(taps, 3)) * tap_scale_;
384 for (
int j = 0;
j < kVectors; ++
j)
385 lanes::store(row + 4 * j, v[j]);
386 for (
int i = 0;
i < active_n_; ++
i)
387 row[
i] +=
static_cast<SampleType
>(
388 shimmer_weight_ *
static_cast<double>(shimmer_.process(
i, row[
i])));
389 for (
int j = 0;
j < kVectors; ++
j)
390 v[j] = lanes::load(row + 4 * j);
396 const F4
drive = lanes::splat(
static_cast<float>(drive_));
397 for (
int j = 0;
j < kVectors; ++
j)
398 v[j] +=
drive * (lipschitz_tanh_lanes(v[j]) - v[j]);
404 for (
int j = 0;
j < kVectors; ++
j)
405 v[j] *= matrix_gain_[j];
406 const F4 a0 =
v[0] +
v[1];
407 const F4 a1 =
v[0] -
v[1];
408 const F4 a2 =
v[2] +
v[3];
409 const F4 a3 =
v[2] -
v[3];
410 v[0] = lanes::hadamard4(a0 + a2);
411 v[1] = lanes::hadamard4(a1 + a3);
412 v[2] = lanes::hadamard4(a0 - a2);
413 v[3] = lanes::hadamard4(a1 - a3);
420 const F4
feed = lanes::make(in_gain_ * in_l[k], in_gain_ * in_r[k], in_gain_ * in_l[k],
423 for (
int j = 0;
j < kVectors; ++
j) {
425 clean(lanes::clamp(v[j], -matrix_ceiling, matrix_ceiling) +
feed,
kSanityCeil);
426 for (
int lane = 0; lane < 4; ++lane)
427 ring[4 * j + lane][k] = lanes::lane(w, lane);
430 flux_.store_lanes(flux);
431 for (
int j = 0;
j < kVectors; ++
j) {
432 lanes::store(&lp_state_[4 *
static_cast<std::size_t
>(j)], lp_state[j]);
433 lanes::store(&hp_state_[4 *
static_cast<std::size_t
>(j)], hp_state[j]);
439 for (
auto& line : lines_)
453 const std::size_t
write = lines_[0].write_index();
456 ring[
i] = lines_[
i].taps_at(0);
458 for (
int p = 0;
p < kPairs; ++
p)
459 length[p] = lanes::load(¤t_delay_[2 *
static_cast<std::size_t
>(p)]);
460 const D2 depth = lanes::splat(mod_depth_);
463 const D2 one = lanes::splat(1.0);
464 const D2 lowest = lanes::splat(2.0);
465 const D2 highest = lanes::splat(
static_cast<double>(lines_[0].max_delay()));
466 const I2 mask = lanes::splat_i(
static_cast<std::int64_t
>(lines_[0].mask()));
467 const F4
half = lanes::splat(0.5f);
468 const F4 two_and_half = lanes::splat(2.5f);
469 const F4 two = lanes::splat(2.0f);
470 const F4 one_and_half = lanes::splat(1.5f);
471 const F4 onef = lanes::splat(1.0f);
478 const auto flush = [&](
int last) {
482 lanes::transpose(
a, b,
c, d);
483 const int first = last - 3;
484 lanes::store(&span[4 * j][
first],
a);
485 lanes::store(&span[4 * j + 1][
first], b);
486 lanes::store(&span[4 * j + 2][
first],
c);
487 lanes::store(&span[4 * j + 3][
first], d);
490 for (
int h = 0;
h < held; ++
h)
492 for (
int lane = 0; lane < 4; ++lane)
493 span[4 * j + lane][last - held + 1 + h] =
494 lanes::lane(pending[h][j], lane);
498 for (
int k = 0;
k <
n; ++
k) {
502 lanes::splat_i(
static_cast<std::int64_t
>(write +
static_cast<std::size_t
>(k)) - 2);
507 for (
int h = 0;
h < 2; ++
h) {
508 const int p = 2 *
j +
h;
509 const D2
d = lanes::clamp(length[p] * (one + depth * lanes::load(m + 2 * p)),
511 const I2
whole = lanes::to_int(d);
512 f[
h] =
d - lanes::to_double(whole);
513 const I2
at = (origin -
whole) & mask;
514 start[2 *
p] = lanes::lane(at, 0);
515 start[2 *
p + 1] = lanes::lane(at, 1);
517 frac[
j] = lanes::narrow(f[0], f[1]);
520 F4 ym1 = lanes::load(ring[4 * j] + start[4 * j]);
521 F4 y0 = lanes::load(ring[4 * j + 1] + start[4 * j + 1]);
522 F4 y1 = lanes::load(ring[4 * j + 2] + start[4 * j + 2]);
523 F4 y2 = lanes::load(ring[4 * j + 3] + start[4 * j + 3]);
524 lanes::transpose(ym1, y0, y1, y2);
525 const F4
t = onef - frac[
j];
526 const F4
c1 =
half * (y1 - ym1);
527 const F4
c2 = ym1 - two_and_half * y0 + two * y1 -
half * y2;
528 const F4
c3 =
half * (y2 - ym1) + one_and_half * (y0 - y1);
540 if (controls_.flutter) {
542 for (
int i = 0;
i < active_n_; ++
i) {
543 const auto ui =
static_cast<std::size_t
>(
i);
544 for (
int k = 0;
k <
n; ++
k)
545 lfo[k] = flutter_lfo_[ui].tick();
546 const SampleType
g = (
i & 1) ? -loop_g_ : loop_g_;
547 loop_diffusion_[ui].process_block_fluttered(span[
i], n, g, lfo,
kFlutterDepth);
549 advance_inactive_cascades(n);
557 static_assert(kStages == 4,
"run_spans4 is the four-stage kernel");
558 if (n > loop_diffusion_[0].min_delay()) {
559 for (
int i = 0;
i < active_n_; ++
i)
560 loop_diffusion_[
static_cast<std::size_t
>(
i)].process_block(
561 span[
i], n, (
i & 1) ? -loop_g_ : loop_g_);
562 advance_inactive_cascades(n);
565 std::size_t
write[kStages];
566 std::size_t mask[kStages];
567 bool upkeep[kStages];
568 for (
int s = 0;
s < kStages; ++
s) {
569 auto&
line = loop_diffusion_[0].stage(s).line();
571 mask[
s] =
line.mask();
573 write[
s] +
static_cast<std::size_t
>(
n) >
line.size();
575 for (
int i = 0;
i < active_n_; ++
i) {
576 auto& cascade = loop_diffusion_[
static_cast<std::size_t
>(
i)];
577 SampleType* delayed[kStages];
578 SampleType* stored[kStages];
579 for (
int s = 0;
s < kStages; ++
s) {
580 auto& stage = cascade.stage(s);
581 SampleType* base = stage.line().data();
583 base + ((
write[
s] -
static_cast<std::size_t
>(stage.delay())) & mask[s]);
590 for (
int s = 0;
s < kStages; ++
s) {
592 auto&
line = loop_diffusion_[
static_cast<std::size_t
>(
i)].stage(s).line();
603 void advance_inactive_cascades(
int n) {
605 for (
int s = 0; s < loop_diffusion_[static_cast<std::size_t>(
i)].num_stages(); ++
s)
606 loop_diffusion_[
static_cast<std::size_t
>(
i)].stage(s).line().advance(n);
615 lp_state_[
i] = std::isfinite(lp_state_[
i]) ?
snap_to_zero(lp_state_[
i]) : 0.0f;
616 hp_state_[
i] = std::isfinite(hp_state_[
i]) ?
snap_to_zero(hp_state_[
i]) : 0.0f;
623 static lanes::F4 clean(lanes::F4 x,
double ceiling) {
624 const auto c = lanes::splat(
static_cast<float>(ceiling));
625 const lanes::F4
bounded = lanes::clamp(x, -
c,
c);
626 const lanes::F4 magnitude = lanes::abs(x);
627 const lanes::M4 keep = lanes::both(
628 lanes::less_equal(magnitude, lanes::splat(std::numeric_limits<float>::max())),
630 return lanes::select(keep,
bounded, lanes::splat(0.0f));
633 static lanes::F4 lipschitz_tanh_lanes(lanes::F4 v) {
634 const lanes::F4
x = lanes::clamp(v, lanes::splat(-3.0f), lanes::splat(3.0f));
635 const lanes::F4
x2 =
x *
x;
636 return x * (lanes::splat(27.0f) +
x2) / (lanes::splat(27.0f) + lanes::splat(9.0f) * x2);
641 void derive_safe_block() {
643 for (
int i = 0;
i < active_n_; ++
i) {
644 const auto ui =
static_cast<std::size_t
>(
i);
647 const double shortest = std::max(2.0, current_delay_[ui] * (1.0 - mod_depth_));
648 bound = std::min(bound,
static_cast<int>(shortest) - 1);
649 int stage = loop_diffusion_[ui].min_delay();
650 if (controls_.flutter)
651 stage =
static_cast<int>(
static_cast<double>(stage) * (1.0 -
kFlutterDepth)) - 1;
652 bound = std::min(bound, stage);
654 safe_block_ = std::max(bound, 1);
657 void recompute_derived_state(
const TankControls&
c,
bool force_damping) {
660 current_delay_[
i] + loop_diffusion_[
static_cast<std::size_t
>(
i)].total_delay();
668 eq_.worst_case_boost() * flux_.worst_case_boost() * (1.0 + shimmer_weight_);
670 const double ceiling =
kGainCeil / worst_case_boost_;
672 const double t60 = std::max(
c.decay, 0.01);
675 if (!force_damping && ceiling == gain_key_.ceiling && authority == gain_key_.authority &&
676 t60 == gain_key_.t60 && tank_rate_ == gain_key_.tank_rate &&
677 loop_delay_ == gain_key_.loop_delay) {
678 finish_derived_state(
c, force_damping);
681 gain_key_ = {ceiling, authority, t60, tank_rate_, loop_delay_};
690 std::pow(10.0, -3.0 * loop_delay_[
i] / (t60 * tank_rate_));
691 double g = std::min(jot, ceiling);
692 g += (ceiling -
g) * authority;
696 const auto u =
static_cast<std::size_t
>(4 *
j);
698 matrix_gain_[
j] = lanes::make(
699 static_cast<float>(gain_[
u] * 0.25),
static_cast<float>(gain_[
u + 1] * 0.25),
700 static_cast<float>(gain_[
u + 2] * 0.25),
static_cast<float>(gain_[
u + 3] * 0.25));
703 finish_derived_state(
c, force_damping);
706 void finish_derived_state(
const TankControls&
c,
bool force_damping) {
707 if (force_damping ||
c.damp_hi != cached_.damp_hi ||
708 c.damp_lo != cached_.damp_lo ||
c.size != cached_.size)
709 derive_damping(
c.damp_hi,
c.damp_lo);
720 static SampleType sanitize(SampleType x) {
721 if (!std::isfinite(
static_cast<double>(x)))
return SampleType{0};
730 void derive_lengths(
double size) {
736 const double frac =
static_cast<double>(
i) /
738 const double base = dmin + (dmax - dmin) * std::pow(frac,
kDelayExponent);
745 std::round(base * tank_rate_) +
747 const double cap =
static_cast<double>(lines_[
static_cast<std::size_t
>(
i)]
749 target_delay_[
i] = std::clamp(target_delay_[
i], 4.0, cap);
762 const double lp_med = std::exp(-6.283185307179586 * lp_hz / tank_rate_);
765 std::clamp(loop_delay_[
i] / std::max(median_delay_, 1.0), 0.05, 20.0);
770 lp_a_[
i] = lp_med <= 0.0
772 : lerp_uniform(lp_med, 1.0 - std::pow(1.0 - lp_med,
ratio));
779 const double hp_hz_i = hp_hz *
ratio;
780 hp_a_[
i] = lerp_uniform(
781 std::exp(-6.283185307179586 * hp_hz / tank_rate_),
782 std::exp(-6.283185307179586 * hp_hz_i / tank_rate_));
785 const auto u =
static_cast<std::size_t
>(4 *
j);
787 lanes::make(
static_cast<float>(lp_a_[
u]),
static_cast<float>(lp_a_[
u + 1]),
788 static_cast<float>(lp_a_[
u + 2]),
static_cast<float>(lp_a_[
u + 3]));
790 lanes::make(
static_cast<float>(hp_a_[
u]),
static_cast<float>(hp_a_[
u + 1]),
791 static_cast<float>(hp_a_[
u + 2]),
static_cast<float>(hp_a_[
u + 3]));
792 lp_on_[
j] = lanes::mask_of(lp_a_[
u] > 0.0, lp_a_[
u + 1] > 0.0, lp_a_[
u + 2] > 0.0,
794 hp_on_[
j] = lanes::mask_of(hp_a_[
u] > 0.0, hp_a_[
u + 1] > 0.0, hp_a_[
u + 2] > 0.0,
802 static double lerp_uniform(
double uniform,
double proportional) {
806 std::array<FracDelayT<SampleType>,
kNumChannels> lines_{};
807 std::array<AllpassCascade<SampleType>,
kNumChannels> loop_diffusion_{};
808 std::array<PhaseOsc, kNumChannels> flutter_lfo_{};
809 DelayModulationBank modulation_{};
810 LoopEq<SampleType> eq_{};
811 FluxBank<SampleType> flux_{};
812 ShimmerBank<SampleType> shimmer_{};
814 std::array<double, kNumChannels> target_delay_{};
815 std::array<double, kNumChannels> current_delay_{};
816 std::array<double, kNumChannels> loop_delay_{};
817 std::array<double, kNumChannels> gain_{};
818 std::array<double, kNumChannels> lp_a_{};
819 std::array<double, kNumChannels> hp_a_{};
820 std::array<float, kNumChannels> lp_state_{};
821 std::array<float, kNumChannels> hp_state_{};
828 lanes::F4 active_[
kNumChannels / 4] = {lanes::splat(1.0f), lanes::splat(1.0f),
829 lanes::splat(1.0f), lanes::splat(1.0f)};
831 TankControls controls_{};
832 TankControls cached_{};
833 double tank_rate_ = 20000.0;
835 double median_delay_ = 1.0;
837 double mod_depth_ = 0.0;
839 double shimmer_weight_ = 0.0;
840 double worst_case_boost_ = 1.0;
841 SampleType loop_g_ = 0;
842 SampleType tap_scale_ =
static_cast<SampleType
>(0.25);
843 SampleType in_gain_ =
static_cast<SampleType
>(0.35355339059327373);
850 double ceiling = -1.0;
851 double authority = -1.0;
853 double tank_rate = -1.0;
854 std::array<double, kNumChannels> loop_delay{};