89 static_assert(std::is_same_v<SampleType, float>,
90 "the classic reverb's block kernels are single precision");
96 void prepare(
double sample_rate,
int max_block_size) {
97 rate_ = sample_rate > 0.0 ? sample_rate : 48000.0;
99 const double longest_ms =
101 const int comb_cap =
static_cast<int>(longest_ms * 0.001 * rate_) +
103 std::size_t size = 8;
104 while (size <
static_cast<std::size_t
>(comb_cap))
112 comb_mask_ = size - 1;
113 for (
auto& cascade : allpass_)
117 const int predelay_cap =
119 0.001 *
fdn::kParamSpecs[
static_cast<std::size_t
>(Param::predelay)].max * rate_) +
121 for (
auto& line : predelay_)
122 line.prepare(predelay_cap);
126 target_[
static_cast<std::size_t
>(p)] =
135 const auto param =
static_cast<Param>(p);
136 if (param == Param::predelay || param == Param::width)
145 double v = std::clamp(value, spec.
min, spec.
max);
147 v = std::floor(v + 0.5);
148 target_[
static_cast<std::size_t
>(p)] = v;
153 return target_[
static_cast<std::size_t
>(p)];
159 return std::all_of(storage_.begin(), storage_.end(),
160 [](SampleType v) { return v == SampleType{0}; });
166 smoothing_stages_.fill(target_);
175 std::fill(storage_.begin(), storage_.end(), SampleType{0});
177 lp_.fill(SampleType{0});
178 for (
auto& cascade : allpass_)
180 for (
auto& line : predelay_)
182 control_countdown_ = 0;
191 return length_[
static_cast<std::size_t
>(comb)];
194 return gain_[
static_cast<std::size_t
>(comb)];
198 void process_block(
const SampleType* in_l,
const SampleType* in_r, SampleType* out_l,
199 SampleType* out_r,
int n) {
202 if (control_countdown_ <= 0) {
206 const int piece = std::min(n - done, control_countdown_);
207 process_piece(in_l + done, in_r + done, out_l + done, out_r + done, piece,
209 control_countdown_ -= piece;
210 if (control_countdown_ == 0)
224 void tick_controls() {
231 bool changed =
false;
233 for (std::size_t p = 0; p < smoothed_.size(); ++p) {
234 const double before = smoothed_[p];
236 for (
auto& stage : smoothing_stages_)
237 stage[p] = target_[p];
238 smoothed_[p] = target_[p];
240 double in = target_[p];
241 for (
auto& stage : smoothing_stages_) {
242 const double was = stage[p];
243 stage[p] += (in - stage[p]) * smoothing_coeff_;
244 moving = moving || stage[p] != was;
247 smoothed_[
p] += (in - smoothed_[
p]) * smoothing_coeff_;
259 const auto from_gain = gain_f_;
260 const auto from_damp = damp_f_;
261 const SampleType from_width = width_;
262 if (smoothed(Param::size) != lengths_size_)
267 gain_from_[
i] = from_gain[
i];
268 gain_step_[
i] = (gain_f_[
i] - from_gain[
i]) * inv;
269 damp_from_[
i] = from_damp[
i];
270 damp_step_[
i] = (damp_f_[
i] - from_damp[
i]) * inv;
272 width_from_ = from_width;
273 width_step_ = (width_ - from_width) * inv;
276 double smoothed(Param p)
const {
277 return smoothed_[
static_cast<std::size_t
>(
p)];
281 void derive_lengths() {
282 lengths_size_ = smoothed(Param::size);
287 for (
int rail = 0; rail < 2; ++rail) {
292 const int length =
static_cast<int>(std::lround(base + rail * spread)) +
297 for (
int rail = 0; rail < 2; ++rail) {
298 allpass_[
static_cast<std::size_t
>(rail)].configure(
300 rail == 0 ? 0 :
static_cast<int>(std::lround(spread)));
309 const double t60 = std::max(smoothed(Param::decay), 0.01);
312 std::pow(1.0 - std::clamp(smoothed(Param::damp_hi), 0.0, 1.0),
314 const double pole = std::exp(-6.283185307179586 * lp_hz / rate_);
317 median +=
static_cast<double>(length_[
static_cast<std::size_t
>(
i)]);
320 const double length =
static_cast<double>(length_[
i]);
321 gain_[
i] = std::pow(10.0, -3.0 * length / (t60 * rate_));
322 const double ratio = std::clamp(length / median, 0.05, 20.0);
323 const double damp = 1.0 - std::pow(1.0 - pole,
ratio);
324 gain_f_[
i] =
static_cast<SampleType
>(gain_[
i]);
325 damp_f_[
i] =
static_cast<SampleType
>(damp);
328 predelay_samples_ = std::max(2.0, smoothed(Param::predelay) * 0.001 * rate_);
329 width_ =
static_cast<SampleType
>(smoothed(Param::width));
333 void process_piece(
const SampleType* in_l,
const SampleType* in_r, SampleType* out_l,
334 SampleType* out_r,
int n,
int phase) {
341 for (
int k = 0;
k <
n; ++
k) {
345 for (
int rail = 0; rail < 2; ++rail) {
346 predelay_[
static_cast<std::size_t
>(rail)].push_block(
feed[rail], n);
347 predelay_[
static_cast<std::size_t
>(rail)].read_hermite_span(predelay_samples_,
348 feed[rail], n, n - 1);
360 allpass_[0].process_block(
left, n, allpass_g_);
361 allpass_[1].process_block(
right, n, allpass_g_);
364 const auto mix = [&](
int k, SampleType
width) {
365 const SampleType mid = SampleType{0.5} * (
left[
k] +
right[
k]);
367 out_l[
k] = output_gain * (mid + side);
368 out_r[
k] = output_gain * (mid - side);
371 for (
int k = 0;
k <
n; ++
k)
372 mix(k, width_from_ + width_step_ *
static_cast<SampleType
>(phase + k + 1));
374 for (
int k = 0;
k <
n; ++
k)
379 std::size_t read_index(std::size_t comb)
const {
380 return (write_ -
static_cast<std::size_t
>(length_[comb])) & comb_mask_;
389 void run_combs(
const SampleType* in_l,
const SampleType* in_r, SampleType*
left,
390 SampleType*
right,
int n,
int phase) {
391 namespace lanes = detail::lanes;
393 constexpr std::size_t kRow =
kCombs;
396 read[
i] = storage_.data() + read_index(
i) * kRow +
i;
397 SampleType*
write = storage_.data() + write_ * kRow;
399 const F4 zero = lanes::splat(SampleType{0});
400 F4 lp[4], damp[4], gain[4];
401 for (
int j = 0;
j < 4; ++
j) {
402 lp[
j] = lanes::load(&lp_[4 *
static_cast<std::size_t
>(j)]);
403 damp[
j] = lanes::load(&damp_f_[4 *
static_cast<std::size_t
>(j)]);
404 gain[
j] = lanes::load(&gain_f_[4 *
static_cast<std::size_t
>(j)]);
406 for (
int k = 0;
k <
n; ++
k) {
407 const std::size_t
at =
static_cast<std::size_t
>(
k) * kRow;
409 for (
int j = 0;
j < 4; ++
j)
410 delayed[j] = lanes::make(read[4 * j][at], read[4 * j + 1][at], read[4 * j + 2][at],
411 read[4 * j + 3][at]);
412 const F4
feed[2] = {lanes::splat(in_l[k]), lanes::splat(in_r[k])};
413 if constexpr (Ramp) {
414 const F4 step = lanes::splat(
static_cast<SampleType
>(phase + k + 1));
415 for (
int j = 0;
j < 4; ++
j) {
416 const auto u = 4 *
static_cast<std::size_t
>(
j);
417 gain[
j] = lanes::load(&gain_from_[
u]) + lanes::load(&gain_step_[
u]) * step;
418 damp[
j] = lanes::load(&damp_from_[
u]) + lanes::load(&damp_step_[
u]) * step;
421 for (
int j = 0;
j < 4; ++
j) {
422 lp[
j] = delayed[
j] + (lp[
j] - delayed[
j]) * damp[j];
426 const F4 stored =
feed[
j / 2] + gain[
j] * lp[
j];
428 write + at + 4 *
static_cast<std::size_t
>(j),
429 lanes::select(lanes::less(lanes::abs(stored), threshold), zero, stored));
431 const F4
l = delayed[0] + delayed[1];
432 const F4
r = delayed[2] + delayed[3];
434 (lanes::lane(l, 0) + lanes::lane(l, 1)) + (lanes::lane(l, 2) + lanes::lane(l, 3));
436 (lanes::lane(r, 0) + lanes::lane(r, 1)) + (lanes::lane(r, 2) + lanes::lane(r, 3));
438 for (
int j = 0;
j < 4; ++
j)
439 lanes::store(&lp_[4 *
static_cast<std::size_t
>(j)], lp[j]);
445 void commit_combs(
int n) {
446 constexpr std::size_t kRow =
kCombs;
447 const std::size_t
size = comb_mask_ + 1;
448 const std::size_t
end = write_ +
static_cast<std::size_t
>(
n);
449 SampleType* rows = storage_.data();
451 std::copy(rows + size * kRow, rows + end * kRow, rows);
453 if (write_ < mirror || end > size)
454 std::copy(rows, rows + mirror * kRow, rows + size * kRow);
455 write_ =
end & comb_mask_;
466 std::vector<SampleType> storage_{};
467 std::array<int, kCombs> length_{};
468 std::array<double, kCombs> gain_{};
469 std::array<SampleType, kCombs> gain_f_{};
470 std::array<SampleType, kCombs> damp_f_{};
471 std::array<SampleType, kCombs> lp_{};
472 std::array<fdn::AllpassCascade<SampleType>, 2> allpass_{};
473 std::array<fdn::FracDelayT<SampleType>, 2> predelay_{};
474 std::array<double, fdn::kNumParams> target_{};
475 std::array<std::array<double, fdn::kNumParams>, 2> smoothing_stages_{};
476 std::array<double, fdn::kNumParams> smoothed_{};
477 std::size_t comb_mask_ = 0;
479 double lengths_size_ = -1.0;
480 std::size_t write_ = 0;
481 double rate_ = 48000.0;
482 double smoothing_coeff_ = 1.0;
483 double predelay_samples_ = 2.0;
484 SampleType allpass_g_ = 0;
485 SampleType width_ = 1;
487 std::array<SampleType, kCombs> gain_from_{};
488 std::array<SampleType, kCombs> gain_step_{};
489 std::array<SampleType, kCombs> damp_from_{};
490 std::array<SampleType, kCombs> damp_step_{};
491 SampleType width_from_ = 1;
492 SampleType width_step_ = 0;
493 bool ramping_ =
false;
494 bool settling_ =
true;
495 int control_countdown_ = 0;