Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::signal::detail Namespace Reference

Round-2 effect-wave vocabulary. More...

Classes

class  StreamingAnalysisWindowT
struct  EnvelopeStageClock
 Sample counter for one envelope stage. More...
class  EnvelopeEngine
 The DAHDSR superset state machine. More...
struct  FftRealSplit
 The packed split-complex form of a real transform, for Pulp's own frequency-domain stages (the overlap-add ConvolverT). More...
struct  FractionalDelaySplit
class  BasicLfoT
 Shared implementation selected by the two explicit public LFO types below. More...
class  NonlinearOversamplingT
class  FixedHalfBandFir65
class  LinearPhaseOversamplingStage2x
class  FixedOrderFirBranch
 One polyphase branch of a block 2x stage: a direct-form FIR whose every output is the taps summed oldest-first, one multiply-add each (fused where the hardware has it; see fir_multiply_add()). More...
class  LinearPhaseOversamplingBlockStage2x
 The block form of LinearPhaseOversamplingStage2x, for OversamplerT: the same prototype and the same polyphase split, run a block at a time. More...
struct  SaturatorShapeMath
 The waveshaper family's three closed forms per member: the curve f, its derivative f' (needed for the bias normaliser), and its antiderivative F1 (needed for ADAA). More...
class  DualTapResonatorT
 Dual-tap TPT state-variable resonator (Zavalishin topology, identical to SvfT) returning the highpass and lowpass outputs from ONE tick. More...
struct  CapacityLayout
struct  ReservationToken
class  ProgramBank
class  Segmenter
class  ReservationLedger
class  OutputSplicer
class  SlicedFftPlanT
 Resumable in-place radix-2 complex FFT (Cooley & Tukey 1965). More...
struct  ZeroLatencyConvolverGroup
struct  ZeroLatencyConvolverCell
struct  ZeroLatencyConvolverOnePole

Concepts

concept  MirroredHistorySample

Enumerations

enum class  AnalysisWindow : std::uint8_t { rectangular , symmetric_hann }
enum class  LfoConvention : std::uint8_t { toolkit , effect }
 Selects the waveform/rate convention at the type boundary. More...

Functions

template<typename SampleType, std::size_t MaxChannels>
bool mix_planar_frame (const SampleType *const *channels, std::size_t channel_count, std::size_t frame, bool compute_power, double &mono, double &channel_power) noexcept
int pitch_class_for_frequency (double frequency_hz) noexcept
bool beat_repeat_checked_add (std::int64_t lhs, std::int64_t rhs, std::int64_t &result) noexcept
bool beat_repeat_checked_subtract (std::int64_t lhs, std::int64_t rhs, std::int64_t &result) noexcept
bool beat_repeat_ceil_tick (std::int64_t value, std::int64_t quantum, std::int64_t &result) noexcept
constexpr std::size_t convolver_block_size (std::size_t block_size) noexcept
 The block size a convolver prepared for block_size accepts: the request rounded up to the next power of two (the radix-2 FFT needs it), with 0 treated as 1.
template<typename SampleType = float>
std::unique_ptr< ConvolverIrStateT< SampleType > > build_convolver_ir_state (const SampleType *ir, std::size_t ir_length, std::size_t block_size)
 Build a ConvolverIrState from a raw IR.
template<typename SampleType = float>
void migrate_input_history (ConvolverInputHistoryT< SampleType > &from, ConvolverInputHistoryT< SampleType > &to)
 Migrate the recorded input history from from into to, age-aligned.
void apply_diode_model (junction::JunctionPair &network, DiodeModel model)
 The (Is, n) pair for a device family.
void apply_symmetry (junction::JunctionPair &network, double symmetry)
 symmetry ∈ [−1, +1] → the two legs' reciprocal series counts.
template<typename Residual, typename Derivative>
double solve_safeguarded_newton (Residual &&f, Derivative &&df, double lo, double hi, double initial, double alternate, double tolerance, int max_iterations, int &iterations_out)
 One monotone implicit stage, solved by a SAFEGUARDED Newton iteration.
double solve_implicit_flow_stage (const junction::JunctionPair &network, double history_voltage, double history_flow, double source_current, double flow_weight, double c_over_step, double resistance, double initial, int &iterations_out)
 Solves one implicit stage of C·dv/dt = source_current − v/R − i_D(v).
