152 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
154 update_drift_coeffs();
161 double pulse_width() const noexcept {
return core_.pulse_width(); }
170 void set_bow(
double curvature)
noexcept { bow_ = curvature > 0.0 ? curvature : 0.0; }
171 double bow() const noexcept {
return bow_; }
180 p.amount = std::clamp(p.amount, 0.0, 1.0);
181 shapers_[
static_cast<std::size_t
>(
shape)] = p;
184 return shapers_[
static_cast<std::size_t
>(
shape)];
193 void set_level_tilt(
double db_per_octave)
noexcept { level_tilt_db_per_octave_ = db_per_octave; }
194 double level_tilt() const noexcept {
return level_tilt_db_per_octave_; }
202 core_reset_seconds_ = seconds > 0.0 ? seconds : 0.0;
212 ac_corner_hz_ = corner_hz > 0.0 ? corner_hz : 0.0;
219 double ac_pole() const noexcept {
return ac_enabled_ ? ac_pole_ : 1.0; }
234 drift_rate_hz_ = corner_hz > 0.0 ? corner_hz : 0.0;
235 update_drift_coeffs();
253 std::uint64_t
seed() const noexcept {
return seed_; }
269 double phase() const noexcept {
return core_.phase(); }
276 double next(
double increment)
noexcept {
277 const double modulated = increment * pitch_noise_factor();
278 double v = core_.next(modulated);
280 v = apply_waveshaper(v);
281 v *= level_for(modulated);
282 return ac_enabled_ ? dc_blocker_.process(v) : v;
291 double apply_bow(
double v)
const noexcept {
292 if (bow_ == 0.0 || core_.shape() !=
VaShape::saw)
return v;
295 double p = 0.5 * (v + 1.0);
296 p = p < 0.0 ? 0.0 : (p > 1.0 ? 1.0 : p);
299 const double charged = (1.0 - std::exp(-bow_ * p)) / (1.0 - std::exp(-bow_));
300 return 2.0 * charged - 1.0;
304 double apply_waveshaper(
double v)
const noexcept {
306 if (w.amount == 0.0)
return v;
312 const double lo = std::tanh(-w.drive + w.asymmetry);
313 const double hi = std::tanh(w.drive + w.asymmetry);
314 const double mid = 0.5 * (hi + lo);
315 const double half = 0.5 * (hi - lo);
316 const double shaped =
317 half != 0.0 ? (std::tanh(w.drive * v + w.asymmetry) - mid) / half : v;
318 return (1.0 - w.amount) * v + w.amount * shaped;
324 double level_for(
double increment)
const noexcept {
325 if (level_tilt_db_per_octave_ == 0.0 && core_reset_seconds_ == 0.0)
return 1.0;
326 const double frequency = std::fabs(increment) * sample_rate_;
327 if (!(frequency > 0.0))
return 1.0;
330 if (level_tilt_db_per_octave_ != 0.0) {
332 gain *= std::pow(10.0, -level_tilt_db_per_octave_ * octaves / 20.0);
334 if (core_reset_seconds_ != 0.0) {
335 const double eaten = core_reset_seconds_ * frequency;
336 gain *= eaten < 1.0 ? (1.0 - eaten) : 0.0;
351 double pitch_noise_factor() noexcept {
352 if (drift_depth_ == 0.0 && jitter_depth_ == 0.0)
return 1.0;
355 if (drift_depth_ != 0.0) {
356 drift_state_ = drift_pole_ * drift_state_ + drift_norm_ * drift_rng_.next_gaussian();
357 cents += drift_depth_ * drift_state_;
359 if (jitter_depth_ != 0.0)
360 cents += jitter_depth_ * jitter_rng_.next_gaussian();
362 return std::exp2(cents / 1200.0);
371 std::uint64_t state = 0;
373 void seed(std::uint64_t s)
noexcept { state =
s; }
375 std::uint64_t next_u64() noexcept {
376 std::uint64_t
z = (state += 0x9E3779B97F4A7C15ull);
377 z = (
z ^ (
z >> 30)) * 0xBF58476D1CE4E5B9ull;
378 z = (
z ^ (
z >> 27)) * 0x94D049BB133111EBull;
379 return z ^ (
z >> 31);
384 double next_uniform() noexcept {
386 static_cast<double>(next_u64() >> 11) * (1.0 / 9007199254740992.0);
387 return u > 0.0 ?
u : std::numeric_limits<double>::min();
390 double next_gaussian() noexcept {
391 const double u1 = next_uniform();
392 const double u2 = next_uniform();
393 return std::sqrt(-2.0 * std::log(u1)) *
394 std::cos(2.0 * std::numbers::pi * u2);
400 void seed_streams() noexcept {
401 drift_rng_.seed(seed_);
402 jitter_rng_.seed(seed_ ^ 0xD1B54A32D192ED03ull);
410 void update_drift_coeffs() noexcept {
418 drift_pole_ = drift_rate_hz_ > 0.0
419 ? std::exp(-2.0 * std::numbers::pi * drift_rate_hz_ / sample_rate_)
421 drift_norm_ = std::sqrt(1.0 - drift_pole_ * drift_pole_);
424 void update_ac_pole() noexcept {
425 ac_enabled_ = ac_corner_hz_ > 0.0;
427 ac_pole_ = std::exp(-2.0 * std::numbers::pi * ac_corner_hz_ / sample_rate_);
428 dc_blocker_.set_pole(ac_pole_);
435 double sample_rate_ = 48000.0;
438 std::array<WaveshaperParams, 4> shapers_{};
440 double level_tilt_db_per_octave_ = 0.0;
441 double core_reset_seconds_ = 0.0;
443 double ac_corner_hz_ = 0.0;
444 double ac_pole_ = 1.0;
445 bool ac_enabled_ =
false;
446 pulp::signal::DcBlocker<double> dc_blocker_;
448 double drift_depth_ = 0.0;
449 double jitter_depth_ = 0.0;
451 double drift_pole_ = 0.0;
452 double drift_norm_ = 1.0;
453 double drift_state_ = 0.0;
454 std::uint64_t seed_ = 0x0123456789ABCDEFull;
455 NoiseSource drift_rng_{seed_};
456 NoiseSource jitter_rng_{seed_ ^ 0xD1B54A32D192ED03ull};
Bandlimited-step (BLEP) and bandlimited-ramp (BLAMP) correction kernels.
VaShape shape() const noexcept
Definition va.hpp:177
void set_ac_coupling(double corner_hz) noexcept
Output DC-blocking corner, in Hz.
Definition vco.hpp:211
WaveshaperParams waveshaper(VaShape shape) const noexcept
Definition vco.hpp:183
double drift_depth() const noexcept
Definition vco.hpp:228
VaShape shape() const noexcept
Definition vco.hpp:158
void set_drift_rate_hz(double corner_hz) noexcept
Corner of the drift coloring filter, in Hz.
Definition vco.hpp:233
void set_shape(VaShape shape) noexcept
Definition vco.hpp:157
double drift_rate_hz() const noexcept
Definition vco.hpp:237
std::uint64_t seed() const noexcept
Definition vco.hpp:253
double pulse_width() const noexcept
Definition vco.hpp:161
void set_drift_depth(double depth_cents) noexcept
RMS of the slow drift wander, in cents.
Definition vco.hpp:227
void set_level_tilt(double db_per_octave) noexcept
Output level tilt, in dB per octave relative to kLevelReferenceHz.
Definition vco.hpp:193
VcoOscillator() noexcept
Seed the drift filter coefficients for the default sample rate so a default-constructed oscillator is...
Definition vco.hpp:149
void prepare(double sample_rate) noexcept
Definition vco.hpp:151
void set_pulse_width(double width) noexcept
Definition vco.hpp:160
void set_core_reset_time(double seconds) noexcept
Finite core-reset time, in seconds.
Definition vco.hpp:201
double phase() const noexcept
Definition vco.hpp:269
double bow() const noexcept
Definition vco.hpp:171
static constexpr double kLevelReferenceHz
Reference level frequency for the tilt curve.
Definition vco.hpp:286
double core_reset_time() const noexcept
Definition vco.hpp:204
double ac_pole() const noexcept
The DC blocker's realized pole for the current corner and sample rate; 1.0 when the stage is bypassed...
Definition vco.hpp:219
double jitter_depth() const noexcept
Definition vco.hpp:243
void set_waveshaper(VaShape shape, const WaveshaperParams ¶ms) noexcept
Definition vco.hpp:174
double level_tilt() const noexcept
Definition vco.hpp:194
static constexpr double kDefaultDriftRateHz
Default drift corner (Hz): a wander whose correlation time is a few hundred milliseconds — slow relat...
Definition vco.hpp:257
double next(double increment) noexcept
Generate one sample and advance by increment cycles.
Definition vco.hpp:276
void reset(double phase=0.0) noexcept
Reset the phase (and the AC-coupling and correction state) to a known point, and restart the noise st...
Definition vco.hpp:262
void set_seed(std::uint64_t seed) noexcept
Seed for both noise streams.
Definition vco.hpp:249
void set_bow(double curvature) noexcept
Curvature of the saw/ramp bow.
Definition vco.hpp:170
double ac_coupling() const noexcept
Definition vco.hpp:215
void set_jitter_depth(double depth_cents) noexcept
RMS of the fast, near-white cycle-to-cycle jitter, in cents per sample.
Definition vco.hpp:242
VaShape
The static shapes.
Definition va.hpp:113
@ saw
Value step of -2 at the wrap.
Definition va.hpp:115
@ u
Definition formant_filter_bank.hpp:20
@ s
Definition input_events.hpp:41
@ z
Definition input_events.hpp:42
The analog exponential pitch converter, as a pure control-voltage → frequency map.
Definition vco.hpp:91
double volts_per_octave
Volts spanning one octave (the converter's nominal sensitivity).
Definition vco.hpp:95
double tune_offset_cents
Per-oscillator calibration offset, in cents. Shifts every note equally.
Definition vco.hpp:97
double reference_hz
Frequency produced at 0 V with no calibration offset.
Definition vco.hpp:93
double hf_compression
High-end tracking compression: above hf_knee_octaves the converter tracks flat by this many octaves p...
Definition vco.hpp:105
double scale_error
V/oct scaling error: the realized octave span as a multiple of the ideal.
Definition vco.hpp:101
double hf_knee_octaves
Definition vco.hpp:106
double phase_increment(double control_volts, double sample_rate) const noexcept
Per-sample phase increment (cycles) for a control voltage at sample_rate.
Definition vco.hpp:118
double frequency_hz(double control_volts) const noexcept
Frequency for a control voltage, in Hz. Pure; allocates nothing.
Definition vco.hpp:109
A per-shape lumped memoryless nonlinearity — the "analog character" the output stage imprints.
Definition vco.hpp:130
double asymmetry
Even-harmonic bias.
Definition vco.hpp:137
double amount
Blend from clean (0) to fully shaped (1). Exactly 0 is a bit-exact bypass.
Definition vco.hpp:132
double drive
Pre-gain into the saturator. Higher drive = harder curve = more harmonics.
Definition vco.hpp:134
Virtual-analog oscillator: the classic static shapes, bandlimited by composing PhaseAccumulator (the ...