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

A sinusoidal-bank additive synthesizer. More...

#include <additive_bank.hpp>

Public Types

enum class  RetrigPhase : std::uint8_t { reset_stored , free_run , seeded_random }
 Phase treatment on retrigger(). More...
enum class  EnvelopeMode : std::uint8_t { shared_ar , per_partial_decay }
 How a partial's amplitude evolves. More...

Public Member Functions

 AdditiveBankT ()
void prepare (double sample_rate, int max_partials=kMaxPartialsDefault)
 Sizes the per-partial arrays and returns the bank to a fresh state.
void reset ()
 Clears every phase, envelope, and ramp, and rewinds the generator, so a render from here is bit-identical every time.
void set_fundamental_hz (double f0)
double fundamental_hz () const
void set_partial_count (int n)
int partial_count () const
int max_partials () const
void set_inharmonicity_b (double b)
 Stiff-string coefficient.
double inharmonicity_b () const
void set_spectral_tilt_db_oct (double s)
double spectral_tilt_db_oct () const
void set_master_gain_db (double g)
double master_gain_db () const
bool set_trig_profile (FastTrigProfile profile) noexcept
 Selects the bounded-cycle sine implementation used by the float bank.
FastTrigProfile trig_profile () const noexcept
void load_voice (const VoiceTable &v)
const VoiceTable & voice () const
void set_partial (int p, double ratio, double amp_lin, double phase01, double decay_ms)
 Sets one row of the active table.
void set_envelope_a (const SpectralEnvelope &e)
void set_envelope_b (const SpectralEnvelope &e)
void set_morph (float m)
 Crossfade A↔B.
double morph () const
void set_spectral_domain (SpectralDomain d)
SpectralDomain spectral_domain () const
double envelope_db_at (double f_hz) const
 The live morphed envelope in dB at a partial frequency — the exact quantity the render applies, exposed so a test asserts the shipped path rather than a reimplementation of it.
void set_envelope_mode (EnvelopeMode mode)
EnvelopeMode envelope_mode () const
void set_attack_ms (double ms)
void set_release_ms (double ms)
void set_detune_cents (double cents)
 Doublet spread in cents.
double detune_cents () const
bool doublet_active () const
void set_pitch_glide (double start_cents, double time_ms)
 A shared initial pitch glide: every partial is scaled by 2^(cents(t)/1200), starting at start_cents and reaching 0 over time_ms.
void set_retrig_phase (RetrigPhase p)
RetrigPhase retrig_phase () const
void set_seed (std::uint32_t seed)
 Seeds the initial-phase and doublet-jitter generator.
void retrigger ()
 Note-on.
void release ()
 Note-off.
bool active () const
double partial_frequency (int p) const
 The current swept frequency of one partial's primary oscillator, in Hz — the quantity the inharmonicity and guard laws take as input.
SampleType next ()
void process (SampleType *out, int n)

Static Public Member Functions

static double partial_frequency_hz (double f0_hz, double ratio, double inharmonicity_b)
 The published stiff-string law: f = f0 · ratio · √(1 + B·ratio²).
static double nyquist_guard_gain (double f_hz, double sample_rate)
 The Nyquist guard: unity below fs/2 − kNyquistGuardBandHz, a raised cosine to exactly zero at fs/2, and zero above it.
static double worst_case_gain ()
 The worst-case output magnitude over the entire legal parameter space.
static constexpr int latency_samples ()
 Zero. Direct time-domain oscillators; nothing is buffered.
static constexpr bool trig_profile_has_vector_path (FastTrigProfile profile)

Static Public Attributes

static constexpr int kMaxPartialsCeiling = VoiceTable::kMaxPartials
static constexpr int kMaxPartialsDefault = 128
static constexpr int kPartialCountDefault = 64
static constexpr double kFundamentalMinHz = 20.0
static constexpr double kFundamentalMaxHz = 8000.0
static constexpr double kFundamentalDefaultHz = 220.0
static constexpr double kInharmonicityDefault = 0.0
static constexpr double kInharmonicityCitedMax = 0.02
 Ceiling of the Fletcher/Blackham/Stratton measured band.
static constexpr double kInharmonicityMax = 0.05
 Parameter ceiling.
static constexpr double kBPianoTenor = 0.0002
 Reference points, all inside the cited band.