double solve_tr_bdf2 (const junction::JunctionPair &network, double previous_voltage, double previous_source_current, double source_current, double capacitance, double timestep, double resistance, int &iterations_out)
 One L-stable TR-BDF2 step of C·dv/dt = source_current(t) − v/R − i_D(v).
long long envelope_ms_to_samples (double ms, double sample_rate)
template<typename T>
T select_bits (bool condition, T if_true, T if_false) noexcept
 condition ?
template<typename T>
T snap_to_zero_bits (T x) noexcept
 snap_to_zero() as bit operations (see select_bits()).
float tanh_accurate_bits (float x) noexcept
 FastMath::tanh_accurate() with its clamps as bit operations; the same value for every input.
constexpr bool is_power_of_two (int value) noexcept
constexpr std::size_t fft_twiddle_index (int phase_index, int stride) noexcept
template<typename SampleType>
constexpr bool fft_real_split_needs_scratch (std::uint64_t fft_size) noexcept
 Whether a FftT of fft_size points keeps a real_bins()-long complex scratch for the split-spectrum path (FftRealSplit): every size the portable real transform serves, which is all of them except vDSP's float sizes.
template<typename SampleType>
void multiply_packed_split (const SampleType *a_re, const SampleType *a_im, const SampleType *b_re, const SampleType *b_im, SampleType *out_re, SampleType *out_im, int half) noexcept
 Product of two packed split spectra of half = size() / 2 slots each (see FftRealSplit): bin k of out is bin k of a times bin k of b for k = 1..half-1, and slot 0's DC and Nyquist reals multiply separately.
template<typename SampleType>
void multiply_accumulate_packed_split (const SampleType *a_re, const SampleType *a_im, const SampleType *b_re, const SampleType *b_im, SampleType *acc_re, SampleType *acc_im, int half) noexcept
 acc += a * b over packed split spectra, with the product as in multiply_packed_split().
template<typename SampleType>
SampleType fm_bright_band_gain (SampleType base_hz, SampleType sample_rate, FmOperatorAliasPolicy policy) noexcept
template<typename SampleType, std::size_t MaxOperators, typename FrequencyFn, typename PhaseRouteFn, typename FrequencyRouteFn, typename CarrierGainFn, typename OscillatorFn>
SampleType render_fm_operator_sample (std::size_t operator_count, SampleType sample_rate, FmOperatorAliasPolicy alias_policy, std::array< SampleType, MaxOperators > &phases, std::array< SampleType, MaxOperators > &previous, const std::array< SampleType, MaxOperators > &amplitudes, const std::array< SampleType, MaxOperators > &feedback_radians, FrequencyFn &&base_frequency, PhaseRouteFn &&phase_route, FrequencyRouteFn &&frequency_route, CarrierGainFn &&carrier_gain, OscillatorFn &&oscillator) noexcept
 Canonical one-sample-delayed operator evaluator shared by melodic and drum engines.
FractionalDelaySplit split_fractional_delay (double delay_samples) noexcept
template<typename Coefficient>
void halfband_path_response (const HalfBandIirDesign &design, bool path_a, double frequency, std::complex< double > &response, double &group_delay) noexcept
template<typename Coefficient>
HalfBandIirDesign halfband_design_from_paths (std::span< const Coefficient > a, std::span< const Coefficient > b) noexcept
HalfBandIirDesign default_halfband_design () noexcept
double wrap01 (double x) noexcept
double clamp_open (double x) noexcept
double 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 wave_integral_per_cycle (LfoWave wave, const LfoShapeParams &params) noexcept
 The integral over one whole cycle: zero for every wave but the square.
template<typename SampleType>
SampleType precise_stage_curve (SampleType progress, SampleType curve) noexcept
bool valid_alias_policy (NonlinearAliasPolicy policy) noexcept
double triangle_fold (double input) noexcept
double triangle_fold_slope (double input) noexcept
std::vector< double > linear_phase_oversampling_prototype (double passband_edge_fraction, double stopband_db, std::size_t taps)
 The Kaiser-windowed lowpass prototype both linear-phase 2x stage forms split into polyphase branches.
template<typename SampleType>
SampleType fir_multiply_add (SampleType a, SampleType b, SampleType acc) noexcept
 acc + a * b for the block FIR: one fused multiply-add where the hardware has it, a separate multiply and add otherwise.
