44 compander_coefficient_ =
46 pre_highpass_.set_highpass(20.0, 0.70710678, fs);
54 time_accumulator_ = 0.0;
56 previous_input_ = 0.0;
60 pre_highpass_.reset();
61 post_lowpass_.reset();
65 void set_seed(std::uint32_t seed)
noexcept { rng_.reseed(seed); }
75 void update(
double character_amount,
double delay_seconds)
noexcept {
76 stages_ =
static_cast<std::size_t
>(std::llround(
78 stages_ = std::clamp<std::size_t>(stages_, 1u,
kBbdMaxStages);
82 const double delay = std::max(delay_seconds, 1
e-6);
83 clock_period_ =
delay /
static_cast<double>(stages_);
84 const double clock_hz = 1.0 / clock_period_;
93 pre_lowpass_.set_lowpass(filter_cutoff, 0.70710678, sample_rate_);
94 post_lowpass_.set_lowpass(filter_cutoff, 0.70710678, oversampled_rate_);
99 shaper_offset_tanh_ =
fast_tanh(shaper_offset_);
109 shaper_compensation_ = 1.0;
110 constexpr double kProbe = 1
e-4;
111 const double slope = (waveshape(kProbe) - waveshape(-kProbe)) / (2.0 * kProbe);
112 shaper_compensation_ = (std::abs(slope) > 1
e-9) ? 1.0 / slope : 1.0;
116 std::size_t
stages() const noexcept {
return stages_; }
121 double process(
double x,
double clock_mod = 0.0) noexcept {
126 const double compressed =
129 compander_coefficient_ * compress_envelope_ +
130 (1.0 - compander_coefficient_) * std::abs(compressed));
132 const double filtered = pre_highpass_.process(pre_lowpass_.process(compressed));
139 (filtered - previous_input_) /
static_cast<double>(
kBbdOversample - 1);
140 double value = previous_input_;
141 const double internal_period = 1.0 / oversampled_rate_;
142 double first_read = 0.0;
143 const double period = std::max(clock_period_ * (1.0 + clock_mod), 1
e-9);
146 time_accumulator_ += internal_period;
154 while (time_accumulator_ >= period && ticks++ < kMaxTicksPerStep) {
155 const double jittered = period + next_jitter();
156 buckets_[write_] =
static_cast<float>(value);
158 time_accumulator_ -= std::max(jittered, 1
e-9);
162 const std::size_t read_index =
164 double read =
static_cast<double>(buckets_[read_index]);
165 if (drive_ > 0.0) read = waveshape(read);
166 read = post_lowpass_.process(read);
167 if (
u == 0) first_read = read;
169 previous_input_ = filtered;
194 compander_coefficient_ * expand_envelope_ +
195 (1.0 - compander_coefficient_) * std::abs(first_read));
196 return compander_enabled_ ? first_read * gain : first_read;
202 static constexpr int kMaxTicksPerStep = 64;
209 double waveshape(
double x)
const noexcept {
210 return (
fast_tanh(shaper_gain_ * x + shaper_offset_) - shaper_offset_tanh_) *
211 shaper_compensation_;
217 double next_jitter() noexcept {
218 if (jitter_amount_ <= 0.0)
return 0.0;
220 jitter_state_ = coefficient * jitter_state_ + (1.0 - coefficient) * rng_.
bipolar();
221 return jitter_state_ * jitter_amount_ *
kBbdJitterMax * clock_period_;
224 std::array<float, kBbdMaxStages> buckets_{};
225 std::size_t write_ = 0;
233 double sample_rate_ = 48000.0;
235 double clock_period_ = 1.0 / 48000.0;
237 double time_accumulator_ = 0.0;
238 double previous_input_ = 0.0;
239 double jitter_state_ = 0.0;
240 double jitter_amount_ = 0.0;
242 double shaper_gain_ = 1.0;
243 double shaper_offset_ = 0.0;
244 double shaper_offset_tanh_ = 0.0;
245 double shaper_compensation_ = 1.0;
246 double compander_coefficient_ = 0.0;
247 double compress_envelope_ = 1.0;
248 double expand_envelope_ = 1.0;
249 bool compander_enabled_ =
true;
One channel of the clocked BBD core, compander included.
Definition bbd.hpp:39
std::size_t stages() const noexcept
Definition bbd.hpp:116
void prepare(double fs)
Definition bbd.hpp:41
double process(double x, double clock_mod=0.0) noexcept
Definition bbd.hpp:121
double bandwidth_hz() const noexcept
Definition bbd.hpp:115
void set_compander_enabled(bool enabled) noexcept
Test hook: bypass the compander so the raw bucket-line noise floor is observable.
Definition bbd.hpp:70
void set_seed(std::uint32_t seed) noexcept
Definition bbd.hpp:65
void update(double character_amount, double delay_seconds) noexcept
Control-rate coefficient update.
Definition bbd.hpp:75
void reset() noexcept
Definition bbd.hpp:51
double bipolar() noexcept
Uniform in [-1, 1].
Definition primitives.hpp:92
constexpr double kBbdBandwidthMinHz
Definition tables.hpp:212
constexpr double kBbdShape
Definition tables.hpp:231
double flush_denormal(double v) noexcept
Definition primitives.hpp:45
constexpr std::array< double, 3 > kBbdDrive
Definition tables.hpp:197
constexpr double kBbdCompanderTauMs
Compander: hardware τ is set by an external capacitor, so real units vary.
Definition tables.hpp:222
constexpr double kBbdFilterCutoffScale
The clock law names the complete wet path's audible -3 dB edge.
Definition tables.hpp:218
constexpr std::array< double, 3 > kBbdJitterAmount
Definition tables.hpp:198
double fast_tanh(double v) noexcept
Definition primitives.hpp:57
constexpr std::array< double, 3 > kBbdAxis
Definition tables.hpp:190
constexpr double kBbdCompanderFloor
Definition tables.hpp:223
constexpr double kBbdBandwidthDivisor
Bandwidth law: the usable passband must sit well below the clock's Nyquist (the published aliasing co...
Definition tables.hpp:211
constexpr std::uint32_t kPrngSeed
PRNG seed.
Definition tables.hpp:92
double interpolate_knots(const std::array< double, N > &axis, const std::array< double, N > &values, double c) noexcept
Definition primitives.hpp:125
constexpr int kBbdOversample
Internal oversampling of the clocked core.
Definition tables.hpp:206
constexpr std::array< double, 3 > kBbdStages
PUBLISHED chip stage counts (Panasonic MN3005 / MN3008 / MN3007 datasheets), not design parameters: t...
Definition tables.hpp:195
constexpr double kBbdJitterSmoothness
Definition tables.hpp:235
constexpr double kBbdJitterMax
Clock jitter, as a fraction of the clock period. [1e-4–2e-3].
Definition tables.hpp:234
constexpr double kBbdBandwidthMaxHz
Definition tables.hpp:213
constexpr double kBbdCompanderDriveScale
The compander holds loop level near the rail, so the waveshaper's drive is backed off when it runs be...
Definition tables.hpp:227
constexpr std::size_t kBbdMaxStages
Largest stage count any table knot may request; sizes the bucket buffer.
Definition tables.hpp:202
constexpr double kBbdDriveGainSpan
Asymmetric soft clip: gain map and DC offset for even-harmonic content.
Definition tables.hpp:230
@ delay
Definition envelope.hpp:57
@ u
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20