static constexpr double kBPianoMid = 0.0008
static constexpr double kBUpperTreble = 0.004
static constexpr double kBElectricPianoTine = 0.02
static constexpr double kSpectralTiltMinDbOct = -24.0
static constexpr double kSpectralTiltMaxDbOct = 6.0
static constexpr double kSpectralTiltDefaultDbOct = -6.0
static constexpr double kMasterGainMinDb = -60.0
static constexpr double kMasterGainMaxDb = 6.0
static constexpr double kMasterGainDefaultDb = -6.0
static constexpr double kAttackDefaultMs = 5.0
static constexpr double kAttackMinMs = 0.1
static constexpr double kAttackMaxMs = 2000.0
static constexpr double kReleaseDefaultMs = 400.0
static constexpr double kReleaseMinMs = 1.0
static constexpr double kReleaseMaxMs = 20000.0
static constexpr double kDetuneDefaultCents = 0.0
static constexpr double kDetuneMaxCents = 50.0
static constexpr double kDoubletJitterSpread = 0.5
 Per-mode variation in the doublet's detune, as a fraction of the nominal.
static constexpr int kAmpUpdateCadenceSamples = 32
 Control-rate cadence for frequency/gain recomputation, with linear ramping between updates so partial gains never step.
static constexpr double kNyquistGuardBandHz = 1000.0
 Width of the raised-cosine taper below Nyquist.
static constexpr double kEnvelopeCurve = 0.5
 Curvature of the shared onset and release segments.
static constexpr double kTwoPi = 6.283185307179586476925286766559

Detailed Description

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

A sinusoidal-bank additive synthesizer.

Member Enumeration Documentation

◆ EnvelopeMode

template<typename SampleType = float>
enum class pulp::signal::AdditiveBankT::EnvelopeMode : std::uint8_t
strong

How a partial's amplitude evolves.

Enumerator
shared_ar 

One onset/release shape for every partial.

The organ/pad case, where partials move together.

per_partial_decay 

The shared onset times each partial's own exp(−t/τ_p).

The physical mode: higher partials die first, which is what makes a struck body read as an object.

◆ RetrigPhase

template<typename SampleType = float>
enum class pulp::signal::AdditiveBankT::RetrigPhase : std::uint8_t
strong

Phase treatment on retrigger().

Enumerator
reset_stored 

Every phase returns to its table phase01.

The default, because it makes the attack bit-identical every strike (series law 2).

free_run 

Phases keep running.

A softer, already-sounding re-entry; still deterministic given trigger times.

seeded_random 

Phases drawn from the Xorshift32 seeded at reset().

Diffuse attack with no crest alignment, identical across render/reset/re-render.

Constructor & Destructor Documentation

◆ AdditiveBankT()

template<typename SampleType = float>
pulp::signal::AdditiveBankT< SampleType >::AdditiveBankT ( )
inline

Member Function Documentation

◆ active()

template<typename SampleType = float>
bool pulp::signal::AdditiveBankT< SampleType >::active ( ) const
inline

◆ detune_cents()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::detune_cents ( ) const
inline

◆ doublet_active()

template<typename SampleType = float>
bool pulp::signal::AdditiveBankT< SampleType >::doublet_active ( ) const
inline

◆ envelope_db_at()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::envelope_db_at ( double f_hz) const
inline

The live morphed envelope in dB at a partial frequency — the exact quantity the render applies, exposed so a test asserts the shipped path rather than a reimplementation of it.

◆ envelope_mode()

template<typename SampleType = float>
EnvelopeMode pulp::signal::AdditiveBankT< SampleType >::envelope_mode ( ) const
inline

◆ fundamental_hz()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::fundamental_hz ( ) const
inline

◆ inharmonicity_b()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::inharmonicity_b ( ) const
inline

◆ latency_samples()

template<typename SampleType = float>
constexpr int pulp::signal::AdditiveBankT< SampleType >::latency_samples ( )
inlinestaticconstexpr

Zero. Direct time-domain oscillators; nothing is buffered.

◆ load_voice()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::load_voice ( const VoiceTable & v)
inline

◆ master_gain_db()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::master_gain_db ( ) const
inline

◆ max_partials()

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::max_partials ( ) const
inline

◆ morph()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::morph ( ) const
inline

◆ next()

template<typename SampleType = float>
SampleType pulp::signal::AdditiveBankT< SampleType >::next ( )
inline

◆ nyquist_guard_gain()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::nyquist_guard_gain ( double f_hz,
double sample_rate )
inlinestatic

The Nyquist guard: unity below fs/2 − kNyquistGuardBandHz, a raised cosine to exactly zero at fs/2, and zero above it.

Smooth at both ends, so a partial gliding through the guard fades rather than clicking.

◆ partial_count()

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::partial_count ( ) const
inline

◆ partial_frequency()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::partial_frequency ( int p) const
inline

The current swept frequency of one partial's primary oscillator, in Hz — the quantity the inharmonicity and guard laws take as input.

Exposed because it is what a host draws, and what the trajectory tests measure directly rather than recovering from audio.