template<typename Transitioning, typename Sample, typename Block>
void transition_then_block (std::size_t frames, Transitioning &&transitioning, Sample &&sample, Block &&block)
 The block path the cascade EQs share: a design transition (crossfade or coefficient ramp) runs sample by sample through sample(i) while transitioning() holds; once it has finished, the rest of the block goes to block(first, count) in one call.
template<typename SampleType>
bool valid_spectral_band_layout (const SpectralBandLayoutT< SampleType > &layout, SampleType nyquist, SampleType &effective_min, SampleType &effective_max) noexcept
template<typename SampleType>
SampleType band_coordinate (SampleType hz, SampleType min_hz, SampleType max_hz, SpectralBandSpacing spacing) noexcept
template<typename SampleType>
SampleType frequency_at_coordinate (SampleType coordinate, SampleType min_hz, SampleType max_hz, SpectralBandSpacing spacing) noexcept
template<typename SampleType>
SampleType raised_cosine (SampleType position) noexcept
template<typename SampleType>
SampleType band_gain (const SpectralBandLayoutT< SampleType > &layout, std::uint32_t index) noexcept
template<typename SampleType>
std::uint32_t band_index_at_frequency (SampleType hz, SampleType min_hz, SampleType max_hz, SpectralBandSpacing spacing, std::uint32_t band_count) noexcept
template<typename SampleType>
std::vector< SampleType > build_wavetable_band_ceilings (std::size_t num_bands, SampleType reference_sample_rate)
 Shared ceiling plan for synthesized and authored wavetable stacks.
template<typename SampleType, typename HarmonicAmp>
std::vector< SampleType > generate_wavetable (std::size_t length, std::size_t max_harmonic, HarmonicAmp &&harmonic_amp)
template<typename SampleType, typename HarmonicAmp>
std::vector< WavetableEntryT< SampleType > > build_wavetable_band_stack (std::size_t num_bands, std::size_t table_length, SampleType reference_sample_rate, HarmonicAmp &&harmonic_amp)
template<typename SampleType>
SampleType four_term_cosine_window (SampleType phase, SampleType a0, SampleType a1, SampleType a2, SampleType a3)
template<typename SampleType>
SampleType blackman_harris_window (SampleType phase)
template<typename SampleType>
SampleType blackman_nuttall_window (SampleType phase)

Variables

template<std::size_t Inputs, std::size_t Outputs>
constexpr bool valid_audio_matrix_capacity
constexpr double kTwoPi = 6.283185307179586477
constexpr double kNonlinearMinimumSampleRate = 1000.0
constexpr double kNonlinearMaximumSampleRate = 768000.0
template<typename SampleType>
constexpr SampleType kWavetableTwoPi

Detailed Description

Round-2 effect-wave vocabulary.

Returns normalized travel from the start of a segment to its end.

It is intentionally distinct from the modulation toolkit's nested LfoT::Wave: selecting this overload also selects the effect convention whose triangle is sine-aligned (zero at phase 0, positive peak at 0.25) instead of the toolkit convention (-1 at phase 0).

rising selects the musical direction used by exponential/logarithmic labels; it does not constrain the caller's endpoint values.

Enumeration Type Documentation

◆ AnalysisWindow

enum class pulp::signal::detail::AnalysisWindow : std::uint8_t
strong
Enumerator
rectangular 
symmetric_hann 

◆ LfoConvention

enum class pulp::signal::detail::LfoConvention : std::uint8_t
strong

Selects the waveform/rate convention at the type boundary.

The default is the shipped modulation-toolkit contract; Round-2 effects opt into their distinct phase and waveform laws through EffectLfoT below.

Enumerator
toolkit 
effect 

Function Documentation

◆ apply_diode_model()

void pulp::signal::detail::apply_diode_model ( junction::JunctionPair & network,
DiodeModel model )
inline

The (Is, n) pair for a device family.

Honest [design parameter] rows representing a class's published order of magnitude, never a measured part.

[design parameter] silicon Is 1e-14 (range 1e-15 .. 1e-12), n 1.0 (1.0 .. 1.05) [design parameter] germanium Is 1e-6 (range 1e-7 .. 1e-5), n 1.1 (1.0 .. 1.3) [design parameter] led Is 1e-18 (range 1e-19 .. 1e-17), n 2.0 (1.5 .. 2.5)

◆ apply_symmetry()

void pulp::signal::detail::apply_symmetry ( junction::JunctionPair & network,
double symmetry )
inline

