Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
lfo_sampling.hpp File Reference

How to SAMPLE an LFO waveform without aliasing, for each kind of consumer. More...

#include <pulp/signal/osc/blep.hpp>
#include <cmath>
#include <cstdint>

Go to the source code of this file.

Classes

struct  pulp::signal::LfoShapeParams
 The shape parameters the deterministic waves take. More...

Namespaces

namespace  pulp
namespace  pulp::signal
namespace  pulp::signal::detail
 Round-2 effect-wave vocabulary.

Enumerations

enum class  pulp::signal::LfoWave : std::uint8_t {
  pulp::signal::sine , pulp::signal::triangle , pulp::signal::saw_up , pulp::signal::saw_down ,
  pulp::signal::square , pulp::signal::sample_hold , pulp::signal::smooth_random
}
 The effect-wave vocabulary, shared by these functions and by EffectLfoT (lfo.hpp), whose set_wave(LfoWave) overload selects the effect convention. More...

Functions

constexpr bool pulp::signal::lfo_wave_is_periodic (LfoWave wave) noexcept
 True for the waves that are periodic functions of phase (everything but the two random waves).
double pulp::signal::detail::wrap01 (double x) noexcept
double pulp::signal::detail::clamp_open (double x) noexcept
double pulp::signal::detail::wave_integral_in_cycle (LfoWave wave, double x, const LfoShapeParams &params) noexcept
 The integral of one wave over [0, x] for x in [0, 1].
double pulp::signal::detail::wave_integral_per_cycle (LfoWave wave, const LfoShapeParams &params) noexcept
 The integral over one whole cycle: zero for every wave but the square.
double pulp::signal::lfo_wave (LfoWave wave, double phase, const LfoShapeParams &params={}) noexcept
 The trivial value of wave at phase (any real number of cycles), in [-1, 1].
double pulp::signal::lfo_wave_integral (LfoWave wave, double phase, const LfoShapeParams &params={}) noexcept
 The cumulative integral of wave from phase 0 to phase, in value x cycles.
double pulp::signal::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 length: the band-limited value for a consumer that takes one value per interval.
double pulp::signal::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 phase step of increment (cycles).

Variables

constexpr double pulp::signal::detail::kTwoPi = 6.283185307179586477

Detailed Description

How to SAMPLE an LFO waveform without aliasing, for each kind of consumer.

An LFO is a continuous function of phase. What a plugin can do with it depends on how often the destination can take a new value:

  • Once per block / per hop – a spectral mask, a filter restage, a parameter the host reads at block rate. Reading the waveform at one phase per block samples it at the block rate, and a cycle shorter than two blocks aliases: a 2 kHz square sampled at 187.5 Hz is a random- looking stepper, not a 2 kHz anything. The band-limited value for such a consumer is the waveform's MEAN over the block's phase interval (lfo_wave_mean): a box filter one block wide, whose first null sits at the block rate, before the decimation to block rate. Below the block Nyquist it is the value at the block's centre to within the box's own rolloff (-0.9 dB at a quarter of the block rate); above it the swing collapses to the waveform's average instead of folding. Closed form, so a 2 kHz LFO costs the same as a 0.2 Hz one.
  • Every sample – a gain stage, a per-sample smoother, AM. Here the waveform itself must be band-limited at audio rates: a trivial square or saw folds its harmonics above Nyquist straight into the band. The per-sample value with the polyBLEP (step) and polyBLAMP (slope) corrections of osc/blep.hpp is lfo_wave_bandlimited; a sine needs none. At sub-audio rates the correction only touches the two samples straddling each corner, so a slow LFO costs two comparisons a sample and reads exactly as the trivial waveform elsewhere.
  • For display – the trivial point value, lfo_wave.

lfo_wave_integral is the cumulative integral the mean is built from, so a consumer with its own interval bookkeeping can take differences itself.

Waveforms are the effect vocabulary LfoWave. A square's pulse width and a triangle's peak position are parameters; the triangle starts at -1, peaks at +1 at triangle_peak, and returns to -1 at the cycle's end (the toolkit LfoT law; the effect lane's triangle is this one a quarter cycle later). The two random waves are not functions of phase: they belong to LfoT's own state, and every function here returns 0 for them.

RT contract: pure functions, no allocation, no state.