63 update_smoothing_coefficient();
77 update_internal_rate();
78 update_smoothing_coefficient();
89 if (!valid_oversampling_factor(factor))
91 if (factor_ == factor)
94 update_internal_rate();
104 pole_hz_ = std::clamp(pole_hz, 20.0, 0.45 * sample_rate_);
113 resonance_ = std::clamp(normalized, 0.0, 1.0);
122 k_max_ = std::max(0.0, k_max);
130 drive_target_ = std::pow(10.0, std::clamp(drive_db, -96.0, 72.0) / 20.0);
137 saturation_target_ = std::clamp(headroom, 0.25, 16.0);
144 bias_target_ = std::clamp(bias, -2.0, 2.0);
152 compensation_ = std::clamp(amount, 0.0, 1.0);
153 update_compensation_target();
160 output_gain_target_ = std::max(0.0, gain);
169 cross_mod_depth_target_ = std::clamp(depth, -1.0, 1.0);
170 cross_mod_drive_target_ = std::clamp(drive, 0.1, 16.0);
179 update_compensation_target();
188 smoothing_ms_ = std::max(0.0, milliseconds);
189 update_smoothing_coefficient();
200 void set_drift(
double cutoff_cents,
double resonance_fraction,
double cutoff_rate_hz = 1.5,
201 double resonance_rate_hz = 0.0) noexcept {
202 drift_cutoff_cents_ = std::clamp(cutoff_cents, 0.0, 100.0);
203 drift_resonance_fraction_ = std::clamp(resonance_fraction, 0.0, 0.1);
204 drift_cutoff_rate_hz_ = std::clamp(cutoff_rate_hz, 0.05, 20.0);
205 drift_resonance_rate_hz_ =
206 resonance_rate_hz <= 0.0 ? 0.0 : std::clamp(resonance_rate_hz, 0.05, 40.0);
207 if (drift_cutoff_cents_ == 0.0 && drift_resonance_fraction_ == 0.0) {
208 drift_g_multiplier_ = 1.0;
209 drift_k_multiplier_ = 1.0;
218 SampleType
process(SampleType input)
noexcept {
219 step_base_rate_controls();
221 std::array<double, 16> values{};
222 std::array<double, 16> expanded{};
223 values[0] =
static_cast<double>(input);
224 std::size_t count = 1;
225 const int stages = stage_count();
227 for (
int stage = 0; stage < stages; ++stage) {
228 for (std::size_t
i = 0;
i < count; ++
i) {
229 expanded[2 *
i] = 2.0 * upsamplers_[stage].process(values[
i]);
230 expanded[2 *
i + 1] = 2.0 * upsamplers_[stage].process(0.0);
233 std::copy_n(expanded.begin(), count, values.begin());
236 for (std::size_t
i = 0;
i < count; ++
i)
237 values[
i] = process_internal(values[
i]);
239 for (
int stage = stages; stage-- > 0;) {
241 for (std::size_t
i = 0;
i < count; ++
i) {
244 const double output = downsamplers_[stage].process(values[2 *
i]);
245 static_cast<void>(downsamplers_[stage].process(values[2 *
i + 1]));
250 return static_cast<SampleType
>(values[0]);
259 if (
buffer ==
nullptr || num_samples <= 0)
261 for (
int i = 0;
i < num_samples; ++
i)
273 for (
auto& stage : upsamplers_)
275 for (
auto& stage : downsamplers_)
278 drift_rng_ = kDriftSeed;
279 drift_cutoff_state_ = 0.0;
280 drift_resonance_state_ = 0.0;
281 drift_g_multiplier_ = 1.0;
282 drift_k_multiplier_ = 1.0;
283 drift_countdown_ = 0;
292 if (factor != 1 && factor != 2 && factor != 4 && factor != 8 &&
296 for (
int level = 0; factor > 1; ++level, factor >>= 1)
297 latency += 32 >> level;
317 static constexpr std::uint64_t kDriftSeed = 0xD41F7ull;
318 static constexpr double kPi = 3.141592653589793238462643383279502884;
320 static bool valid_oversampling_factor(
int factor)
noexcept {
321 return factor == 1 || factor == 2 || factor == 4 || factor == 8 || factor == 16;
324 int stage_count() const noexcept {
326 for (
int factor = factor_; factor > 1; factor >>= 1)
331 static double flush(
double value)
noexcept {
332 return std::abs(value) <= 1.0e-18 ? 0.0 : value;
335 static double smooth(
double current,
double target,
double coefficient)
noexcept {
336 return current + coefficient * (target - current);
339 void update_internal_rate() noexcept {
340 internal_rate_ = sample_rate_ *
static_cast<double>(factor_);
343 void update_smoothing_coefficient() noexcept {
344 smoothing_coefficient_ =
345 smoothing_ms_ <= 0.0 ? 1.0
346 : 1.0 - std::exp(-1.0 / (smoothing_ms_ * 0.001 * sample_rate_));
349 void update_g_target() noexcept {
350 const double clamped = std::clamp(pole_hz_, 20.0, 0.45 * sample_rate_);
351 const double g = std::tan(kPi * clamped / internal_rate_);
352 g_target_ =
g / (1.0 +
g);
355 void update_k_target() noexcept {
356 k_target_ = resonance_ * k_max_;
357 update_compensation_target();
360 void update_compensation_target() noexcept {
361 compensation_gain_target_ =
365 void snap_coefficients() noexcept {
368 drive_ = drive_target_;
369 saturation_ = saturation_target_;
370 bias_ = bias_target_;
371 compensation_gain_ = compensation_gain_target_;
372 output_gain_ = output_gain_target_;
373 cross_mod_depth_ = cross_mod_depth_target_;
374 cross_mod_drive_ = cross_mod_drive_target_;
377 std::uint64_t random_u64() noexcept {
378 std::uint64_t
z = (drift_rng_ += 0x9E3779B97F4A7C15ull);
379 z = (
z ^ (
z >> 30)) * 0xBF58476D1CE4E5B9ull;
380 z = (
z ^ (
z >> 27)) * 0x94D049BB133111EBull;
381 return z ^ (
z >> 31);
384 double uniform_open01() noexcept {
385 constexpr double kScale = 1.0 / 9007199254740992.0;
386 return (
static_cast<double>(random_u64() >> 11) + 0.5) *
kScale;
389 double gaussian() noexcept {
390 const double u1 = uniform_open01();
391 const double u2 = uniform_open01();
392 return std::sqrt(-2.0 * std::log(u1)) * std::cos(2.0 * kPi * u2);
395 void step_drift() noexcept {
396 if (drift_cutoff_cents_ == 0.0 && drift_resonance_fraction_ == 0.0) {
397 drift_g_multiplier_ = 1.0;
398 drift_k_multiplier_ = 1.0;
402 const double dt = 32.0 / sample_rate_;
403 const double a1 = std::clamp(2.0 * kPi * drift_cutoff_rate_hz_ * dt, 0.0, 0.5);
404 const double resonance_rate =
405 drift_resonance_rate_hz_ == 0.0 ? drift_cutoff_rate_hz_ : drift_resonance_rate_hz_;
406 const double a2 = std::clamp(2.0 * kPi * resonance_rate * dt, 0.0, 0.5);
408 drift_cutoff_state_ += a1 * -drift_cutoff_state_ + std::sqrt(2.0 * a1) * 0.5 * gaussian();
409 drift_resonance_state_ +=
410 a2 * -drift_resonance_state_ + std::sqrt(2.0 * a2) * 0.5 * gaussian();
411 drift_cutoff_state_ = std::clamp(drift_cutoff_state_, -1.5, 1.5);
412 drift_resonance_state_ = std::clamp(drift_resonance_state_, -1.5, 1.5);
414 drift_g_multiplier_ = std::exp2(drift_cutoff_cents_ * drift_cutoff_state_ / 1200.0);
415 drift_k_multiplier_ = 1.0 + drift_resonance_fraction_ * drift_resonance_state_;
418 void step_base_rate_controls() noexcept {
419 if (drift_countdown_ <= 0) {
421 drift_countdown_ = 32;
425 const double coefficient = smoothing_coefficient_;
426 g_ = smooth(g_, std::clamp(g_target_ * drift_g_multiplier_, 0.0, 0.999999), coefficient);
427 k_ = smooth(k_, std::max(0.0, k_target_ * drift_k_multiplier_), coefficient);
428 drive_ = smooth(drive_, drive_target_, coefficient);
429 saturation_ = smooth(saturation_, saturation_target_, coefficient);
430 bias_ = smooth(bias_, bias_target_, coefficient);
431 compensation_gain_ = smooth(compensation_gain_, compensation_gain_target_, coefficient);
432 output_gain_ = smooth(output_gain_, output_gain_target_, coefficient);
433 cross_mod_depth_ = smooth(cross_mod_depth_, cross_mod_depth_target_, coefficient);
434 cross_mod_drive_ = smooth(cross_mod_drive_, cross_mod_drive_target_, coefficient);
437 double process_internal(
double input)
noexcept {
438 std::array<double, 4> precomputed{};
439 for (std::size_t
i = 0;
i < states_.size(); ++
i)
440 precomputed[
i] = (1.0 - g_) * states_[
i];
442 const double g2 = g_ * g_;
443 const double g3 = g2 * g_;
444 const double a = g2 * g2;
446 g3 * precomputed[0] + g2 * precomputed[1] + g_ * precomputed[2] + precomputed[3];
447 const double driven = drive_ * input;
448 const double tanh_bias = std::tanh(bias_);
450 double y4 = (
a * driven +
b) / (1.0 + k_ *
a);
451 for (
int iteration = 0; iteration < 3; ++iteration) {
452 const double w = driven - k_ * y4;
453 const double th = std::tanh(w / saturation_ + bias_);
454 const double f = y4 -
a * saturation_ * (th - tanh_bias) - b;
455 const double derivative = 1.0 +
a * k_ * (1.0 - th * th);
456 y4 -=
f / derivative;
459 const double junction =
460 saturation_ * (std::tanh((driven - k_ * y4) / saturation_ + bias_) - tanh_bias);
462 double previous = junction;
463 for (std::size_t
i = 0;
i < states_.size(); ++
i) {
464 const double output = g_ * previous + precomputed[
i];
465 states_[
i] = flush(2.0 * output - states_[
i]);
470 double output = taps_[3];
479 output = junction - 4.0 * taps_[0] + 6.0 * taps_[1] - 4.0 * taps_[2] + taps_[3];
482 output = 4.0 * (taps_[1] - 2.0 * taps_[2] + taps_[3]);
486 output *= 1.0 + cross_mod_depth_ * std::tanh(cross_mod_drive_ * driven);
487 return output * compensation_gain_ * output_gain_;
490 double sample_rate_ = 44100.0;
491 double internal_rate_ = 88200.0;
493 double pole_hz_ = 1000.0;
494 double resonance_ = 0.0;
498 double smoothing_ms_ = 0.0;
499 double smoothing_coefficient_ = 1.0;
501 double g_target_ = 0.0;
502 double k_target_ = 0.0;
503 double drive_target_ = 1.0;
504 double saturation_target_ = 1.0;
505 double bias_target_ = 0.0;
506 double compensation_ = 0.0;
507 double compensation_gain_target_ = 1.0;
508 double output_gain_target_ = 1.0;
509 double cross_mod_depth_target_ = 0.0;
510 double cross_mod_drive_target_ = 2.0;
515 double saturation_ = 1.0;
517 double compensation_gain_ = 1.0;
518 double output_gain_ = 1.0;
519 double cross_mod_depth_ = 0.0;
520 double cross_mod_drive_ = 2.0;
522 double drift_cutoff_cents_ = 0.0;
523 double drift_resonance_fraction_ = 0.0;
524 double drift_cutoff_rate_hz_ = 1.5;
525 double drift_resonance_rate_hz_ = 0.0;
526 double drift_cutoff_state_ = 0.0;
527 double drift_resonance_state_ = 0.0;
528 double drift_g_multiplier_ = 1.0;
529 double drift_k_multiplier_ = 1.0;
530 std::uint64_t drift_rng_ = kDriftSeed;
531 int drift_countdown_ = 0;
533 std::array<double, 4> states_{};
534 std::array<double, 4> taps_{};
535 std::array<detail::FixedHalfBandFir65, 4> upsamplers_{};
536 std::array<detail::FixedHalfBandFir65, 4> downsamplers_{};