123template <
typename SampleType =
float>
263 constexpr double kLoMs = 1.66;
264 constexpr double kHiMs = 5.35;
265 constexpr double kRateI = 0.513;
266 constexpr double kRateII = 0.863;
267 constexpr double kBothLoMs = 3.3;
268 constexpr double kBothHiMs = 3.7;
271 return {0.5 * (kLoMs + kHiMs), 0.5 * (kHiMs - kLoMs), kRateI, 0.0,
false};
273 return {0.5 * (kLoMs + kHiMs), 0.5 * (kHiMs - kLoMs), kRateII, 0.0,
false};
275 return {0.5 * (kBothLoMs + kBothHiMs), 0.5 * (kBothHiMs - kBothLoMs),
276 kRateI, kRateII,
true};
278 return {0.5 * (kLoMs + kHiMs), 0.5 * (kHiMs - kLoMs), kRateI, 0.0,
false};
288 sample_rate_ = (std::isfinite(sample_rate) && sample_rate > 0.0) ? sample_rate : 48000.0;
290 const int capacity =
static_cast<int>(
292 for (
auto& line : lines_) line.prepare(capacity);
293 max_read_samples_ =
static_cast<double>(capacity -
kInterpOrder - 1);
296 lfo_a_[k].prepare(sample_rate_);
297 lfo_b_[k].prepare(sample_rate_);
298 bbd_[k].prepare(sample_rate_);
304 for (std::size_t
c = 0;
c < 2; ++
c) {
305 shelf_lowpass_[
c].prepare(sample_rate_);
307 cross_highpass_[
c].prepare(sample_rate_);
341 if (!std::isfinite(
static_cast<double>(hz)))
return;
342 rate_hz_ = std::clamp(
static_cast<double>(hz), kRateMinHz, kRateMaxHz);
351 if (!std::isfinite(
static_cast<double>(depth01)))
return;
352 depth_ = std::clamp(
static_cast<double>(depth01), 0.0, 1.0);
360 if (!std::isfinite(
static_cast<double>(mix01)))
return;
361 mix_ = std::clamp(
static_cast<double>(mix01), 0.0, 1.0);
368 if (!std::isfinite(
static_cast<double>(width01)))
return;
369 width_ = std::clamp(
static_cast<double>(width01), 0.0, 1.0);
408 if (!prepared_ ||
left ==
nullptr ||
right ==
nullptr)
return;
412 const JunoModeSpec juno_cal =
juno_spec(juno_mode_);
413 const double center_ms = juno ? juno_cal.center_ms : cal.center_ms;
414 const double depth_ms = depth_ * (juno ? juno_cal.depth_ms : cal.depth_ms);
415 const double ms_to_samples = sample_rate_ * 0.001;
418 for (
int i = 0;
i < n; ++
i) {
419 const double in_l =
static_cast<double>(
left[
i]);
420 const double in_r =
static_cast<double>(
right[
i]);
421 if (!std::isfinite(in_l) || !std::isfinite(in_r)) {
426 const double src_l = cal.mono_source ? 0.5 * (in_l + in_r) : in_l;
427 const double src_r = cal.mono_source ? src_l : in_r;
429 lines_[0].push(
static_cast<SampleType
>(src_l));
430 lines_[1].push(
static_cast<SampleType
>(src_r));
432 std::array<double, kVoiceSlots> wet{};
433 const auto active =
static_cast<std::size_t
>(voices_);
434 for (std::size_t k = 0; k < active; ++k) {
435 const double m = modulation(k);
436 const double delay_ms = center_ms + depth_ms * m;
437 delay_ms_[k] = delay_ms;
439 double read_samples = delay_ms * ms_to_samples;
440 if (bbd_color_) read_samples -= bbd_delay_samples_;
441 read_samples = std::clamp(read_samples,
445 double tap = read_lagrange(lines_[voice_line_[k]], read_samples);
446 if (bbd_color_) tap = bbd_[k].process(tap);
450 double matrix_l = 0.0;
451 double matrix_r = 0.0;
454 matrix_l = src_l + wet[0];
458 matrix_l = in_l + wet[0];
459 matrix_r = in_r + wet[1];
466 const double boost_l = in_l + boost_extra * shelf_lowpass_[0].process_lowpass(in_l);
467 const double boost_r = in_r + boost_extra * shelf_lowpass_[1].process_lowpass(in_r);
468 const double cross_l = cross_highpass_[0].process_highpass(wet[1]);
469 const double cross_r = cross_highpass_[1].process_highpass(wet[0]);
470 matrix_l = boost_l + wet[0] - width_ * cross_l;
471 matrix_r = boost_r + wet[1] - width_ * cross_r;
475 matrix_l = in_l + wet[0] + width_ * 0.5 * wet[1];
476 matrix_r = in_r + wet[2] + width_ * 0.5 * wet[1];
480 left[
i] =
static_cast<SampleType
>((1.0 - mix_) * in_l + mix_ * matrix_l);
481 right[
i] =
static_cast<SampleType
>((1.0 - mix_) * in_r + mix_ * matrix_r);
486 for (
auto& line : lines_) line.reset();
493 for (std::size_t
c = 0;
c < 2; ++
c) {
494 shelf_lowpass_[
c].reset();
495 cross_highpass_[
c].reset();
503 for (
auto& line : lines_) line.discard_history();
510 for (std::size_t
c = 0;
c < 2; ++
c) {
511 shelf_lowpass_[
c].reset();
512 cross_highpass_[
c].reset();
529 return (voice >= 0 && voice <
kMaxVoices) ? delay_ms_[
static_cast<std::size_t
>(voice)] : 0.0;
540 return bbd_color_ ? bbd_delay_samples_ * 1000.0 / sample_rate_ : 0.0;
596 const double t = std::tan(kPi *
kDimCornerHz / sample_rate_);
597 return 2.0 / (1.0 + t);
601 static constexpr double kPi = 3.14159265358979323846;
604 static constexpr double kRateMinHz = 0.05;
605 static constexpr double kRateMaxHz = 10.0;
613 const int i =
static_cast<int>(std::floor(delay_samples));
614 const double frac = delay_samples -
static_cast<double>(
i);
615 const double ym1 =
static_cast<double>(line.read(
i - 1));
616 const double y0 =
static_cast<double>(line.read(
i));
617 const double y1 =
static_cast<double>(line.read(
i + 1));
618 const double y2 =
static_cast<double>(line.read(
i + 2));
623 double modulation(std::size_t index) {
631 const double a = lfo_a_[index].next();
632 const double b = lfo_b_[index].next();
633 return std::clamp(0.5 * (
a + b), -1.0, 1.0);
635 const double a = lfo_a_[index].next();
636 if (!trapezoid_)
return a;
640 return std::clamp(
kTrapK *
a, -1.0, 1.0);
643 void configure_voicing() {
648 voices_ = cal.voices;
649 trapezoid_ = cal.trapezoid;
650 juno_dual_ = juno && juno_cal.dual;
651 rate_hz_ = juno ? juno_cal.rate_a_hz : cal.rate_hz;
656 voice_line_[k] = (cal.mono_source || k == 0) ? 0 : 1;
662 const double offset =
static_cast<double>(
k) /
static_cast<double>(voices_);
663 lfo_a_[
k].set_wave(cal.wave);
664 lfo_a_[
k].set_phase_offset(offset);
665 lfo_b_[
k].set_wave(cal.wave);
666 lfo_b_[
k].set_phase_offset(offset);
677 const double rate_a = juno ? juno_cal.rate_a_hz : rate_hz_;
678 const double rate_b = juno ? juno_cal.rate_b_hz : rate_hz_;
680 lfo_a_[
k].set_rate_hz(rate_a);
681 lfo_b_[
k].set_rate_hz(rate_b);
685 void update_bbd_stage() {
686 if (!prepared_)
return;
690 const double center_ms = juno ? juno_cal.center_ms : cal.center_ms;
691 const double depth_ms = juno ? juno_cal.depth_ms : cal.depth_ms;
693 const double stage_ms = std::max(center_ms - depth_ms -
kBbdColorGuardMs, 0.05);
694 bbd_delay_samples_ = stage_ms * sample_rate_ * 0.001;
699 std::array<DelayLineT<SampleType>, 2> lines_{};
700 std::array<EffectLfoT<double>,
kVoiceSlots> lfo_a_{};
701 std::array<EffectLfoT<double>,
kVoiceSlots> lfo_b_{};
702 std::array<chardelay::BbdChannel, kVoiceSlots> bbd_{};
703 std::array<TptFilterT<double>, 2> shelf_lowpass_{};
704 std::array<TptFilterT<double>, 2> cross_highpass_{};
706 std::array<double, kVoiceSlots> delay_ms_{};
707 std::array<std::size_t, kVoiceSlots> voice_line_{};
709 double sample_rate_ = 48000.0;
710 double max_read_samples_ = 0.0;
711 double bbd_delay_samples_ = 0.0;
712 double rate_hz_ = 1.2;
720 bool trapezoid_ =
false;
721 bool juno_dual_ =
false;
722 bool bbd_color_ =
false;
723 bool prepared_ =
false;
One chorus engine, four voicings. Stereo in place.
Definition chorus_family.hpp:124
static constexpr int kGuardSamples
Definition chorus_family.hpp:147
double worst_case_gain() const
Peak output gain this configuration can produce for |input| ≤ 1 at mix = 1, as an L1 (worst-case-over...
Definition chorus_family.hpp:578
int voice_count() const
Definition chorus_family.hpp:532
static constexpr double kDimBoostDb
Definition chorus_family.hpp:173
static constexpr double kMaxDelayMs
Definition chorus_family.hpp:154
void set_stereo_width(SampleType width01)
0 .
Definition chorus_family.hpp:367
double highpass_l1() const
L1 norm of the first-order cross-feed high-pass at the prepared rate.
Definition chorus_family.hpp:595
static constexpr JunoModeSpec juno_spec(JunoMode m)
Definition chorus_family.hpp:262
void set_juno_mode(JunoMode m)
Ignored unless the voicing is juno_ensemble.
Definition chorus_family.hpp:330
double bbd_bandwidth_hz() const
Bandwidth the composed BBD stage is running at, Hz.
Definition chorus_family.hpp:536
double current_delay_ms(int voice) const
Instantaneous delay applied to voice k on the most recent sample, ms.
Definition chorus_family.hpp:528
void set_bbd_color(bool on)
Routes every voice's tap through chardelay::BbdChannel — the clock-domain bandwidth law and compander...
Definition chorus_family.hpp:398
void set_voicing(Voicing v)
Switches the calibration table and rewinds every LFO phase.
Definition chorus_family.hpp:321
static constexpr double kBbdColorCharacter
Definition chorus_family.hpp:182
static constexpr Calibration calibration(Voicing v)
Definition chorus_family.hpp:213
void discard_history() noexcept
Constant-time audio fault recovery.
Definition chorus_family.hpp:502
void set_mix(SampleType mix01)
Dry/wet, 0 .
Definition chorus_family.hpp:359
void prepare(double sample_rate)
Sizes both delay lines for kMaxDelayMs plus the interpolation stencil, prepares the composed primitiv...
Definition chorus_family.hpp:287
static constexpr double kBbdColorGuardMs
Definition chorus_family.hpp:189
void set_rate_hz(SampleType hz)
LFO rate in Hz, 0.05 .
Definition chorus_family.hpp:340
static constexpr double kDimCornerHz
Definition chorus_family.hpp:169
Voicing voicing() const
Definition chorus_family.hpp:326
static constexpr double kTapL1
Definition chorus_family.hpp:556
JunoMode
The Juno's three front-panel positions.
Definition chorus_family.hpp:131
@ mode_I_plus_II
Definition chorus_family.hpp:131
@ mode_I
Definition chorus_family.hpp:131
@ mode_II
Definition chorus_family.hpp:131
JunoMode juno_mode() const
Definition chorus_family.hpp:335
static constexpr int kMaxVoices
Definition chorus_family.hpp:136
ChorusEnsembleT()
Definition chorus_family.hpp:283
void reset()
Definition chorus_family.hpp:485
double shelf_l1() const
L1 norm of the dry low-shelf: its DC gain.
Definition chorus_family.hpp:592
bool bbd_color() const
Definition chorus_family.hpp:403
void process(SampleType *left, SampleType *right, int n)
Definition chorus_family.hpp:407
double bbd_stage_delay_ms() const
Delay the BBD colour stage carries when enabled, ms. Zero when off.
Definition chorus_family.hpp:539
static constexpr std::size_t kVoiceSlots
Definition chorus_family.hpp:137
double rate_hz() const
Definition chorus_family.hpp:346
static constexpr int kInterpOrder
Definition chorus_family.hpp:141
int latency_samples() const
0 for every voicing. See this file's latency note.
Definition chorus_family.hpp:517
Voicing
Definition chorus_family.hpp:126
@ juno_ensemble
Definition chorus_family.hpp:126
@ tri_chorus
Definition chorus_family.hpp:126
@ dimension_d
Definition chorus_family.hpp:126
@ ce2
Definition chorus_family.hpp:126
void set_depth(SampleType depth01)
Depth macro, 0 .
Definition chorus_family.hpp:350
static constexpr double kTrapK
Definition chorus_family.hpp:161
Definition delay_line.hpp:13
High-quality sample interpolation for delay lines and resampling.
The house low-frequency oscillator: seven waveforms plus a continuous shape morph,...
constexpr std::uint32_t kPrngSeed
PRNG seed.
Definition tables.hpp:92
T db_to_linear(T db)
Amplitude dB → linear gain. 0 dB → 1, −6.0206 dB → 0.5.
Definition units.hpp:71
Definition additive_bank.hpp:218
@ right
Definition multi_channel_meter.hpp:67
@ left
Definition multi_channel_meter.hpp:66
ChorusEnsembleT< float > ChorusEnsemble
Definition chorus_family.hpp:726
@ c
Definition spectrum_trace.hpp:35
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
LfoWave
The effect-wave vocabulary, shared by these functions and by EffectLfoT (lfo.hpp),...
Definition lfo_sampling.hpp:53
@ sine
Definition lfo_sampling.hpp:54
@ triangle
Definition lfo_sampling.hpp:55
ChorusEnsembleT< double > ChorusEnsemble64
Definition chorus_family.hpp:727
@ k
Definition input_events.hpp:40
One voicing's shipped calibration.
Definition chorus_family.hpp:197
bool trapezoid
True when the triangle is trapezoid-shaped before it modulates.
Definition chorus_family.hpp:210
int voices
Definition chorus_family.hpp:202
LfoWave wave
Definition chorus_family.hpp:203
double mix_default
Definition chorus_family.hpp:201
double rate_hz
Definition chorus_family.hpp:200
double depth_ms
Definition chorus_family.hpp:199
bool mono_source
True when the taps read a mono sum of the input rather than their own channel — the CE-2 because the ...
Definition chorus_family.hpp:208
double center_ms
Definition chorus_family.hpp:198
The Juno's per-mode calibration.
Definition chorus_family.hpp:254
double depth_ms
Definition chorus_family.hpp:256
double rate_b_hz
Second LFO; used only when dual.
Definition chorus_family.hpp:258
bool dual
Definition chorus_family.hpp:259
double center_ms
Definition chorus_family.hpp:255
double rate_a_hz
Definition chorus_family.hpp:257
static SampleType lagrange(SampleType frac, SampleType ym1, SampleType y0, SampleType y1, SampleType y2)
4-point Lagrange interpolation.
Definition interpolator.hpp:60
First-order topology-preserving transform (TPT) filter.
The real-unit vocabulary shared by every DSP block in the catalog.