Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::signal::detail::BasicLfoT< SampleType, Convention > Class Template Reference

Shared implementation selected by the two explicit public LFO types below. More...

#include <lfo.hpp>

Public Types

enum class  Wave : std::uint8_t {
  sine , triangle , saw_up , saw_down ,
  square , sh_random , smooth_random
}
enum class  Mode : std::uint8_t { free , retrig , one_shot }
 free runs from reset() and ignores retrigger() — that is what "free running" means, and a free LFO that silently restarts on note-on is the single most common LFO bug. More...
enum class  FadeCurve : std::uint8_t { linear , quadratic }
enum class  Stage : std::uint8_t {
  delay , fade_in , sustain , fade_out ,
  done
}

Public Member Functions

void prepare (double sample_rate)
void reset ()
 Rewind to a fresh start: phase 0, lifecycle restarted, random streams re-seeded.
void retrigger ()
 Restart phase and the delay/fade lifecycle.
void set_rate_hz (double hz)
 Free-running rate.
void set_period_samples (double samples)
 Synced use.
double rate_hz () const
double period_samples () const
void set_wave (Wave w)
void set_wave (LfoWave w)
Wave wave () const
LfoWave wave () const
void set_shape_morph (float m)
 Continuous blend across sine -> triangle -> saw_up -> square as m runs 0 -> 3, by crossfading the adjacent pair.
void set_shape_morph_enabled (bool on)
bool shape_morph_enabled () const
void set_pulse_width (float pw)
 Duty cycle of the square wave.
void set_tri_bias (float b)
 Rise/fall skew of the triangle.
void set_random_blend (float b)
 Crossfade the chosen waveform against a per-cycle sample-and-hold random value — "how random is this LFO" as one control.
void set_random_segments (int n)
 Targets latched per cycle in smooth_random.
void set_seed (std::uint32_t s)
 Seed for the random waveforms and the random blend.
void set_phase_offset (double cycles01)
 Output phase offset in cycles (0..1); present a 0-360 degree control.
void set_stereo_offset (double cycles01)
 Offset of the right output in next_stereo(), in cycles.
void set_delay_ms (double ms)
 Silence (a neutral output) before the LFO starts.
void set_fade_in_ms (double ms)
 Depth ramps 0 -> 1 over this time once the delay expires.
void set_fade_out_ms (double ms, bool enabled)
 Depth ramps back to 0 after the repeat count is exhausted.
void set_fade_curve (FadeCurve c)
void set_repeat_count (int n)
 0 = run forever.
void set_mode (Mode m)
Mode mode () const
void set_band_limited (bool on)
 Band-limit the deterministic waves at audio rates: the triangle, saws and square take the polyBLEP / polyBLAMP corrections of lfo_wave_bandlimited at every corner.
bool band_limited () const
SampleType next_mean (int samples)
 The mean of the next samples outputs, advancing by that many samples: the band-limited value for a consumer that takes ONE value per block (a spectral mask, a filter restage).
Stage stage () const
bool finished () const
long long cycles_completed () const
double phase () const
SampleType next ()
 Bipolar output in [-1, 1], scaled by the lifecycle depth envelope.
SampleType next_unipolar ()
 Unipolar output in [0, 1].
void next_stereo (SampleType &left, SampleType &right)
 The stereo pair: the same LFO evaluated at phase and phase + stereo_offset.
void next_quadrature (SampleType &sine_out, SampleType &cosine_out)
 Exact sine/cosine pair at the current phase, regardless of the selected waveform.

Static Public Attributes

static constexpr bool kEffectConvention = Convention == LfoConvention::effect
static constexpr double kMinRateHz = 0.001
 Rate floor.
static constexpr double kMaxRateFraction = 0.45
 Rate ceiling as a fraction of the sample rate.
static constexpr double kMaxRateHz = 200.0
 Ceiling of the bounded effect-LFO lane.
static constexpr int kDefaultRandomSegments = 4
 Suggested segment count for the N-segment smooth-random mode.
