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

The fixed-cycle splice stretcher. More...

#include <cyclic_stretch.hpp>

Public Member Functions

void prepare (double sample_rate)
 Sizes the capture ring for kCaptureMsMax and the accumulator for the largest grain the parameter ranges permit, so no later set_* can reallocate.
void reset ()
 Never allocates. A zero-initialised instance is already in this state.
void set_cycle_hz (double hz)
void set_grain_periods (int periods)
void set_crossfade_pct (double pct)
void set_crossfade_shape (double shape) noexcept
void set_stretch_ratio (double ratio) noexcept
void set_capture_ms (double ms)
void set_mix (double percent) noexcept
void set_output_db (double db) noexcept
void set_regime (const CyclicStretchRegime &regime)
 Apply one of the two documented regimes in a single call.
int cycle_samples () const noexcept
 L — cycle length in samples.
int grain_samples () const noexcept
 N — grain length in samples.
int crossfade_samples () const noexcept
 X — splice overlap in samples.
int hop_samples () const noexcept
 S — synthesis hop in samples.
double flutter_hz () const noexcept
 The metallic flutter fundamental, fs / S, in Hz.
long long capture_window_samples () const noexcept
 The active capture window in samples — what the falling-behind clamp folds against, which is the configured capture_ms, not the allocation.
double sample_rate () const noexcept
double stretch_ratio () const noexcept
double crossfade_shape () const noexcept
int latency_samples () const noexcept
 The initial fill, reported exactly (series law 5).
double worst_case_gain () const noexcept
 The proven bound on |out| / max|in| — see the file doc block.
long long schedule_input_position (long long out_pos) const noexcept
 The splice schedule at a given OUTPUT position — no clamping, no buffer state, a pure function of the resolved lengths and the ratio.
long long grain_input_position (long long g) const noexcept
 The schedule at grain index g, valid while the resolved lengths hold still — out_pos(g) = g·S only as long as S has not moved.
long long grain_count () const noexcept
 Number of grains emitted since reset().
long long next_grain_out_pos () const noexcept
 The next grain's output position, in absolute output samples.
long long read_position () const noexcept
 The live read cursor, in absolute INPUT samples — where the last grain actually read from after the bounded-ring fold.
long long total_captured () const noexcept
 Input samples captured since reset().
void process (const SampleType *in, SampleType *out, int n)
 One block.

Static Public Attributes

static constexpr double kCycleHzMin = 20.0
 The assumed cycle pitch — the user-tuned "cycle" of the control surface.
static constexpr double kCycleHzMax = 2000.0
static constexpr double kCycleHzDefault = 200.0
static constexpr int kGrainPeriodsMin = 1
 Whole cycles per grain. [design parameter] default 1, range 1 .. 16.
static constexpr int kGrainPeriodsMax = 16
static constexpr int kGrainPeriodsDefault = 1
static constexpr double kCrossfadePctMin = 1.0
 Splice overlap as a percentage of the grain.
static constexpr double kCrossfadePctMax = 50.0
static constexpr double kCrossfadePctDefault = 25.0
static constexpr double kCrossfadeShapeDefault = 1.0
 0 = equal-gain (linear), 1 = equal-power (cosine), blended in between.
static constexpr double kStretchRatioMin = 0.25
 Output duration over input duration.
static constexpr double kStretchRatioMax = 4.0
static constexpr double kStretchRatioDefault = 1.0
static constexpr double kCaptureMsMin = 250.0
 Capture ring capacity in ms.
static constexpr double kCaptureMsMax = 8000.0
static constexpr double kCaptureMsDefault = 2000.0
static constexpr double kMixDefault = 100.0
 Dry/wet in percent; the dry path is delayed by N so the blend is phase-honest.
static constexpr double kOutputDbMin = -24.0
 Post-gain trim, in dB.
static constexpr double kOutputDbMax = 12.0
static constexpr double kOutputDbDefault = 0.0
static constexpr int kMinCycleSamples = 4
 Floor on L.
static constexpr int kMinHopSamples = 2
 Floor on S.
static constexpr double kWorstCaseGain = 1.41421356237309504880
 The proven splice bound.

Detailed Description

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

The fixed-cycle splice stretcher.

Member Function Documentation