◆ partial_frequency_hz()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::partial_frequency_hz ( double f0_hz,
double ratio,
double inharmonicity_b )
inlinestatic

The published stiff-string law: f = f0 · ratio · √(1 + B·ratio²).

Note the mode number used for the stretch is the RATIO, not the array index. For a pure harmonic table those are the same thing, but for a table like the organ's — whose second row is the 16′ sub at ratio 0.5 — they are not, and the physics depends on which mode of the string is vibrating, not on where the author happened to put the row.

◆ prepare()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::prepare ( double sample_rate,
int max_partials = kMaxPartialsDefault )
inline

Sizes the per-partial arrays and returns the bank to a fresh state.

The only method here that may allocate, and only when max_partials grows past what is already reserved.

◆ process()

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

◆ release()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::release ( )
inline

Note-off.

The shared onset falls over release_ms. Named separately from retrigger() because release_ms is a parameter and a parameter with no way to reach it is a parameter that does not exist.

◆ reset()

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

Clears every phase, envelope, and ramp, and rewinds the generator, so a render from here is bit-identical every time.

◆ retrig_phase()

template<typename SampleType = float>
RetrigPhase pulp::signal::AdditiveBankT< SampleType >::retrig_phase ( ) const
inline

◆ retrigger()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::retrigger ( )
inline

Note-on.

Applies the phase policy, restarts the per-partial decays, and opens the shared onset.

◆ set_attack_ms()

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

◆ set_detune_cents()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_detune_cents ( double cents)
inline

Doublet spread in cents.

0 disables the pair outright — the second oscillator is not rendered, so a mono voice costs half of a detuned one.

◆ set_envelope_a()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_envelope_a ( const SpectralEnvelope & e)
inline

◆ set_envelope_b()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_envelope_b ( const SpectralEnvelope & e)
inline

◆ set_envelope_mode()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_envelope_mode ( EnvelopeMode mode)
inline

◆ set_fundamental_hz()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_fundamental_hz ( double f0)
inline

◆ set_inharmonicity_b()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_inharmonicity_b ( double b)
inline

Stiff-string coefficient.

Applies only to harmonic-typed voices; a modal voice ignores it (see VoiceTable::harmonic).

◆ set_master_gain_db()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_master_gain_db ( double g)
inline

◆ set_morph()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_morph ( float m)
inline

Crossfade A↔B.

The blend is in dB, on the shared abscissa grid — never a crossfade of linear partial amplitudes, and never an interpolation between independently-fitted per-pitch amplitude tables (series law 7).

◆ set_partial()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_partial ( int p,
double ratio,
double amp_lin,
double phase01,
double decay_ms )
inline

Sets one row of the active table.

Control-rate: the change reaches the audio through the next control block's ramp, not as a step.

◆ set_partial_count()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_partial_count ( int n)
inline

◆ set_pitch_glide()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_pitch_glide ( double start_cents,
double time_ms )
inline

A shared initial pitch glide: every partial is scaled by 2^(cents(t)/1200), starting at start_cents and reaching 0 over time_ms.

The bell's strike chiff and a drum's pitch drop.

◆ set_release_ms()

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

◆ set_retrig_phase()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_retrig_phase ( RetrigPhase p)
inline

◆ set_seed()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_seed ( std::uint32_t seed)
inline

Seeds the initial-phase and doublet-jitter generator.

Construction or preset choice only — never an automatable parameter (series law 2).

◆ set_spectral_domain()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_spectral_domain ( SpectralDomain d)
inline

◆ set_spectral_tilt_db_oct()

template<typename SampleType = float>
void pulp::signal::AdditiveBankT< SampleType >::set_spectral_tilt_db_oct ( double s)
inline

◆ set_trig_profile()

template<typename SampleType = float>
bool pulp::signal::AdditiveBankT< SampleType >::set_trig_profile ( FastTrigProfile profile)
inlinenoexcept

Selects the bounded-cycle sine implementation used by the float bank.

The reference profile remains the compatibility default. The precise profile is opt-in; unsupported targets retain scalar profile semantics.

◆ spectral_domain()

template<typename SampleType = float>
SpectralDomain pulp::signal::AdditiveBankT< SampleType >::spectral_domain ( ) const
inline

◆ spectral_tilt_db_oct()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::spectral_tilt_db_oct ( ) const
inline

◆ trig_profile()

template<typename SampleType = float>
FastTrigProfile pulp::signal::AdditiveBankT< SampleType >::trig_profile ( ) const
inlinenoexcept

◆ trig_profile_has_vector_path()

template<typename SampleType = float>
constexpr bool pulp::signal::AdditiveBankT< SampleType >::trig_profile_has_vector_path ( FastTrigProfile profile)
inlinestaticconstexpr

◆ voice()