symmetry ∈ [−1, +1] → the two legs' reciprocal series counts.

Positive symmetry lengthens the positive leg (turns on later); negative symmetry fades the negative leg out entirely. Both are continuous and both reduce to the matched pair at 0.

◆ band_coordinate()

template<typename SampleType>
SampleType pulp::signal::detail::band_coordinate ( SampleType hz,
SampleType min_hz,
SampleType max_hz,
SpectralBandSpacing spacing )
inlinenoexcept

◆ band_gain()

template<typename SampleType>
SampleType pulp::signal::detail::band_gain ( const SpectralBandLayoutT< SampleType > & layout,
std::uint32_t index )
inlinenoexcept

◆ band_index_at_frequency()

template<typename SampleType>
std::uint32_t pulp::signal::detail::band_index_at_frequency ( SampleType hz,
SampleType min_hz,
SampleType max_hz,
SpectralBandSpacing spacing,
std::uint32_t band_count )
inlinenoexcept

◆ beat_repeat_ceil_tick()

bool pulp::signal::detail::beat_repeat_ceil_tick ( std::int64_t value,
std::int64_t quantum,
std::int64_t & result )
inlinenoexcept

◆ beat_repeat_checked_add()

bool pulp::signal::detail::beat_repeat_checked_add ( std::int64_t lhs,
std::int64_t rhs,
std::int64_t & result )
inlinenoexcept

◆ beat_repeat_checked_subtract()

bool pulp::signal::detail::beat_repeat_checked_subtract ( std::int64_t lhs,
std::int64_t rhs,
std::int64_t & result )
inlinenoexcept

◆ blackman_harris_window()

template<typename SampleType>
SampleType pulp::signal::detail::blackman_harris_window ( SampleType phase)
inline

◆ blackman_nuttall_window()

template<typename SampleType>
SampleType pulp::signal::detail::blackman_nuttall_window ( SampleType phase)
inline

◆ build_convolver_ir_state()

template<typename SampleType = float>
std::unique_ptr< ConvolverIrStateT< SampleType > > pulp::signal::detail::build_convolver_ir_state ( const SampleType * ir,
std::size_t ir_length,
std::size_t block_size )
inline

Build a ConvolverIrState from a raw IR.

This is the expensive path (allocations + N forward FFTs) and must run off the audio thread.

◆ build_wavetable_band_ceilings()

template<typename SampleType>
std::vector< SampleType > pulp::signal::detail::build_wavetable_band_ceilings ( std::size_t num_bands,
SampleType reference_sample_rate )
inline

Shared ceiling plan for synthesized and authored wavetable stacks.

Keeping the geometric ladder in one helper prevents offline-authored tables from drifting away from WavetableT's band-selection contract.

◆ build_wavetable_band_stack()

template<typename SampleType, typename HarmonicAmp>
std::vector< WavetableEntryT< SampleType > > pulp::signal::detail::build_wavetable_band_stack ( std::size_t num_bands,
std::size_t table_length,
SampleType reference_sample_rate,
HarmonicAmp && harmonic_amp )
inline

◆ clamp_open()

double pulp::signal::detail::clamp_open ( double x)
inlinenodiscardnoexcept

◆ convolver_block_size()

std::size_t pulp::signal::detail::convolver_block_size ( std::size_t block_size)
constexprnoexcept

The block size a convolver prepared for block_size accepts: the request rounded up to the next power of two (the radix-2 FFT needs it), with 0 treated as 1.

Every fixed-block convolver uses this one rule so their accepted block sizes cannot drift apart.

◆ default_halfband_design()

HalfBandIirDesign pulp::signal::detail::default_halfband_design ( )
inlinenoexcept

◆ envelope_ms_to_samples()

long long pulp::signal::detail::envelope_ms_to_samples ( double ms,
double sample_rate )
inline

◆ fft_real_split_needs_scratch()

template<typename SampleType>
bool pulp::signal::detail::fft_real_split_needs_scratch ( std::uint64_t fft_size)
inlineconstexprnoexcept

Whether a FftT of fft_size points keeps a real_bins()-long complex scratch for the split-spectrum path (FftRealSplit): every size the portable real transform serves, which is all of them except vDSP's float sizes.

◆ fft_twiddle_index()

std::size_t pulp::signal::detail::fft_twiddle_index ( int phase_index,
int stride )
inlineconstexprnoexcept

