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 |
A sinusoidal-bank additive synthesizer.
|
strong |
How a partial's amplitude evolves.
|
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. |
|
inline |
|
inline |
|
inline |
|
inline |
|
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.
|
inline |
|
inline |
|
inline |
|
inlinestaticconstexpr |
Zero. Direct time-domain oscillators; nothing is buffered.
|
inline |
|
inline |
|
inline |
|
inline |
|
inline |
|
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.
|
inline |
|
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.
|
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.
|
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.
|
inline |
|
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.
|
inline |
Clears every phase, envelope, and ramp, and rewinds the generator, so a render from here is bit-identical every time.
|
inline |
|
inline |
Note-on.
Applies the phase policy, restarts the per-partial decays, and opens the shared onset.
|
inline |
|
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.
|
inline |
|
inline |
|
inline |
|
inline |
|
inline |
Stiff-string coefficient.
Applies only to harmonic-typed voices; a modal voice ignores it (see VoiceTable::harmonic).
|
inline |
|
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).
|
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.
|
inline |
|
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.
|
inline |
|
inline |
|
inline |
Seeds the initial-phase and doublet-jitter generator.
Construction or preset choice only — never an automatable parameter (series law 2).
|
inline |
|
inline |
|
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.
|
inline |
|
inline |
|
inlinenoexcept |
|
inlinestaticconstexpr |
|
inline |
|
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.
|
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.
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
Reference points, all inside the cited band.
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
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.
|
staticconstexpr |
Curvature of the shared onset and release segments.
[design parameter] default 0.5, range 0 .. 1.
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
Ceiling of the Fletcher/Blackham/Stratton measured band.
|
staticconstexpr |
|
staticconstexpr |
Parameter ceiling.
Beyond kInharmonicityCitedMax this is an uncited extension — see the honest-gap note in the file doc block.
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
Width of the raised-cosine taper below Nyquist.
[design parameter] default 1000 Hz, range 200 .. 4000 Hz.
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |
|
staticconstexpr |