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

The flanger. More...

#include <flanger.hpp>

Public Types

using Mode = FlangerMode
using Polarity = FlangerPolarity
using Engine = FlangerDelayEngine
using Wave = LfoWave

Public Member Functions

void prepare (double sample_rate)
 Sizes both delay buffers for the widest sweep any mode can ask for and prepares the composed engines.
void reset ()
 Rewinds every buffer, filter, oscillator and generator.
void discard_history () noexcept
 Constant-time audio fault recovery.
void set_mode (Mode mode)
 Takes effect through a kModeSwitchMs transition rather than instantly.
Mode mode () const
void set_polarity (Polarity polarity)
Polarity polarity () const
void set_delay_engine (Engine engine)
Engine delay_engine () const
void set_rate_hz (double hz)
void set_waveform (Wave wave)
void set_stereo_spread (double cycles01)
 Stereo spread in CYCLES of the sweep, in the range [0, 0.5].
void set_depth_ms (double ms)
 Modulation excursion.
double depth_ms () const
void set_center_delay_ms (double ms)
double center_delay_ms () const
void set_offset_ms (double ms)
double offset_ms () const
void set_feedback (double fb)
 Resonance, in the range [-kFbClamp, +kFbClamp].
double feedback () const
void set_mix (double wet01)
 Equal-power dry/wet, in the range [0, 1].
double mix () const
void set_barberpole_shift_hz (double hz)
 Barberpole shift in Hz. Positive climbs, negative descends.
double barberpole_shift_hz () const
void process (const SampleType *in, SampleType *out, int n)
void process_stereo (const SampleType *in_left, const SampleType *in_right, SampleType *out_left, SampleType *out_right, int n)
int latency_samples () const noexcept
 Zero in classic and barberpole modes — the dry path is a direct passthrough and the mix is a gain, not a resampling.
double effective_depth_ms () const
 The excursion actually in force, after the mode's clamp.
double instantaneous_delay_ms (int channel=0) const
 The delay the modulated line was last read at, in ms.
int fixed_delay_samples () const noexcept
 The fixed dry delay in samples, exact and integer.

Static Public Member Functions

static double notch_hz (int k, double delay_ms, Polarity polarity)
 Notch frequencies of the comb at a given delay, from Δf = 1/D.
static double notch_spacing_hz (double delay_ms)
 Spacing between adjacent notches — the reciprocal of the delay, for either polarity.
static constexpr double worst_case_gain ()
 The bound the Forge registry cites (series law 8): the steady-state envelope of a recursive comb at the feedback ceiling.
static void mix_gains (double wet01, double &dry_gain, double &wet_gain)
 The dry and wet weights the shipped equal-power law produces at a given mix.

Static Public Attributes

static constexpr double kFbClamp = 0.97
 Feedback ceiling.
static constexpr double kLoopElementGainBound = 1.012
 Supremum of every non-coefficient element in the feedback loop, with headroom over the measured values quoted in the file doc block.
static constexpr double kDcBlockHz = 5.0
 Corner of the DC blocker in the feedback return.
static constexpr double kDepthMinMs = 0.02
 Modulation excursion bounds. [design parameter] 0.02 .. 4.0 ms.
static constexpr double kDepthMaxMs = 4.0
static constexpr double kCenterMinMs = 0.1
 Classic-mode comb base delay — the flanging end of the delay-modulation family, deliberately stopping short of chorus territory.
static constexpr double kCenterMaxMs = 10.0
static constexpr double kOffsetMinMs = 0.5
 Through-zero fixed dry delay — the crossing point.
static constexpr double kOffsetMaxMs = 10.0
static constexpr double kRateMinHz = 0.02
 Sweep rate. [design parameter] 0.02 .. 10.0 Hz.
static constexpr double kRateMaxHz = 10.0
static constexpr double kMinClassicDelayMs = 0.05
 Floor the classic-mode delay may never go below.
static constexpr int kInterpGuardSamples = 2
 Fixed extra history the delay buffer reserves so the 4-point kernel's taps never reach past the valid region, however small the delay gets.