static constexpr int kMaxRandomSegments = 16
static constexpr int kMaxRepeatCount = 128
 Upper bound on set_repeat_count().

Detailed Description

template<typename SampleType, LfoConvention Convention>
class pulp::signal::detail::BasicLfoT< SampleType, Convention >

Shared implementation selected by the two explicit public LFO types below.

Keeping the convention in this internal type preserves the one-parameter public LfoT template identity and makes effect behavior impossible to select through precision or setter history.

Member Enumeration Documentation

◆ FadeCurve

template<typename SampleType, LfoConvention Convention>
enum class pulp::signal::detail::BasicLfoT::FadeCurve : std::uint8_t
strong
Enumerator
linear 
quadratic 

◆ Mode

template<typename SampleType, LfoConvention Convention>
enum class pulp::signal::detail::BasicLfoT::Mode : std::uint8_t
strong

free runs from reset() and ignores retrigger() — that is what "free running" means, and a free LFO that silently restarts on note-on is the single most common LFO bug.

retrig restarts phase and the lifecycle on every trigger. one_shot is retrig with an implied repeat count of 1 when none is set.

Enumerator
free 
retrig 
one_shot 

◆ Stage

template<typename SampleType, LfoConvention Convention>
enum class pulp::signal::detail::BasicLfoT::Stage : std::uint8_t
strong
Enumerator
delay 
fade_in 
sustain 
fade_out 
done 

◆ Wave

template<typename SampleType, LfoConvention Convention>
enum class pulp::signal::detail::BasicLfoT::Wave : std::uint8_t
strong
Enumerator
sine 
triangle 
saw_up 
saw_down 
square 
sh_random 
smooth_random 

Member Function Documentation

◆ band_limited()

template<typename SampleType, LfoConvention Convention>
bool pulp::signal::detail::BasicLfoT< SampleType, Convention >::band_limited ( ) const
inline

◆ cycles_completed()

template<typename SampleType, LfoConvention Convention>
long long pulp::signal::detail::BasicLfoT< SampleType, Convention >::cycles_completed ( ) const
inline

◆ finished()

template<typename SampleType, LfoConvention Convention>
bool pulp::signal::detail::BasicLfoT< SampleType, Convention >::finished ( ) const
inline

◆ mode()

template<typename SampleType, LfoConvention Convention>
Mode pulp::signal::detail::BasicLfoT< SampleType, Convention >::mode ( ) const
inline

◆ next()

template<typename SampleType, LfoConvention Convention>
SampleType pulp::signal::detail::BasicLfoT< SampleType, Convention >::next ( )
inline

Bipolar output in [-1, 1], scaled by the lifecycle depth envelope.

◆ next_mean()

template<typename SampleType, LfoConvention Convention>
SampleType pulp::signal::detail::BasicLfoT< SampleType, Convention >::next_mean ( int samples)
inline

The mean of the next samples outputs, advancing by that many samples: the band-limited value for a consumer that takes ONE value per block (a spectral mask, a filter restage).

Closed form (lfo_wave_mean) while the LFO sustains a deterministic wave, so a cycle shorter than the block collapses to its average instead of aliasing and a long block costs the same as a short one; a fading, delayed or random LFO, or a block in which a repeat limit would end the LFO, averages next() over the block instead, so the lifecycle reads exactly as it does sample by sample. Zero samples read the current value without advancing. The closed form integrates over the samples' own centres, so for a curved wave it is the discrete average to within the midpoint rule's error, and for a square or saw to within one sample's share of the block.

◆ next_quadrature()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::next_quadrature ( SampleType & sine_out,
SampleType & cosine_out )
inline

Exact sine/cosine pair at the current phase, regardless of the selected waveform.

Orthogonal by construction, which is what single-sideband and barberpole effects need — a "quadrature" pair built by offsetting a triangle or a square is not orthogonal and the sidebands do not cancel.

◆ next_stereo()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::next_stereo ( SampleType & left,
SampleType & right )
inline

The stereo pair: the same LFO evaluated at phase and phase + stereo_offset.

