Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
lfo.hpp
Go to the documentation of this file.
1#pragma once
2
44
47#include <pulp/signal/rng.hpp>
48
49#include <algorithm>
50#include <cmath>
51#include <cstdint>
52
53namespace pulp::signal {
54
59// `LfoWave`, the effect-wave vocabulary, lives with the stateless sampling
60// functions (`lfo_wave`, `lfo_wave_mean`, `lfo_wave_bandlimited`) that share
61// it; this header carries the stateful oscillator built on them.
62
63namespace detail {
64
68enum class LfoConvention : std::uint8_t { toolkit, effect };
69
74template <typename SampleType, LfoConvention Convention>
75class BasicLfoT {
76public:
77 static constexpr bool kEffectConvention = Convention == LfoConvention::effect;
78
88
94 enum class Mode : std::uint8_t { free, retrig, one_shot };
95
96 enum class FadeCurve : std::uint8_t { linear, quadratic };
97
98 enum class Stage : std::uint8_t { delay, fade_in, sustain, fade_out, done };
99
102 static constexpr double kMinRateHz = 0.001;
103
107 static constexpr double kMaxRateFraction = 0.45;
108
111 static constexpr double kMaxRateHz = 200.0;
112
116 static constexpr int kDefaultRandomSegments = 4;
117
118 static constexpr int kMaxRandomSegments = 16;
119
121 static constexpr int kMaxRepeatCount = 128;
122
123 // ── lifecycle ────────────────────────────────────────────────────────────
124
125 void prepare(double sample_rate) {
126 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
127 update_increment_();
128 update_stage_lengths_();
129 reset();
130 }
131
135 void reset() {
136 phase_ = 0.0;
137 cycles_ = 0;
138 seed_chains_();
139 restart_lifecycle_();
140 }
141
148 void retrigger() {
149 if (mode_ == Mode::free) return;
150 phase_ = 0.0;
151 cycles_ = 0;
152 restart_lifecycle_();
153 }
154
155 // ── rate ─────────────────────────────────────────────────────────────────
156
162 void set_rate_hz(double hz) {
163 requested_rate_hz_ = hz;
164 update_increment_();
165 }
166
170 void set_period_samples(double samples) {
171 const double min_period = 1.0 / kMaxRateFraction;
172 increment_ = 1.0 / std::max(samples, min_period);
173 rate_hz_ = increment_ * sample_rate_;
174 requested_rate_hz_ = rate_hz_;
175 }
176
177 double rate_hz() const { return rate_hz_; }
178 double period_samples() const { return increment_ > 0.0 ? 1.0 / increment_ : 0.0; }
179
180 // ── shape ────────────────────────────────────────────────────────────────
181
182 void set_wave(Wave w) requires (!kEffectConvention) {
183 wave_ = w;
184 update_increment_();
185 }
187 morph_enabled_ = false;
188 switch (w) {
189 case LfoWave::sine: wave_ = Wave::sine; break;
190 case LfoWave::triangle: wave_ = Wave::triangle; break;
191 case LfoWave::saw_up: wave_ = Wave::saw_up; break;
192 case LfoWave::saw_down: wave_ = Wave::saw_down; break;
193 case LfoWave::square: wave_ = Wave::square; break;
194 case LfoWave::sample_hold: wave_ = Wave::sh_random; break;
195 case LfoWave::smooth_random: wave_ = Wave::smooth_random; break;
196 }
197 update_increment_();
198 }
199 Wave wave() const requires (!kEffectConvention) { return wave_; }
200 LfoWave wave() const requires kEffectConvention {
201 switch (wave_) {
202 case Wave::sine: return LfoWave::sine;
204 case Wave::saw_up: return LfoWave::saw_up;
206 case Wave::square: return LfoWave::square;
209 }
210 return LfoWave::sine;
211 }
212
217 void set_shape_morph(float m) {
218 shape_morph_ = std::clamp(m, 0.0f, 3.0f);
219 morph_enabled_ = true;
220 }
221
222 void set_shape_morph_enabled(bool on) { morph_enabled_ = on; }
223 bool shape_morph_enabled() const { return morph_enabled_; }
224
226 void set_pulse_width(float pw) { pulse_width_ = std::clamp(pw, 0.05f, 0.95f); }
227
230 void set_tri_bias(float b) { tri_bias_ = std::clamp(b, -1.0f, 1.0f); }
231
235 void set_random_blend(float b) { random_blend_ = std::clamp(b, 0.0f, 1.0f); }
236
241 void set_random_segments(int n) { segments_ = std::clamp(n, 1, kMaxRandomSegments); }
242
245 void set_seed(std::uint32_t s) {
246 seed_ = s;
247 if constexpr (kEffectConvention) reset();
248 }
249
250 // ── phase & stereo ───────────────────────────────────────────────────────
251
253 void set_phase_offset(double cycles01) { phase_offset_ = wrap01(cycles01); }
254
257 void set_stereo_offset(double cycles01) {
258 if constexpr (kEffectConvention)
259 phase_offset_ = wrap01(cycles01);
260 else
261 stereo_offset_ = wrap01(cycles01);
262 }
263
264 // ── lifecycle shaping ────────────────────────────────────────────────────
265
268 void set_delay_ms(double ms) {
269 delay_ms_ = std::clamp(ms, 0.0, 120000.0);
270 update_stage_lengths_();
271 restart_if_idle_();
272 }
273
275 void set_fade_in_ms(double ms) {
276 fade_in_ms_ = std::clamp(ms, 0.0, 120000.0);
277 update_stage_lengths_();
278 restart_if_idle_();
279 }
280
282 void set_fade_out_ms(double ms, bool enabled) {
283 fade_out_ms_ = std::clamp(ms, 0.0, 120000.0);
284 fade_out_enabled_ = enabled;
285 update_stage_lengths_();
286 }
287
288 void set_fade_curve(FadeCurve c) { fade_curve_ = c; }
289
293 void set_repeat_count(int n) { repeat_count_ = std::clamp(n, 0, kMaxRepeatCount); }
294
295 void set_mode(Mode m) { mode_ = m; }
296 Mode mode() const { return mode_; }
297
298 // ── sampling without aliasing ────────────────────────────────────────────
299
307 void set_band_limited(bool on)
308 requires(!kEffectConvention)
309 {
310 band_limited_ = on;
311 }
312 bool band_limited() const
313 requires(!kEffectConvention)
314 {
315 return band_limited_;
316 }
317
330 SampleType next_mean(int samples)
331 requires(!kEffectConvention)
332 {
333 if (samples <= 0) {
334 const double p = wrap01(phase_ + static_cast<double>(phase_offset_));
335 update_chain_(chains_[0], p);
336 return static_cast<SampleType>(envelope_() * shape_(p, chains_[0]));
337 }
338 const double span = static_cast<double>(samples) * increment_;
339 const bool deterministic =
340 random_blend_ <= 0.0f && wave_ != Wave::sh_random && wave_ != Wave::smooth_random;
341 const int limit = effective_repeat_();
342 const bool ends_this_block =
343 limit > 0 && cycles_ + static_cast<long long>(std::floor(phase_ + span)) >= limit;
344 if (stage_ != Stage::sustain || !deterministic || ends_this_block) {
345 double acc = 0.0;
346 for (int i = 0; i < samples; ++i)
347 acc += static_cast<double>(next());
348 return static_cast<SampleType>(acc / static_cast<double>(samples));
349 }
350 // Each output sample stands for the half-sample on either side of its
351 // phase, so the mean of the next N outputs is the integral from half a
352 // step before the first to half a step before the one after the last.
353 const double p = wrap01(phase_ + static_cast<double>(phase_offset_)) - 0.5 * increment_;
354 const float env = envelope_();
355 const double mean =
356 morph_enabled_ ? morph_mean_(p, p + span) : wave_mean_(wave_, p, p + span);
357 phase_ += span;
358 if (phase_ >= 1.0) {
359 const double whole = std::floor(phase_);
360 phase_ -= whole;
361 cycles_ += static_cast<long long>(whole);
362 on_cycles_completed_();
363 }
364 return static_cast<SampleType>(static_cast<double>(env) * mean);
365 }
366
367 Stage stage() const { return stage_; }
368 bool finished() const { return stage_ == Stage::done; }
369 long long cycles_completed() const { return cycles_; }
370 double phase() const {
371 if constexpr (kEffectConvention)
372 return wrap01(phase_ + static_cast<double>(phase_offset_));
373 return phase_;
374 }
375
376 // ── output ───────────────────────────────────────────────────────────────
377
379 SampleType next() {
380 if constexpr (kEffectConvention) advance_();
381 const float env = envelope_();
382 const double p = wrap01(phase_ + static_cast<double>(phase_offset_));
383 update_chain_(chains_[0], p);
384 if constexpr (kEffectConvention)
385 return static_cast<SampleType>(static_cast<double>(env)
386 * legacy_effect_shape_(p, chains_[0]));
387 const float value = shape_(p, chains_[0]);
388 advance_();
389 return static_cast<SampleType>(env * value);
390 }
391
395 SampleType next_unipolar() { return static_cast<SampleType>(bi_to_uni(static_cast<float>(next()))); }
396
407 void next_stereo(SampleType& left, SampleType& right) {
408 const float env = envelope_();
409 const double pl = wrap01(phase_ + static_cast<double>(phase_offset_));
410 update_chain_(chains_[0], pl);
411 const float l = shape_(pl, chains_[0]);
412 float r = l;
413 if (stereo_offset_ != 0.0f) {
414 const double pr = wrap01(pl + static_cast<double>(stereo_offset_));
415 update_chain_(chains_[1], pr);
416 r = shape_(pr, chains_[1]);
417 }
418 left = static_cast<SampleType>(env * l);
419 right = static_cast<SampleType>(env * r);
420 advance_();
421 }
422
427 void next_quadrature(SampleType& sine_out, SampleType& cosine_out) {
428 if constexpr (kEffectConvention) advance_();
429 const float env = envelope_();
430 const double p = wrap01(phase_ + static_cast<double>(phase_offset_));
431 update_chain_(chains_[0], p);
432 const double angle = 6.283185307179586477 * p;
433 if constexpr (kEffectConvention) {
434 sine_out = static_cast<SampleType>(static_cast<double>(env) * std::sin(angle));
435 cosine_out = static_cast<SampleType>(static_cast<double>(env) * std::cos(angle));
436 return;
437 }
438 sine_out = static_cast<SampleType>(env * static_cast<float>(std::sin(angle)));
439 cosine_out = static_cast<SampleType>(env * static_cast<float>(std::cos(angle)));
440 advance_();
441 }
442
443private:
444 struct RandomChain {
445 Xorshift32 rng{};
446 float sh = 0.0f;
447 float glide_prev = 0.0f;
448 float glide_next = 0.0f;
449 float seg_frac = 0.0f;
450 int seg_index = -1;
451 float prev_phase = 2.0f;
452 bool primed = false;
453
454 float smooth() const {
455 const float w = 0.5f - 0.5f * std::cos(3.14159265358979324f * seg_frac);
456 return glide_prev + (glide_next - glide_prev) * w;
457 }
458 };
459
460 static double wrap01(double x) { return x - std::floor(x); }
461 static float wrap01(float x) { return x - std::floor(x); }
462
463 void update_increment_() {
464 const double max_rate = kEffectConvention
465 ? kMaxRateHz
466 : kMaxRateFraction * sample_rate_;
467 const double min_rate = kEffectConvention ? 0.0 : kMinRateHz;
468 rate_hz_ = std::clamp(requested_rate_hz_, min_rate,
469 std::max(min_rate, max_rate));
470 increment_ = rate_hz_ / sample_rate_;
471 }
472
473 void update_stage_lengths_() {
474 delay_samples_ = static_cast<long long>(std::lround(delay_ms_ * 0.001 * sample_rate_));
475 fade_in_samples_ = static_cast<long long>(std::lround(fade_in_ms_ * 0.001 * sample_rate_));
476 fade_out_samples_ = static_cast<long long>(std::lround(fade_out_ms_ * 0.001 * sample_rate_));
477 }
478
479 void seed_chains_() {
480 chains_[0] = RandomChain{};
481 chains_[1] = RandomChain{};
482 chains_[0].rng.seed(static_cast<std::uint32_t>(mix64(seed_) & 0xFFFFFFFFu));
483 chains_[1].rng.seed(static_cast<std::uint32_t>(mix64(seed_ ^ 0x5DEECE66Du) >> 32));
484 }
485
486 void restart_lifecycle_() {
487 stage_pos_ = 0;
488 fade_out_start_ = 1.0f;
489 if (delay_samples_ > 0) stage_ = Stage::delay;
490 else if (fade_in_samples_ > 0) stage_ = Stage::fade_in;
491 else stage_ = Stage::sustain;
492 }
493
497 void restart_if_idle_() {
498 if (stage_pos_ == 0 && cycles_ == 0 && phase_ == 0.0) restart_lifecycle_();
499 }
500
501 int effective_repeat_() const {
502 if (mode_ == Mode::one_shot && repeat_count_ == 0) return 1;
503 return repeat_count_;
504 }
505
506 float fade_shape_(float t) const {
507 const float c = std::clamp(t, 0.0f, 1.0f);
508 return fade_curve_ == FadeCurve::quadratic ? c * c : c;
509 }
510
511 float envelope_() const {
512 switch (stage_) {
513 case Stage::delay:
514 return 0.0f;
515 case Stage::fade_in:
516 if (fade_in_samples_ <= 0) return 1.0f;
517 return fade_shape_(static_cast<float>(stage_pos_)
518 / static_cast<float>(fade_in_samples_));
519 case Stage::sustain:
520 return 1.0f;
521 case Stage::fade_out:
522 if (fade_out_samples_ <= 0) return 0.0f;
523 return fade_out_start_
524 * (1.0f - fade_shape_(static_cast<float>(stage_pos_)
525 / static_cast<float>(fade_out_samples_)));
526 case Stage::done:
527 default:
528 return 0.0f;
529 }
530 }
531
532 void advance_() {
533 const Stage before = stage_;
534 advance_lifecycle_();
535 if (before == Stage::delay || before == Stage::done) return;
536 advance_phase_();
537 }
538
539 void advance_lifecycle_() {
540 switch (stage_) {
541 case Stage::delay:
542 if (++stage_pos_ >= delay_samples_) {
543 stage_ = fade_in_samples_ > 0 ? Stage::fade_in : Stage::sustain;
544 stage_pos_ = 0;
545 }
546 return;
547 case Stage::fade_in:
548 if (++stage_pos_ >= fade_in_samples_) {
549 stage_ = Stage::sustain;
550 stage_pos_ = 0;
551 }
552 return;
553 case Stage::fade_out:
554 if (++stage_pos_ >= fade_out_samples_) {
555 stage_ = Stage::done;
556 stage_pos_ = 0;
557 }
558 return;
559 case Stage::sustain:
560 case Stage::done:
561 default:
562 return;
563 }
564 }
565
566 void advance_phase_() {
567 phase_ += increment_;
568 if (phase_ >= 1.0) {
569 const double whole = std::floor(phase_);
570 phase_ -= whole;
571 cycles_ += static_cast<long long>(whole);
572 on_cycles_completed_();
573 }
574 }
575
576 void on_cycles_completed_() {
577 const int limit = effective_repeat_();
578 if (limit <= 0 || cycles_ < limit) return;
579 if (stage_ == Stage::fade_out || stage_ == Stage::done) return;
580 if (fade_out_enabled_ && fade_out_samples_ > 0) {
581 fade_out_start_ = envelope_();
582 stage_ = Stage::fade_out;
583 stage_pos_ = 0;
584 } else {
585 stage_ = Stage::done;
586 stage_pos_ = 0;
587 }
588 }
589
590 void update_chain_(RandomChain& c, double phase) {
591 const auto p = static_cast<float>(phase);
592 if (!c.primed || p < c.prev_phase) {
593 c.sh = c.rng.next_bipolar();
594 c.primed = true;
595 }
596 c.prev_phase = p;
597 const float scaled = p * static_cast<float>(segments_);
598 int seg = static_cast<int>(scaled);
599 seg = std::clamp(seg, 0, segments_ - 1);
600 if (seg != c.seg_index) {
601 c.glide_prev = c.seg_index < 0 ? c.rng.next_bipolar() : c.glide_next;
602 c.glide_next = c.rng.next_bipolar();
603 c.seg_index = seg;
604 }
605 c.seg_frac = std::clamp(scaled - static_cast<float>(seg), 0.0f, 1.0f);
606 }
607
608 static constexpr LfoWave sampling_wave_(Wave w) noexcept {
609 switch (w) {
610 case Wave::sine:
611 return LfoWave::sine;
612 case Wave::triangle:
613 return LfoWave::triangle;
614 case Wave::saw_up:
615 return LfoWave::saw_up;
616 case Wave::saw_down:
617 return LfoWave::saw_down;
618 case Wave::square:
619 return LfoWave::square;
620 case Wave::sh_random:
623 default:
625 }
626 }
627
628 LfoShapeParams shape_params_() const noexcept {
629 LfoShapeParams params;
630 params.pulse_width = static_cast<double>(pulse_width_);
631 params.triangle_peak =
632 static_cast<double>(std::clamp(0.5f + 0.5f * tri_bias_, 1.0e-4f, 1.0f - 1.0e-4f));
633 return params;
634 }
635
636 double wave_mean_(Wave w, double from, double to) const noexcept {
637 return lfo_wave_mean(sampling_wave_(w), from, to, shape_params_());
638 }
639
640 double morph_mean_(double from, double to) const noexcept {
641 static constexpr Wave kMorphOrder[4] = {Wave::sine, Wave::triangle, Wave::saw_up,
643 int index = static_cast<int>(shape_morph_);
644 index = std::clamp(index, 0, 2);
645 const double w = static_cast<double>(shape_morph_ - static_cast<float>(index));
646 const double a = wave_mean_(kMorphOrder[index], from, to);
647 if (w <= 0.0)
648 return a;
649 const double b = wave_mean_(kMorphOrder[index + 1], from, to);
650 return a + w * (b - a);
651 }
652
653 float pure_wave_(Wave w, double phase, const RandomChain& c) const {
654 if constexpr (!kEffectConvention) {
655 if (band_limited_ && w != Wave::sine && w != Wave::sh_random &&
657 return static_cast<float>(
658 lfo_wave_bandlimited(sampling_wave_(w), phase, increment_, shape_params_()));
659 }
660 const auto p = static_cast<float>(phase);
661 switch (w) {
662 case Wave::sine:
663 return static_cast<float>(std::sin(6.283185307179586477 * phase));
664 case Wave::triangle: {
665 if constexpr (kEffectConvention) {
666 if (p < 0.25f) return 4.0f * p;
667 if (p < 0.75f) return 2.0f - 4.0f * p;
668 return 4.0f * p - 4.0f;
669 }
670 const float peak = std::clamp(0.5f + 0.5f * tri_bias_, 1.0e-4f, 1.0f - 1.0e-4f);
671 if (p < peak) return -1.0f + 2.0f * (p / peak);
672 return 1.0f - 2.0f * ((p - peak) / (1.0f - peak));
673 }
674 case Wave::saw_up:
675 return 2.0f * p - 1.0f;
676 case Wave::saw_down:
677 return 1.0f - 2.0f * p;
678 case Wave::square:
679 return p < pulse_width_ ? 1.0f : -1.0f;
680 case Wave::sh_random:
681 return c.sh;
683 default:
684 return c.smooth();
685 }
686 }
687
693 double legacy_effect_shape_(double p, const RandomChain& c) const {
694 switch (wave_) {
695 case Wave::sine: return std::sin(6.283185307179586477 * p);
696 case Wave::triangle:
697 if (p < 0.25) return 4.0 * p;
698 if (p < 0.75) return 2.0 - 4.0 * p;
699 return 4.0 * p - 4.0;
700 case Wave::saw_up: return 2.0 * p - 1.0;
701 case Wave::saw_down: return 1.0 - 2.0 * p;
702 case Wave::square: return p < static_cast<double>(pulse_width_) ? 1.0 : -1.0;
703 case Wave::sh_random:
705 default: return static_cast<double>(shape_(p, c));
706 }
707 }
708
709 float morph_shape_(double phase, const RandomChain& c) const {
710 static constexpr Wave kMorphOrder[4] = {
712 int index = static_cast<int>(shape_morph_);
713 index = std::clamp(index, 0, 2);
714 const float w = shape_morph_ - static_cast<float>(index);
715 // Both endpoints of the pair short-circuit, so an integer morph
716 // position is the pure wave bit for bit rather than a crossfade that
717 // happens to land near it.
718 if (w == 0.0f) return pure_wave_(kMorphOrder[index], phase, c);
719 if (w == 1.0f) return pure_wave_(kMorphOrder[index + 1], phase, c);
720 const float a = pure_wave_(kMorphOrder[index], phase, c);
721 const float b = pure_wave_(kMorphOrder[index + 1], phase, c);
722 return a + w * (b - a);
723 }
724
725 float shape_(double phase, const RandomChain& c) const {
726 const float base = morph_enabled_ ? morph_shape_(phase, c)
727 : pure_wave_(wave_, phase, c);
728 if (random_blend_ <= 0.0f) return base;
729 return base + random_blend_ * (c.sh - base);
730 }
731
732 RandomChain chains_[2]{};
733
734 double sample_rate_ = 48000.0;
735 double rate_hz_ = 1.0;
736 double requested_rate_hz_ = 1.0;
737 double increment_ = 1.0 / 48000.0;
738 double phase_ = 0.0;
739
740 double delay_ms_ = 0.0;
741 double fade_in_ms_ = 0.0;
742 double fade_out_ms_ = 0.0;
743 long long delay_samples_ = 0;
744 long long fade_in_samples_ = 0;
745 long long fade_out_samples_ = 0;
746 long long stage_pos_ = 0;
747
748 double phase_offset_ = 0.0;
749 float stereo_offset_ = 0.0f;
750 float shape_morph_ = 0.0f;
751 float pulse_width_ = 0.5f;
752 float tri_bias_ = 0.0f;
753 float random_blend_ = 0.0f;
754 float fade_out_start_ = 1.0f;
755 bool band_limited_ = false;
756
757 std::uint32_t seed_ = 0x12345678u;
758 int segments_ = 1;
759 int repeat_count_ = 0;
760 // 64-bit: a free-running LFO at the rate ceiling wraps ~86400 cycles per
761 // second at 192 kHz, which overflows a 32-bit counter within hours.
762 long long cycles_ = 0;
763
764 Wave wave_ = Wave::sine;
765 Mode mode_ = Mode::free;
766 Stage stage_ = Stage::sustain;
767 FadeCurve fade_curve_ = FadeCurve::linear;
768 bool morph_enabled_ = false;
769 bool fade_out_enabled_ = false;
770};
771
772} // namespace detail
773
774template <typename SampleType = float>
775class LfoT : public detail::BasicLfoT<SampleType, detail::LfoConvention::toolkit> {};
776template <typename SampleType = float>
777class EffectLfoT : public detail::BasicLfoT<SampleType, detail::LfoConvention::effect> {};
782
783} // namespace pulp::signal
Definition lfo.hpp:777
Definition lfo.hpp:775
Xorshift32 (Marsaglia, "Xorshift RNGs", J.
Definition rng.hpp:33
Shared implementation selected by the two explicit public LFO types below.
Definition lfo.hpp:75
void set_fade_out_ms(double ms, bool enabled)
Depth ramps back to 0 after the repeat count is exhausted.
Definition lfo.hpp:282
Stage
Definition lfo.hpp:98
void prepare(double sample_rate)
Definition lfo.hpp:125
static constexpr double kMaxRateFraction
Rate ceiling as a fraction of the sample rate.
Definition lfo.hpp:107
void set_seed(std::uint32_t s)
Seed for the random waveforms and the random blend.
Definition lfo.hpp:245
SampleType next_unipolar()
Unipolar output in [0, 1].
Definition lfo.hpp:395
double period_samples() const
Definition lfo.hpp:178
Wave
Definition lfo.hpp:79
@ sh_random
Definition lfo.hpp:85
@ smooth_random
Definition lfo.hpp:86
static constexpr int kMaxRepeatCount
Upper bound on set_repeat_count().
Definition lfo.hpp:121
void next_quadrature(SampleType &sine_out, SampleType &cosine_out)
Exact sine/cosine pair at the current phase, regardless of the selected waveform.
Definition lfo.hpp:427
void set_delay_ms(double ms)
Silence (a neutral output) before the LFO starts.
Definition lfo.hpp:268
void set_repeat_count(int n)
0 = run forever.
Definition lfo.hpp:293
Wave wave() const
Definition lfo.hpp:199
void set_band_limited(bool on)
Band-limit the deterministic waves at audio rates: the triangle, saws and square take the polyBLEP / ...
Definition lfo.hpp:307
void set_mode(Mode m)
Definition lfo.hpp:295
bool shape_morph_enabled() const
Definition lfo.hpp:223
void set_random_segments(int n)
Targets latched per cycle in smooth_random.
Definition lfo.hpp:241
Mode
free runs from reset() and ignores retrigger() — that is what "free running" means,...
Definition lfo.hpp:94
void set_shape_morph_enabled(bool on)
Definition lfo.hpp:222
static constexpr int kMaxRandomSegments
Definition lfo.hpp:118
void set_tri_bias(float b)
Rise/fall skew of the triangle.
Definition lfo.hpp:230
FadeCurve
Definition lfo.hpp:96
bool band_limited() const
Definition lfo.hpp:312
void set_rate_hz(double hz)
Free-running rate.
Definition lfo.hpp:162
void set_fade_in_ms(double ms)
Depth ramps 0 -> 1 over this time once the delay expires.
Definition lfo.hpp:275
double phase() const
Definition lfo.hpp:370
LfoWave wave() const
Definition lfo.hpp:200
void set_fade_curve(FadeCurve c)
Definition lfo.hpp:288
void set_wave(LfoWave w)
Definition lfo.hpp:186
Mode mode() const
Definition lfo.hpp:296
void set_phase_offset(double cycles01)
Output phase offset in cycles (0..1); present a 0-360 degree control.
Definition lfo.hpp:253
bool finished() const
Definition lfo.hpp:368
long long cycles_completed() const
Definition lfo.hpp:369
SampleType next_mean(int samples)
The mean of the next samples outputs, advancing by that many samples: the band-limited value for a co...
Definition lfo.hpp:330
static constexpr double kMinRateHz
Rate floor.
Definition lfo.hpp:102
static constexpr bool kEffectConvention
Definition lfo.hpp:77
void set_random_blend(float b)
Crossfade the chosen waveform against a per-cycle sample-and-hold random value — "how random is this ...
Definition lfo.hpp:235
Stage stage() const
Definition lfo.hpp:367
static constexpr double kMaxRateHz
Ceiling of the bounded effect-LFO lane.
Definition lfo.hpp:111
void next_stereo(SampleType &left, SampleType &right)
The stereo pair: the same LFO evaluated at phase and phase + stereo_offset.
Definition lfo.hpp:407
void set_pulse_width(float pw)
Duty cycle of the square wave.
Definition lfo.hpp:226
SampleType next()
Bipolar output in [-1, 1], scaled by the lifecycle depth envelope.
Definition lfo.hpp:379
double rate_hz() const
Definition lfo.hpp:177
void set_period_samples(double samples)
Synced use.
Definition lfo.hpp:170
static constexpr int kDefaultRandomSegments
Suggested segment count for the N-segment smooth-random mode.
Definition lfo.hpp:116
void set_stereo_offset(double cycles01)
Offset of the right output in next_stereo(), in cycles.
Definition lfo.hpp:257
void retrigger()
Restart phase and the delay/fade lifecycle.
Definition lfo.hpp:148
void set_wave(Wave w)
Definition lfo.hpp:182
void set_shape_morph(float m)
Continuous blend across sine -> triangle -> saw_up -> square as m runs 0 -> 3, by crossfading the adj...
Definition lfo.hpp:217
void reset()
Rewind to a fresh start: phase 0, lifecycle restarted, random streams re-seeded.
Definition lfo.hpp:135
How to SAMPLE an LFO waveform without aliasing, for each kind of consumer.
The small modular utilities every patch reaches for between a modulation source and its destination: ...
@ b
Definition pitch.hpp:31
@ index
Definition gpu_pass.hpp:140
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
LfoConvention
Selects the waveform/rate convention at the type boundary.
Definition lfo.hpp:68
@ effect
Definition lfo.hpp:68
@ toolkit
Definition lfo.hpp:68
@ whole
Definition units.hpp:287
Definition additive_bank.hpp:218
@ right
Definition multi_channel_meter.hpp:67
@ left
Definition multi_channel_meter.hpp:66
@ peak
Sample peak. Literal, fastest-responding.
Definition feedforward_compressor.hpp:119
@ c
Definition spectrum_trace.hpp:35
constexpr std::uint64_t mix64(std::uint64_t z)
splitmix64 finalizer (Steele, Lea & Flood, OOPSLA 2014).
Definition rng.hpp:140
LfoT< double > Lfo64
Definition lfo.hpp:779
double lfo_wave_bandlimited(LfoWave wave, double phase, double increment, const LfoShapeParams &params={}) noexcept
The band-limited value of wave for a consumer that reads it EVERY sample, at phase with a per-sample ...
Definition lfo_sampling.hpp:196
EffectLfoT< double > EffectLfo64
Definition lfo.hpp:781
LfoT< float > Lfo
Definition lfo.hpp:778
@ 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
@ sample_hold
Definition lfo_sampling.hpp:59
@ square
Definition lfo_sampling.hpp:58
@ saw_down
Definition lfo_sampling.hpp:57
@ sine
Definition lfo_sampling.hpp:54
@ saw_up
Definition lfo_sampling.hpp:56
@ smooth_random
Definition lfo_sampling.hpp:60
@ triangle
Definition lfo_sampling.hpp:55
float bi_to_uni(float x)
Bipolar [-1, 1] to unipolar [0, 1].
Definition mod_tools.hpp:81
double lfo_wave_mean(LfoWave wave, double from, double to, const LfoShapeParams &params={}) noexcept
The exact mean of wave over the phase interval [from, to], in any units of whole cycles and of any le...
Definition lfo_sampling.hpp:165
EffectLfoT< float > EffectLfo
Definition lfo.hpp:780
@ p
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
@ w
Definition input_events.hpp:41
Deterministic randomness for modulation: a counter-free PRNG, a Gaussian draw, a stateless purpose-ke...