◆ fir_multiply_add()

template<typename SampleType>
SampleType pulp::signal::detail::fir_multiply_add ( SampleType a,
SampleType b,
SampleType acc )
inlinenoexcept

acc + a * b for the block FIR: one fused multiply-add where the hardware has it, a separate multiply and add otherwise.

Either way the same expression runs for every output, so partition exactness holds; the two forms differ only by rounding, and only between targets.

◆ fm_bright_band_gain()

template<typename SampleType>
SampleType pulp::signal::detail::fm_bright_band_gain ( SampleType base_hz,
SampleType sample_rate,
FmOperatorAliasPolicy policy )
inlinenoexcept

◆ four_term_cosine_window()

template<typename SampleType>
SampleType pulp::signal::detail::four_term_cosine_window ( SampleType phase,
SampleType a0,
SampleType a1,
SampleType a2,
SampleType a3 )
inline

◆ frequency_at_coordinate()

template<typename SampleType>
SampleType pulp::signal::detail::frequency_at_coordinate ( SampleType coordinate,
SampleType min_hz,
SampleType max_hz,
SpectralBandSpacing spacing )
inlinenoexcept

◆ generate_wavetable()

template<typename SampleType, typename HarmonicAmp>
std::vector< SampleType > pulp::signal::detail::generate_wavetable ( std::size_t length,
std::size_t max_harmonic,
HarmonicAmp && harmonic_amp )
inline

◆ halfband_design_from_paths()

template<typename Coefficient>
HalfBandIirDesign pulp::signal::detail::halfband_design_from_paths ( std::span< const Coefficient > a,
std::span< const Coefficient > b )
inlinenoexcept

◆ halfband_path_response()

template<typename Coefficient>
void pulp::signal::detail::halfband_path_response ( const HalfBandIirDesign & design,
bool path_a,
double frequency,
std::complex< double > & response,
double & group_delay )
inlinenoexcept

◆ is_power_of_two()

bool pulp::signal::detail::is_power_of_two ( int value)
inlineconstexprnoexcept

◆ linear_phase_oversampling_prototype()

std::vector< double > pulp::signal::detail::linear_phase_oversampling_prototype ( double passband_edge_fraction,
double stopband_db,
std::size_t taps )
inline

The Kaiser-windowed lowpass prototype both linear-phase 2x stage forms split into polyphase branches.

Fractions are expressed against the input rate; the filter runs at twice that rate, so base Nyquist is 0.25 cycles/output-sample and the transition ends there.

◆ migrate_input_history()

template<typename SampleType = float>
void pulp::signal::detail::migrate_input_history ( ConvolverInputHistoryT< SampleType > & from,
ConvolverInputHistoryT< SampleType > & to )
inline

Migrate the recorded input history from from into to, age-aligned.

Used when the convolver must move to a longer (or differently-shaped) ring than the one it is holding: the incoming ring is a freshly-built spare, so everything the old ring recorded has to be carried across or the input stream restarts from silence.

The carried spectra are copied from one arena into the other: no allocation, and work bounded by the carried ring length times fft_size / 2 + 1 bins (about 0.8 MB for a 96000-tap IR at 64-sample blocks), so this is safe on the audio thread. from keeps its own copy, which is harmless: the caller hands from off to be freed off the audio thread.

from.write_pos is the slot the next block would be written to, so the most-recent (age 0) block is at from.write_pos - 1. History is laid out in to so that the next write lands at slot 0, and to.write_pos is set to 0 accordingly. Ages older than the shorter of the two rings can hold are dropped; they are beyond the reach of any IR that ring can serve.

A geometry mismatch (different block or FFT size) leaves to cold, which is the only correct answer: spectra taken at one FFT size cannot be reinterpreted at another.

◆ mix_planar_frame()

template<typename SampleType, std::size_t MaxChannels>
bool pulp::signal::detail::mix_planar_frame ( const SampleType *const * channels,
std::size_t channel_count,
std::size_t frame,
bool compute_power,
double & mono,
double & channel_power )
noexcept

◆ multiply_accumulate_packed_split()

template<typename SampleType>
void pulp::signal::detail::multiply_accumulate_packed_split ( const SampleType * a_re,
const SampleType * a_im,
const SampleType * b_re,
const SampleType * b_im,
SampleType * acc_re,
SampleType * acc_im,
int half )
inlinenoexcept