template<typename SampleType = float>
const VoiceTable & pulp::signal::AdditiveBankT< SampleType >::voice ( ) const
inline

◆ worst_case_gain()

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::worst_case_gain ( )
inlinestatic

The worst-case output magnitude over the entire legal parameter space.

The normaliser bounds the partial sum to 1 by construction, so this is the master trim's legal maximum and nothing else. An equality, attained with aligned phases — not a safety estimate.

Member Data Documentation

◆ kAmpUpdateCadenceSamples

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::kAmpUpdateCadenceSamples = 32
staticconstexpr

Control-rate cadence for frequency/gain recomputation, with linear ramping between updates so partial gains never step.

[design parameter] default 32 samples, range 8 .. 128.

◆ kAttackDefaultMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kAttackDefaultMs = 5.0
staticconstexpr

◆ kAttackMaxMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kAttackMaxMs = 2000.0
staticconstexpr

◆ kAttackMinMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kAttackMinMs = 0.1
staticconstexpr

◆ kBElectricPianoTine

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kBElectricPianoTine = 0.02
staticconstexpr

◆ kBPianoMid

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kBPianoMid = 0.0008
staticconstexpr

◆ kBPianoTenor

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kBPianoTenor = 0.0002
staticconstexpr

Reference points, all inside the cited band.

◆ kBUpperTreble

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kBUpperTreble = 0.004
staticconstexpr

◆ kDetuneDefaultCents

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kDetuneDefaultCents = 0.0
staticconstexpr

◆ kDetuneMaxCents

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kDetuneMaxCents = 50.0
staticconstexpr

◆ kDoubletJitterSpread

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kDoubletJitterSpread = 0.5
staticconstexpr

Per-mode variation in the doublet's detune, as a fraction of the nominal.

Real bells do not beat in lockstep; without this every mode beats at the same rate and the result reads as a chorus effect rather than a casting asymmetry. [design parameter] default 0.5, range 0 .. 1.

◆ kEnvelopeCurve

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kEnvelopeCurve = 0.5
staticconstexpr

Curvature of the shared onset and release segments.

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

◆ kFundamentalDefaultHz

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kFundamentalDefaultHz = 220.0
staticconstexpr

◆ kFundamentalMaxHz

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kFundamentalMaxHz = 8000.0
staticconstexpr

◆ kFundamentalMinHz

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kFundamentalMinHz = 20.0
staticconstexpr

◆ kInharmonicityCitedMax

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kInharmonicityCitedMax = 0.02
staticconstexpr

Ceiling of the Fletcher/Blackham/Stratton measured band.

◆ kInharmonicityDefault

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kInharmonicityDefault = 0.0
staticconstexpr

◆ kInharmonicityMax

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kInharmonicityMax = 0.05
staticconstexpr

Parameter ceiling.

Beyond kInharmonicityCitedMax this is an uncited extension — see the honest-gap note in the file doc block.

◆ kMasterGainDefaultDb

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kMasterGainDefaultDb = -6.0
staticconstexpr

◆ kMasterGainMaxDb

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kMasterGainMaxDb = 6.0
staticconstexpr

◆ kMasterGainMinDb

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kMasterGainMinDb = -60.0
staticconstexpr

◆ kMaxPartialsCeiling

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::kMaxPartialsCeiling = VoiceTable::kMaxPartials
staticconstexpr

◆ kMaxPartialsDefault

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::kMaxPartialsDefault = 128
staticconstexpr

◆ kNyquistGuardBandHz

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kNyquistGuardBandHz = 1000.0
staticconstexpr

Width of the raised-cosine taper below Nyquist.

[design parameter] default 1000 Hz, range 200 .. 4000 Hz.

◆ kPartialCountDefault

template<typename SampleType = float>
int pulp::signal::AdditiveBankT< SampleType >::kPartialCountDefault = 64
staticconstexpr

◆ kReleaseDefaultMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kReleaseDefaultMs = 400.0
staticconstexpr

◆ kReleaseMaxMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kReleaseMaxMs = 20000.0
staticconstexpr

◆ kReleaseMinMs

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kReleaseMinMs = 1.0
staticconstexpr

◆ kSpectralTiltDefaultDbOct

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kSpectralTiltDefaultDbOct = -6.0
staticconstexpr

◆ kSpectralTiltMaxDbOct

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kSpectralTiltMaxDbOct = 6.0
staticconstexpr

◆ kSpectralTiltMinDbOct

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kSpectralTiltMinDbOct = -24.0
staticconstexpr

◆ kTwoPi

template<typename SampleType = float>
double pulp::signal::AdditiveBankT< SampleType >::kTwoPi = 6.283185307179586476925286766559
staticconstexpr

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