static constexpr double kControlSmoothMs = 3.0
 De-zippering time for the delay-time target.
static constexpr double kModeSwitchMs = 5.0
 Mode-change transition window.
static constexpr double kBarberpoleShiftMaxHz = 20.0
 Barberpole shift ceiling.
static constexpr double kBbdCharacter = 0.5
 The BBD engine's character amount — how hard its clock jitter and compander are driven.

Detailed Description

template<typename SampleType = float>
class pulp::signal::FlangerT< SampleType >

The flanger.

Member Typedef Documentation

◆ Engine

template<typename SampleType = float>
using pulp::signal::FlangerT< SampleType >::Engine = FlangerDelayEngine

◆ Mode

template<typename SampleType = float>
using pulp::signal::FlangerT< SampleType >::Mode = FlangerMode

◆ Polarity

template<typename SampleType = float>
using pulp::signal::FlangerT< SampleType >::Polarity = FlangerPolarity

◆ Wave

template<typename SampleType = float>
using pulp::signal::FlangerT< SampleType >::Wave = LfoWave

Member Function Documentation

◆ barberpole_shift_hz()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::barberpole_shift_hz ( ) const
inline

◆ center_delay_ms()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::center_delay_ms ( ) const
inline

◆ delay_engine()

template<typename SampleType = float>
Engine pulp::signal::FlangerT< SampleType >::delay_engine ( ) const
inline

◆ depth_ms()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::depth_ms ( ) const
inline

◆ discard_history()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::discard_history ( )
inlinenoexcept

Constant-time audio fault recovery.

Dynamic delay storage is invalidated logically; fixed-capacity DSP state is reset normally.

◆ effective_depth_ms()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::effective_depth_ms ( ) const
inline

The excursion actually in force, after the mode's clamp.

Classic: the sweep is subtracted from the centre at the LFO's trough, so an unclamped depth can drive the delay negative — the catalog's own extremes do it (centre 0.1 ms against depth 4.0 ms asks for −3.9 ms). The excursion is clamped against the centre so D(t) ≥ kMinClassicDelayMs holds by construction.

Through-zero: the sweep is about the offset and must not go negative either, so the excursion is capped at the offset — which is exactly the full 0 .. 2·offset sweep, the maximum musical setting.

◆ feedback()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::feedback ( ) const
inline

◆ fixed_delay_samples()

template<typename SampleType = float>
int pulp::signal::FlangerT< SampleType >::fixed_delay_samples ( ) const
inlinenoexcept

The fixed dry delay in samples, exact and integer.

Zero outside through-zero mode, where the dry path is not delayed at all.

◆ instantaneous_delay_ms()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::instantaneous_delay_ms ( int channel = 0) const
inline

The delay the modulated line was last read at, in ms.

The instrument the sweep tests measure with — the delay-time law is the thing under test, and inferring it from the output spectrum would be measuring the comb and the law at once.

◆ latency_samples()

template<typename SampleType = float>
int pulp::signal::FlangerT< SampleType >::latency_samples ( ) const
inlinenoexcept

Zero in classic and barberpole modes — the dry path is a direct passthrough and the mix is a gain, not a resampling.

In through-zero mode the dry path genuinely carries the fixed offset, so the latency is real and is reported exactly: the offset line is an INTEGER-sample buffer (nothing sweeps it, so it needs no fractional accuracy), which is what keeps this an exact integer rather than a rounded approximation of a fractional read.

Reported for the TARGET mode the instant set_mode is called, not at the end of the transition window. A host reads latency on the control thread and re-aligns on its own schedule; handing it the outgoing mode's figure for five more milliseconds would give it a number that is already wrong by the time it acts on it.

◆ mix()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::mix ( ) const
inline

◆ mix_gains()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::mix_gains ( double wet01,
double & dry_gain,
double & wet_gain )
inlinestatic

The dry and wet weights the shipped equal-power law produces at a given mix.

Exposed so a test computes its expectation from the law rather than restating a number the law might later disagree with.

◆ mode()

template<typename SampleType = float>
Mode pulp::signal::FlangerT< SampleType >::mode ( ) const
inline

◆ notch_hz()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::notch_hz ( int k,
double delay_ms,
Polarity polarity )
inlinestatic

Notch frequencies of the comb at a given delay, from Δf = 1/D.

The series depends on the polarity: summing puts zeros at the odd half-multiples, differencing at the whole multiples starting at DC.

◆ notch_spacing_hz()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::notch_spacing_hz ( double delay_ms)
inlinestatic

Spacing between adjacent notches — the reciprocal of the delay, for either polarity.

◆ offset_ms()

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::offset_ms ( ) const
inline

◆ polarity()

template<typename SampleType = float>
Polarity pulp::signal::FlangerT< SampleType >::polarity ( ) const
inline

◆ prepare()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::prepare ( double sample_rate)
inline

Sizes both delay buffers for the widest sweep any mode can ask for and prepares the composed engines.

May allocate; nothing else here does.

◆ process()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::process ( const SampleType * in,
SampleType * out,
int n )
inline

◆ process_stereo()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::process_stereo ( const SampleType * in_left,
const SampleType * in_right,
SampleType * out_left,
SampleType * out_right,
int n )
inline

◆ reset()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::reset ( )
inline

Rewinds every buffer, filter, oscillator and generator.

Allocates nothing; a render from here is reproducible to the bit.

◆ set_barberpole_shift_hz()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_barberpole_shift_hz ( double hz)
inline

Barberpole shift in Hz. Positive climbs, negative descends.

◆ set_center_delay_ms()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_center_delay_ms ( double ms)
inline

◆ set_delay_engine()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_delay_engine ( Engine engine)
inline

◆ set_depth_ms()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_depth_ms ( double ms)
inline

Modulation excursion.

The value the engine uses is effective_depth_ms(), which is this clamped against the mode's fixed reference — never the raw request.

◆ set_feedback()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_feedback ( double fb)
inline

Resonance, in the range [-kFbClamp, +kFbClamp].

A negative coefficient is a different resonance colour, not a polarity control — set_polarity owns that.

◆ set_mix()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_mix ( double wet01)
inline

Equal-power dry/wet, in the range [0, 1].

At 0.5 both gains are 1/√2, which is the setting the comb algebra is written for: equal dry and wet weights give exact nulls.

◆ set_mode()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_mode ( Mode mode)
inline

Takes effect through a kModeSwitchMs transition rather than instantly.

Mode is a preset-time control, not an automation lane: it changes the dry path's latency and the wet path's whole delay law at once.

◆ set_offset_ms()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_offset_ms ( double ms)
inline

◆ set_polarity()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_polarity ( Polarity polarity)
inline

◆ set_rate_hz()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_rate_hz ( double hz)
inline

◆ set_stereo_spread()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_stereo_spread ( double cycles01)
inline

Stereo spread in CYCLES of the sweep, in the range [0, 0.5].

The house default is a quarter cycle: far enough apart that the two channels decorrelate into real width. This is a PHASE control, not a waveform-normalized width control: a half-cycle is exact inversion for sine, triangle and square, but not for either saw. Saw still responds to every offset; its stereo width simply does not vary monotonically by the same law as the odd-symmetric shapes because its wrap discontinuity moves between rails.

◆ set_waveform()

template<typename SampleType = float>
void pulp::signal::FlangerT< SampleType >::set_waveform ( Wave wave)
inline

◆ worst_case_gain()

template<typename SampleType = float>
constexpr double pulp::signal::FlangerT< SampleType >::worst_case_gain ( )
inlinestaticconstexpr

The bound the Forge registry cites (series law 8): the steady-state envelope of a recursive comb at the feedback ceiling.

Not the per-pass gain — a host needs to know the module can present ~35× at resonance, and a registry entry of 0.98 would say it can never exceed unity.

Member Data Documentation

◆ kBarberpoleShiftMaxHz

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kBarberpoleShiftMaxHz = 20.0
staticconstexpr

Barberpole shift ceiling.

A few Hz is the musical setting; the ceiling exists so the control cannot be driven into audible ring modulation. [design parameter] default 20 Hz, range 1 .. 50 Hz.

◆ kBbdCharacter

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kBbdCharacter = 0.5
staticconstexpr

The BBD engine's character amount — how hard its clock jitter and compander are driven.

Fixed here rather than exposed: the flanger's parameter surface is about the comb, and a second character axis would duplicate the delay module's own. [design parameter] default 0.5, range 0 .. 1.

◆ kCenterMaxMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kCenterMaxMs = 10.0
staticconstexpr

◆ kCenterMinMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kCenterMinMs = 0.1
staticconstexpr

Classic-mode comb base delay — the flanging end of the delay-modulation family, deliberately stopping short of chorus territory.

[design parameter] 0.1 .. 10.0 ms.

◆ kControlSmoothMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kControlSmoothMs = 3.0
staticconstexpr

De-zippering time for the delay-time target.

The interpolator smooths a FRACTIONAL position; it does nothing about a jump in the target itself, which is what a host automating center_delay_ms produces. [design parameter] default 3 ms, range 1 .. 10 ms.

◆ kDcBlockHz

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kDcBlockHz = 5.0
staticconstexpr

Corner of the DC blocker in the feedback return.

Without it, rounding in the fractional read accumulates a DC component over a long high-feedback render, and there is no saturator here to bound it. [design parameter] default 5 Hz, range 2 .. 10 Hz.

◆ kDepthMaxMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kDepthMaxMs = 4.0
staticconstexpr

◆ kDepthMinMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kDepthMinMs = 0.02
staticconstexpr

Modulation excursion bounds. [design parameter] 0.02 .. 4.0 ms.

◆ kFbClamp

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kFbClamp = 0.97
staticconstexpr

Feedback ceiling.

The clamp carries the entire stability burden here, because the loop deliberately has no saturator to self-limit. [design parameter] default 0.97, range 0.90 .. 0.97.

◆ kInterpGuardSamples

template<typename SampleType = float>
int pulp::signal::FlangerT< SampleType >::kInterpGuardSamples = 2
staticconstexpr

Fixed extra history the delay buffer reserves so the 4-point kernel's taps never reach past the valid region, however small the delay gets.

[design parameter] default 2 samples, range 1 .. 4 samples.

◆ kLoopElementGainBound

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kLoopElementGainBound = 1.012
staticconstexpr

Supremum of every non-coefficient element in the feedback loop, with headroom over the measured values quoted in the file doc block.

The spec's calibration table calls this kInterpGainBound and attributes it to the interpolator. It is renamed because that attribution is measurably wrong: the 4-point Lagrange kernel's magnitude never exceeds 1.000 anywhere in the band, at any fractional offset. The element that can exceed unity is the feedback DC blocker, and the BBD engine's small-signal peak sits just above it. The shipped default is unchanged so the registry figure does not move. [design parameter] default 1.012, range 1.000 .. 1.020.

◆ kMinClassicDelayMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kMinClassicDelayMs = 0.05
staticconstexpr

Floor the classic-mode delay may never go below.

The excursion is clamped against the centre rather than the caller's request, so D(t) ≥ kMinClassicDelayMs holds by construction for every setting the catalog can produce. [design parameter] default 0.05 ms, range 0.01 .. 0.5 ms.

◆ kModeSwitchMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kModeSwitchMs = 5.0
staticconstexpr

Mode-change transition window.

Short enough to feel immediate, long enough that the per-sample gain step stays far below anything audible as a click. [design parameter] default 5 ms, range 1 .. 20 ms.

◆ kOffsetMaxMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kOffsetMaxMs = 10.0
staticconstexpr

◆ kOffsetMinMs

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kOffsetMinMs = 0.5
staticconstexpr

Through-zero fixed dry delay — the crossing point.

[design parameter] 0.5 .. 10.0 ms.

◆ kRateMaxHz

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kRateMaxHz = 10.0
staticconstexpr

◆ kRateMinHz

template<typename SampleType = float>
double pulp::signal::FlangerT< SampleType >::kRateMinHz = 0.02
staticconstexpr

Sweep rate. [design parameter] 0.02 .. 10.0 Hz.


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