acc += a * b over packed split spectra, with the product as in multiply_packed_split().

acc must not overlap a or b. Allocation-free.

◆ multiply_packed_split()

template<typename SampleType>
void pulp::signal::detail::multiply_packed_split ( const SampleType * a_re,
const SampleType * a_im,
const SampleType * b_re,
const SampleType * b_im,
SampleType * out_re,
SampleType * out_im,
int half )
inlinenoexcept

Product of two packed split spectra of half = size() / 2 slots each (see FftRealSplit): bin k of out is bin k of a times bin k of b for k = 1..half-1, and slot 0's DC and Nyquist reals multiply separately.

The result is the packed spectrum of the circular convolution. out may be a or b; partial overlap is not allowed. Allocation-free.

◆ pitch_class_for_frequency()

int pulp::signal::detail::pitch_class_for_frequency ( double frequency_hz)
inlinenoexcept

◆ precise_stage_curve()

template<typename SampleType>
SampleType pulp::signal::detail::precise_stage_curve ( SampleType progress,
SampleType curve )
inlinenoexcept

◆ raised_cosine()

template<typename SampleType>
SampleType pulp::signal::detail::raised_cosine ( SampleType position)
inlinenoexcept

◆ render_fm_operator_sample()

template<typename SampleType, std::size_t MaxOperators, typename FrequencyFn, typename PhaseRouteFn, typename FrequencyRouteFn, typename CarrierGainFn, typename OscillatorFn>
SampleType pulp::signal::detail::render_fm_operator_sample ( std::size_t operator_count,
SampleType sample_rate,
FmOperatorAliasPolicy alias_policy,
std::array< SampleType, MaxOperators > & phases,
std::array< SampleType, MaxOperators > & previous,
const std::array< SampleType, MaxOperators > & amplitudes,
const std::array< SampleType, MaxOperators > & feedback_radians,
FrequencyFn && base_frequency,
PhaseRouteFn && phase_route,
FrequencyRouteFn && frequency_route,
CarrierGainFn && carrier_gain,
OscillatorFn && oscillator )
noexcept

Canonical one-sample-delayed operator evaluator shared by melodic and drum engines.

Delaying every edge uniformly permits cycles and makes evaluation order irrelevant; it is the routing law already used by the 6/8-op drums.

◆ select_bits()

template<typename T>
T pulp::signal::detail::select_bits ( bool condition,
T if_true,
T if_false )
inlinenoexcept

condition ?

if_true : if_false as bit operations. Same value as the conditional for every input, including NaN payloads and signed zeros, but with no control flow: GCC with its default -ftrapping-math will not if-convert a float conditional whose arms it may sink, which leaves loops over per-voice state unvectorized; this form vectorizes under GCC and Clang alike.

◆ snap_to_zero_bits()

template<typename T>
T pulp::signal::detail::snap_to_zero_bits ( T x)
inlinenoexcept

snap_to_zero() as bit operations (see select_bits()).

◆ solve_implicit_flow_stage()

double pulp::signal::detail::solve_implicit_flow_stage ( const junction::JunctionPair & network,
double history_voltage,
double history_flow,
double source_current,
double flow_weight,
double c_over_step,
double resistance,
double initial,
int & iterations_out )
inline

Solves one implicit stage of C·dv/dt = source_current − v/R − i_D(v).

Both halves of TR-BDF2 have this exact shape. They differ only in the history voltage, the already-known flow carried by that history, the weight of the new flow, and the effective C/dt coefficient. Keeping the nonlinear solve here gives the two stages one bracket, warm-start, tolerance, and iteration policy.

◆ solve_safeguarded_newton()

template<typename Residual, typename Derivative>
double pulp::signal::detail::solve_safeguarded_newton ( Residual && f,
Derivative && df,
double lo,
double hi,
double initial,
double alternate,
double tolerance,
int max_iterations,
int & iterations_out )
inline

One monotone implicit stage, solved by a SAFEGUARDED Newton iteration.

Plain Newton from the previous sample — what the specification calls for — diverges here, and it is worth being precise about why rather than just damping it. The residual's curvature is set by exp(v/θ) with θ ≈ 26 mV, so a single unguarded step taken from outside the conducting region overshoots by orders of magnitude and lands where the exponential is astronomically large; the next step is worse. In practice the iterate escapes to ±10¹⁶ within a few samples of a cold start and never returns.