◆ capture_window_samples()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::capture_window_samples ( ) const
inlinenoexcept

The active capture window in samples — what the falling-behind clamp folds against, which is the configured capture_ms, not the allocation.

◆ crossfade_samples()

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::crossfade_samples ( ) const
inlinenoexcept

X — splice overlap in samples.

◆ crossfade_shape()

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::crossfade_shape ( ) const
inlinenoexcept

◆ cycle_samples()

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::cycle_samples ( ) const
inlinenoexcept

L — cycle length in samples.

◆ flutter_hz()

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::flutter_hz ( ) const
inlinenoexcept

The metallic flutter fundamental, fs / S, in Hz.

◆ grain_count()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::grain_count ( ) const
inlinenoexcept

Number of grains emitted since reset().

◆ grain_input_position()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::grain_input_position ( long long g) const
inlinenoexcept

The schedule at grain index g, valid while the resolved lengths hold still — out_pos(g) = g·S only as long as S has not moved.

The live path uses the running cursor below instead, for the reason given at grain_out_pos_.

◆ grain_samples()

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::grain_samples ( ) const
inlinenoexcept

N — grain length in samples.

◆ hop_samples()

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::hop_samples ( ) const
inlinenoexcept

S — synthesis hop in samples.

◆ latency_samples()

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

The initial fill, reported exactly (series law 5).

This is the only meaningful latency a time-scaling process has: for r ≠ 1 there is no steady-state constant group delay to report, because the output is not a delayed copy of the input at all.

◆ next_grain_out_pos()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::next_grain_out_pos ( ) const
inlinenoexcept

The next grain's output position, in absolute output samples.

◆ prepare()

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

Sizes the capture ring for kCaptureMsMax and the accumulator for the largest grain the parameter ranges permit, so no later set_* can reallocate.

The only method that allocates.

◆ process()

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

One block.

Allocation-free, lock-free, and independent of block size: every decision is driven by absolute counters, so splitting a render into different block sizes cannot change a sample of it.

◆ read_position()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::read_position ( ) const
inlinenoexcept

The live read cursor, in absolute INPUT samples — where the last grain actually read from after the bounded-ring fold.

Exposed because the fold is the one part of the design a rendered output cannot show directly: "the stretch is still stretching after ten seconds" and "the stretch quietly became 1× four seconds ago" produce very similar-looking audio, and only the cursor distinguishes them.

◆ reset()

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

Never allocates. A zero-initialised instance is already in this state.

◆ sample_rate()

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::sample_rate ( ) const
inlinenoexcept

◆ schedule_input_position()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::schedule_input_position ( long long out_pos) const
inlinenoexcept

The splice schedule at a given OUTPUT position — no clamping, no buffer state, a pure function of the resolved lengths and the ratio.

Exposed so the snap rule can be asserted against its closed form directly, rather than inferred from a render where the bounded-ring clamps have already folded it.

◆ set_capture_ms()

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

◆ set_crossfade_pct()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_crossfade_pct ( double pct)
inline

◆ set_crossfade_shape()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_crossfade_shape ( double shape)
inlinenoexcept

◆ set_cycle_hz()

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

◆ set_grain_periods()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_grain_periods ( int periods)
inline

◆ set_mix()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_mix ( double percent)
inlinenoexcept

◆ set_output_db()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_output_db ( double db)
inlinenoexcept

◆ set_regime()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_regime ( const CyclicStretchRegime & regime)
inline

Apply one of the two documented regimes in a single call.

◆ set_stretch_ratio()

template<typename SampleType = float>
void pulp::signal::CyclicStretchT< SampleType >::set_stretch_ratio ( double ratio)
inlinenoexcept

◆ stretch_ratio()

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::stretch_ratio ( ) const
inlinenoexcept

◆ total_captured()

template<typename SampleType = float>
long long pulp::signal::CyclicStretchT< SampleType >::total_captured ( ) const
inlinenoexcept

Input samples captured since reset().

◆ worst_case_gain()

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::worst_case_gain ( ) const
inlinenoexcept

The proven bound on |out| / max|in| — see the file doc block.

Returns the bound for the CURRENT shape; kWorstCaseGain is the bound over all shapes and is what a registry cites.

Member Data Documentation