A zero offset produces bit-identical channels for every waveform, including the random ones — the second latch stream is only engaged when there is actually an offset to decorrelate.

With a nonzero offset the random waveforms give the right channel its own decorrelated latch stream rather than a time-shifted copy of the left, because a time-shifted random stream would require values the generator has not drawn yet. Two decorrelated random walks locked to one rate is also the more useful stereo behavior.

◆ next_unipolar()

template<typename SampleType, LfoConvention Convention>
SampleType pulp::signal::detail::BasicLfoT< SampleType, Convention >::next_unipolar ( )
inline

Unipolar output in [0, 1].

During the delay and after the fade out this sits at 0.5 — the neutral value for a unipolar destination, which is what "silence" means for one.

◆ period_samples()

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::period_samples ( ) const
inline

◆ phase()

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::phase ( ) const
inline

◆ prepare()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::prepare ( double sample_rate)
inline

◆ rate_hz()

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::rate_hz ( ) const
inline

◆ reset()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::reset ( )
inline

Rewind to a fresh start: phase 0, lifecycle restarted, random streams re-seeded.

Deterministic — two reset() LFOs with the same seed produce bit-identical output forever.

◆ retrigger()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::retrigger ( )
inline

Restart phase and the delay/fade lifecycle.

Ignored in free mode.

The random streams deliberately do not re-seed here: a re-seeding retrigger would make every note's "random" modulation identical, which is the opposite of what a random LFO is for. The whole render stays reproducible because reset() is the only seeding point.

◆ set_band_limited()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_band_limited ( bool on)
inline

Band-limit the deterministic waves at audio rates: the triangle, saws and square take the polyBLEP / polyBLAMP corrections of lfo_wave_bandlimited at every corner.

Off by default, so an existing patch reads bit for bit as before; on, a slow LFO differs from the trivial waveform only on the two samples straddling each corner, and an audio-rate one no longer folds its harmonics into the band. The sine and the random waves are unchanged either way.

◆ set_delay_ms()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_delay_ms ( double ms)
inline

Silence (a neutral output) before the LFO starts.

Phase does not advance during the delay, so the wave always begins at its own start.

◆ set_fade_curve()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_fade_curve ( FadeCurve c)
inline

◆ set_fade_in_ms()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_fade_in_ms ( double ms)
inline

Depth ramps 0 -> 1 over this time once the delay expires.

◆ set_fade_out_ms()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_fade_out_ms ( double ms,
bool enabled )
inline

Depth ramps back to 0 after the repeat count is exhausted.

◆ set_mode()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_mode ( Mode m)
inline

◆ set_period_samples()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_period_samples ( double samples)
inline

Synced use.

Tempo-to-period conversion is the caller's job so one transport read feeds every synced object in the plugin; units::division_to_samples() does the arithmetic.

◆ set_phase_offset()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_phase_offset ( double cycles01)
inline

Output phase offset in cycles (0..1); present a 0-360 degree control.

◆ set_pulse_width()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_pulse_width ( float pw)
inline

Duty cycle of the square wave.

◆ set_random_blend()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_random_blend ( float b)
inline

Crossfade the chosen waveform against a per-cycle sample-and-hold random value — "how random is this LFO" as one control.

Works with every wave, including the random ones.

◆ set_random_segments()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_random_segments ( int n)
inline

Targets latched per cycle in smooth_random.

1 is the plain one-target-per-cycle glide; higher values latch N targets per cycle and cosine-glide between them at N times the rate, which wanders instead of swinging. Audibly a different animal, not just a faster one.

◆ set_rate_hz()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_rate_hz ( double hz)
inline

Free-running rate.

The full range is legal, including audio rate; a caller mapping this to a knob should use units::taper_log(). The requested value is kept separately from the effective one, so a rate clamped by a low sample rate is restored by a later prepare() at a higher one instead of being silently lost.

◆ set_repeat_count()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_repeat_count ( int n)
inline

0 = run forever.

Otherwise the LFO completes N cycles and then fades out or stops. Repeat 1 plus a fade out is how you get a shaped one-shot swell out of an oscillator.

◆ set_seed()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_seed ( std::uint32_t s)
inline

Seed for the random waveforms and the random blend.

Applied at the next reset().

◆ set_shape_morph()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_shape_morph ( float m)
inline

Continuous blend across sine -> triangle -> saw_up -> square as m runs 0 -> 3, by crossfading the adjacent pair.

Enables morph mode, which overrides set_wave(). At integer positions the output is bit-identical to the corresponding pure wave.

◆ set_shape_morph_enabled()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_shape_morph_enabled ( bool on)
inline

◆ set_stereo_offset()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_stereo_offset ( double cycles01)
inline

Offset of the right output in next_stereo(), in cycles.

Present a +/-180 degree control; 0.25 is quadrature.

◆ set_tri_bias()

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_tri_bias ( float b)
inline

Rise/fall skew of the triangle.

0 is symmetric; +1 pushes the peak to the end of the cycle (a ramp up with a cliff), -1 to the start.

◆ set_wave() [1/2]

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_wave ( LfoWave w)
inline

◆ set_wave() [2/2]

template<typename SampleType, LfoConvention Convention>
void pulp::signal::detail::BasicLfoT< SampleType, Convention >::set_wave ( Wave w)
inline

◆ shape_morph_enabled()

template<typename SampleType, LfoConvention Convention>
bool pulp::signal::detail::BasicLfoT< SampleType, Convention >::shape_morph_enabled ( ) const
inline

◆ stage()

template<typename SampleType, LfoConvention Convention>
Stage pulp::signal::detail::BasicLfoT< SampleType, Convention >::stage ( ) const
inline

◆ wave() [1/2]

template<typename SampleType, LfoConvention Convention>
LfoWave pulp::signal::detail::BasicLfoT< SampleType, Convention >::wave ( ) const
inline

◆ wave() [2/2]

template<typename SampleType, LfoConvention Convention>
Wave pulp::signal::detail::BasicLfoT< SampleType, Convention >::wave ( ) const
inline

Member Data Documentation

◆ kDefaultRandomSegments

template<typename SampleType, LfoConvention Convention>
int pulp::signal::detail::BasicLfoT< SampleType, Convention >::kDefaultRandomSegments = 4
staticconstexpr

Suggested segment count for the N-segment smooth-random mode.

The default is 1 (one target per cycle, the plain glide); 4 is the setting that produces the wandering character the mode exists for.

◆ kEffectConvention

template<typename SampleType, LfoConvention Convention>
bool pulp::signal::detail::BasicLfoT< SampleType, Convention >::kEffectConvention = Convention == LfoConvention::effect
staticconstexpr

◆ kMaxRandomSegments

template<typename SampleType, LfoConvention Convention>
int pulp::signal::detail::BasicLfoT< SampleType, Convention >::kMaxRandomSegments = 16
staticconstexpr

◆ kMaxRateFraction

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::kMaxRateFraction = 0.45
staticconstexpr

Rate ceiling as a fraction of the sample rate.

Above Nyquist the phase accumulator aliases into nonsense; 0.45 keeps the "extended" audio-rate mode usable right up to the edge without crossing it.

◆ kMaxRateHz

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::kMaxRateHz = 200.0
staticconstexpr

Ceiling of the bounded effect-LFO lane.

The modulation-toolkit Wave overload keeps its audio-rate ceiling of kMaxRateFraction * fs.

◆ kMaxRepeatCount

template<typename SampleType, LfoConvention Convention>
int pulp::signal::detail::BasicLfoT< SampleType, Convention >::kMaxRepeatCount = 128
staticconstexpr

Upper bound on set_repeat_count().

◆ kMinRateHz

template<typename SampleType, LfoConvention Convention>
double pulp::signal::detail::BasicLfoT< SampleType, Convention >::kMinRateHz = 0.001
staticconstexpr

Rate floor.

One cycle every ~17 minutes; below this the LFO is a constant with extra steps.


The documentation for this class was generated from the following file:
  • /home/runner/work/pulp/pulp/core/signal/include/pulp/signal/lfo.hpp