The fix is the standard one for monotone root finds: keep a bracket and fall back to bisection whenever a Newton step would leave it. This is globally convergent — F is strictly increasing in v, since every term of F' is positive — while still getting Newton's quadratic convergence once the iterate is close, which is the common case at audio rates where consecutive samples differ little.

The bracket is physical rather than arbitrary: the diode network can only sink current toward ground, so the node voltage always lies between where it was and where the linear network alone would drive it.

Returns the solved voltage and writes the iteration count taken, so the RT-safety cap can be asserted rather than assumed.

◆ solve_tr_bdf2()

double pulp::signal::detail::solve_tr_bdf2 ( const junction::JunctionPair & network,
double previous_voltage,
double previous_source_current,
double source_current,
double capacitance,
double timestep,
double resistance,
int & iterations_out )
inline

One L-stable TR-BDF2 step of C·dv/dt = source_current(t) − v/R − i_D(v).

gamma = 2 − sqrt(2) is the standard TR-BDF2 choice that makes the method second-order and L-stable. The first implicit trapezoid stage advances to t + gamma·T; the variable-step BDF2 stage completes the sample. Unlike a full trapezoidal step, the composite method drives infinitely stiff modes to zero instead of preserving them as an alternating sample-to-sample mode.

◆ split_fractional_delay()

FractionalDelaySplit pulp::signal::detail::split_fractional_delay ( double delay_samples)
inlinenodiscardnoexcept

◆ tanh_accurate_bits()

float pulp::signal::detail::tanh_accurate_bits ( float x)
inlinenoexcept

FastMath::tanh_accurate() with its clamps as bit operations; the same value for every input.

◆ transition_then_block()

template<typename Transitioning, typename Sample, typename Block>
void pulp::signal::detail::transition_then_block ( std::size_t frames,
Transitioning && transitioning,
Sample && sample,
Block && block )
inline

The block path the cascade EQs share: a design transition (crossfade or coefficient ramp) runs sample by sample through sample(i) while transitioning() holds; once it has finished, the rest of the block goes to block(first, count) in one call.

A transition never starts inside a block, so the split happens at most once.

◆ triangle_fold()

double pulp::signal::detail::triangle_fold ( double input)
inlinenoexcept

◆ triangle_fold_slope()

double pulp::signal::detail::triangle_fold_slope ( double input)
inlinenoexcept

◆ valid_alias_policy()

bool pulp::signal::detail::valid_alias_policy ( NonlinearAliasPolicy policy)
inlinenoexcept

◆ valid_spectral_band_layout()

template<typename SampleType>
bool pulp::signal::detail::valid_spectral_band_layout ( const SpectralBandLayoutT< SampleType > & layout,
SampleType nyquist,
SampleType & effective_min,
SampleType & effective_max )
inlinenoexcept

◆ wave_integral_in_cycle()

double pulp::signal::detail::wave_integral_in_cycle ( LfoWave wave,
double x,
const LfoShapeParams & params )
inlinenodiscardnoexcept

The integral of one wave over [0, x] for x in [0, 1].

◆ wave_integral_per_cycle()

double pulp::signal::detail::wave_integral_per_cycle ( LfoWave wave,
const LfoShapeParams & params )
inlinenodiscardnoexcept

The integral over one whole cycle: zero for every wave but the square.

◆ wrap01()

double pulp::signal::detail::wrap01 ( double x)
inlinenodiscardnoexcept

Variable Documentation

◆ kNonlinearMaximumSampleRate

double pulp::signal::detail::kNonlinearMaximumSampleRate = 768000.0
inlineconstexpr

◆ kNonlinearMinimumSampleRate

double pulp::signal::detail::kNonlinearMinimumSampleRate = 1000.0
inlineconstexpr

◆ kTwoPi

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

◆ kWavetableTwoPi

template<typename SampleType>
SampleType pulp::signal::detail::kWavetableTwoPi
constexpr
Initial value:
=
static_cast<SampleType>(6.283185307179586476925286766559L)

◆ valid_audio_matrix_capacity

template<std::size_t Inputs, std::size_t Outputs>
bool pulp::signal::detail::valid_audio_matrix_capacity
inlineconstexpr
Initial value:
= Inputs > 0 && Outputs > 0 &&
Inputs <= std::numeric_limits<std::size_t>::max() / Outputs