◆ kCaptureMsDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCaptureMsDefault = 2000.0
staticconstexpr

◆ kCaptureMsMax

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCaptureMsMax = 8000.0
staticconstexpr

◆ kCaptureMsMin

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCaptureMsMin = 250.0
staticconstexpr

Capture ring capacity in ms.

Bounds how far a long stretch may fall behind before it folds forward, and therefore bounds the memory. [design parameter] default 2000, range 250 .. 8000, log.

◆ kCrossfadePctDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCrossfadePctDefault = 25.0
staticconstexpr

◆ kCrossfadePctMax

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCrossfadePctMax = 50.0
staticconstexpr

◆ kCrossfadePctMin

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCrossfadePctMin = 1.0
staticconstexpr

Splice overlap as a percentage of the grain.

The 50 % ceiling is structural, not taste: above it three grains would overlap at once, which breaks both the two-term OLA and the √2 bound derived from it. [design parameter] default 25, range 1 .. 50.

◆ kCrossfadeShapeDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCrossfadeShapeDefault = 1.0
staticconstexpr

0 = equal-gain (linear), 1 = equal-power (cosine), blended in between.

[design parameter] default 1, range 0 .. 1.

◆ kCycleHzDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCycleHzDefault = 200.0
staticconstexpr

◆ kCycleHzMax

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCycleHzMax = 2000.0
staticconstexpr

◆ kCycleHzMin

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kCycleHzMin = 20.0
staticconstexpr

The assumed cycle pitch — the user-tuned "cycle" of the control surface.

Not tracked, never inferred: setting it away from the material's fundamental is what produces the detune buzz, so tracking it would delete the effect. [design parameter] default 200 Hz, range 20 .. 2000 Hz, log.

◆ kGrainPeriodsDefault

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::kGrainPeriodsDefault = 1
staticconstexpr

◆ kGrainPeriodsMax

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::kGrainPeriodsMax = 16
staticconstexpr

◆ kGrainPeriodsMin

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::kGrainPeriodsMin = 1
staticconstexpr

Whole cycles per grain. [design parameter] default 1, range 1 .. 16.

◆ kMinCycleSamples

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::kMinCycleSamples = 4
staticconstexpr

Floor on L.

The smallest cycle length that leaves the splice (minimum X = 1) and the grain interior as distinct, non-degenerate regions. [design parameter, fixed] 4 samples — an engineering floor, not a tuned value, so it carries no range.

◆ kMinHopSamples

template<typename SampleType = float>
int pulp::signal::CyclicStretchT< SampleType >::kMinHopSamples = 2
staticconstexpr

Floor on S.

The smallest synthesis hop for which consecutive out_pos grid points are distinguishable samples. [design parameter, fixed] 2 samples — likewise an engineering floor.

◆ kMixDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kMixDefault = 100.0
staticconstexpr

Dry/wet in percent; the dry path is delayed by N so the blend is phase-honest.

[design parameter] default 100, range 0 .. 100.

◆ kOutputDbDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kOutputDbDefault = 0.0
staticconstexpr

◆ kOutputDbMax

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kOutputDbMax = 12.0
staticconstexpr

◆ kOutputDbMin

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kOutputDbMin = -24.0
staticconstexpr

Post-gain trim, in dB.

Its positive half exists so a patch can pull back the +3.01 dB equal-power splice boost without pretending it is not there. [design parameter] default 0, range −24 .. +12 dB.

◆ kStretchRatioDefault

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kStretchRatioDefault = 1.0
staticconstexpr

◆ kStretchRatioMax

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kStretchRatioMax = 4.0
staticconstexpr

◆ kStretchRatioMin

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kStretchRatioMin = 0.25
staticconstexpr

Output duration over input duration.

1.0 is NOT a bypass — grains are still spliced on the grid, so identity still carries the flutter, which is faithful to a machine whose 100 % setting was never transparent. A true bypass is mix = 0. [design parameter] default 1.0, range 0.25 .. 4.0, log.

◆ kWorstCaseGain

template<typename SampleType = float>
double pulp::signal::CyclicStretchT< SampleType >::kWorstCaseGain = 1.41421356237309504880
staticconstexpr

The proven splice bound.

See the file doc block for the derivation; this is not a measured maximum.


The documentation for this class was generated from the following file: