Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::signal::drum::Fm8DrumVoice Class Reference

An eight-operator FM percussion voice with a fixed algorithm table. More...

#include <fm.hpp>

Classes

struct  Algorithm
 One routing: for each operator, a bitmask of the operators that modulate it, and whether it is heard directly. More...

Public Member Functions

 Fm8DrumVoice ()
void set_algorithm (int index)
int algorithm () const
void set_tune_hz (double hz)
void set_operator_ratio (int op, double ratio)
 Frequency ratio of one operator to the voice's tuning.
void set_operator_level (int op, double level)
 Output level of one operator, before the algorithm's routing.
void set_operator_decay_ms (int op, double ms)
 Decay of one operator's own envelope, as a T60.
void set_operator_feedback (int op, double amount)
 Self-feedback on one operator.
void set_operator_wave (int op, int wave)
void set_depth (double depth)
 Overall modulation depth, scaling every routed connection at once.
void set_formant_hz (double hz)
 A formant bandpass on the summed carriers.
void set_formant_q (double q)
void set_transient (int index)
void set_noise_level (double level)
void set_noise_decay_ms (double ms)
void set_noise_color (NoiseColor color)
void set_click_level (double level)
OutputStage & output ()
const OutputStage & output () const
OutputStage * output_stage () noexcept override
 The terminal saturation and degradation stage, at the generic voice boundary.
int latency_samples () const noexcept override
 Fixed host-rate delay introduced by this voice's selected output quality.
OutputOversampling output_oversampling () const noexcept override
 Output quality at the generic voice boundary.
void set_output_oversampling (OutputOversampling factor) override
void prepare (double sample_rate)
 Fixes the sample rate and clears all state.
void reset ()
 Silences the voice immediately, without a fade.
void note_on (float velocity)
 Starts a hit. velocity is in [0, 1]; values outside are clamped.
void choke (float fade_ms=4.0f)
 Fades the voice out over fade_ms and then silences it.
bool is_active () const
 Whether the voice still has something to render.
VoiceState state () const
float velocity () const
double sample_rate () const
const OutputStage * output_stage () const noexcept
void set_velocity_response (const VelocityResponse &r)
const VelocityResponse & velocity_response () const
void process (float *out, int num_samples)
 Adds num_samples of this voice's output into out.
void process_stereo (float *left, float *right, int num_samples)
 Adds a stereo rendering to left and right.

Static Public Attributes

static constexpr int operator_count = 8
static constexpr int algorithm_count = 16
static constexpr Algorithm algorithms [algorithm_count]
 The sixteen routings, ordered roughly from most parallel to most serial: 0 is eight independent carriers (additive), 15 is a single eight-deep stack (the most extreme spectrum the voice can make).

Protected Member Functions

void on_prepare (double sample_rate) override
 Sizes buffers and fixes coefficients.
void on_reset () override
 Clears every piece of state the voice carries, so is_active() becomes false and the next render starts from silence.
void on_note_on (float velocity) override
 Starts a hit at the given velocity, already clamped to [0, 1].
bool on_is_active () const override
void render_add (float *out, int num_samples) override
 Adds the voice's output into out. Must add, never assign.
virtual void render_add_stereo (float *left, float *right, int num_samples)
 Optional stereo realization.

Detailed Description

An eight-operator FM percussion voice with a fixed algorithm table.

Two operators reach bells and clanks; eight reach the dense, evolving spectra that a single modulator cannot, because each operator can modulate several others and the resulting sidebands modulate each other in turn.

Every operator reads every modulator's output one sample late. That single decision is what makes an arbitrary routing matrix computable: with delayed reads there is no ordering constraint at all, so operators can be evaluated in any order and an algorithm may contain cycles that a topological sort would reject outright. The cost is one sample of phase error per modulation hop, which at audio rates is far below what the modulation itself is doing. It is applied uniformly rather than only to paths that would otherwise be circular, so the routing table needs no analysis and adding an algorithm cannot introduce an ordering bug.

An algorithm is a bitmask per operator saying which others feed it, plus a flag saying whether it reaches the output. The sixteen below are chosen to span the useful shapes — deep serial stacks for clangorous metal, wide parallel banks for additive-like bodies, and mixed trees for everything between — rather than to reproduce any particular instrument's set. Each operator independently selects one of the same 26 clean harmonic-table waves as FM2, and the shared procedural transient table can add a tinted noise/contact strike without routing a ninth operator.

Lineage: Chowning, JAES 21(7), 1973, as for the two-operator voice.

RT contract: prepare() allocates nothing; every other method allocates nothing and takes no locks.

Constructor & Destructor Documentation

◆ Fm8DrumVoice()

pulp::signal::drum::Fm8DrumVoice::Fm8DrumVoice ( )
inline

Member Function Documentation

◆ algorithm()

int pulp::signal::drum::Fm8DrumVoice::algorithm ( ) const
inline

◆ choke()

void pulp::signal::drum::Voice::choke ( float fade_ms = 4.0f)
inlineinherited

Fades the voice out over fade_ms and then silences it.

Used by choke groups; a shorter fade is more abrupt but never clicks the way an instant cut does.

◆ is_active()

bool pulp::signal::drum::Voice::is_active ( ) const
inlineinherited

Whether the voice still has something to render.

A kit uses this to skip voices entirely rather than summing silence.

◆ latency_samples()

int pulp::signal::drum::Fm8DrumVoice::latency_samples ( ) const
inlineoverridevirtualnoexcept

Fixed host-rate delay introduced by this voice's selected output quality.

Generic registry consumers can report it without recovering the concrete voice type.

Reimplemented from pulp::signal::drum::Voice.

◆ note_on()

void pulp::signal::drum::Voice::note_on ( float velocity)
inlineinherited

Starts a hit. velocity is in [0, 1]; values outside are clamped.

◆ on_is_active()

bool pulp::signal::drum::Fm8DrumVoice::on_is_active ( ) const
inlineoverrideprotectedvirtual

◆ on_note_on()

void pulp::signal::drum::Fm8DrumVoice::on_note_on ( float velocity)
inlineoverrideprotectedvirtual

Starts a hit at the given velocity, already clamped to [0, 1].

Implements pulp::signal::drum::Voice.

◆ on_prepare()

void pulp::signal::drum::Fm8DrumVoice::on_prepare ( double sample_rate)
inlineoverrideprotectedvirtual

Sizes buffers and fixes coefficients.

The only hook allowed to allocate.

Implements pulp::signal::drum::Voice.

◆ on_reset()

void pulp::signal::drum::Fm8DrumVoice::on_reset ( )
inlineoverrideprotectedvirtual

Clears every piece of state the voice carries, so is_active() becomes false and the next render starts from silence.

Implements pulp::signal::drum::Voice.

◆ output() [1/2]

OutputStage & pulp::signal::drum::Fm8DrumVoice::output ( )
inline

◆ output() [2/2]

const OutputStage & pulp::signal::drum::Fm8DrumVoice::output ( ) const
inline

◆ output_oversampling()

OutputOversampling pulp::signal::drum::Fm8DrumVoice::output_oversampling ( ) const
inlineoverridevirtualnoexcept

Output quality at the generic voice boundary.

Kits use this surface to keep every registered path on one latency contract.

Reimplemented from pulp::signal::drum::Voice.

◆ output_stage() [1/2]

OutputStage * pulp::signal::drum::Fm8DrumVoice::output_stage ( )
inlineoverridevirtualnoexcept

The terminal saturation and degradation stage, at the generic voice boundary.

A registry consumer reaches it without recovering the concrete voice type, so adding an engine cannot desynchronise a downcast from the engine it was selected for. A voice that ends without one returns nullptr.

Reimplemented from pulp::signal::drum::Voice.

◆ output_stage() [2/2]

const OutputStage * pulp::signal::drum::Voice::output_stage ( ) const
inlinenoexceptinherited

◆ prepare()

void pulp::signal::drum::Voice::prepare ( double sample_rate)
inlineinherited

Fixes the sample rate and clears all state.

The only method a derived voice may allocate in.

◆ process()

void pulp::signal::drum::Voice::process ( float * out,
int num_samples )
inlineinherited

Adds num_samples of this voice's output into out.

◆ process_stereo()

void pulp::signal::drum::Voice::process_stereo ( float * left,
float * right,
int num_samples )
inlineinherited

Adds a stereo rendering to left and right.

Voices that do not provide a stereo realization are placed in the centre with half their mono signal in each channel, so summing the two channels reproduces process() exactly. A stereo-capable voice may override render_add_stereo, but the shared choke fade remains here.

◆ render_add()

void pulp::signal::drum::Fm8DrumVoice::render_add ( float * out,
int num_samples )
inlineoverrideprotectedvirtual

Adds the voice's output into out. Must add, never assign.

Implements pulp::signal::drum::Voice.

◆ render_add_stereo()

virtual void pulp::signal::drum::Voice::render_add_stereo ( float * left,
float * right,
int num_samples )
inlineprotectedvirtualinherited

Optional stereo realization.

The default is centre-panned and preserves exact mono-sum equivalence.

Reimplemented in pulp::signal::drum::ClapVoice.

◆ reset()

void pulp::signal::drum::Voice::reset ( )
inlineinherited

Silences the voice immediately, without a fade.

For transport stops and buffer-size changes, where a click cannot be heard because nothing is playing either side of it.

◆ sample_rate()

double pulp::signal::drum::Voice::sample_rate ( ) const
inlineinherited

◆ set_algorithm()

void pulp::signal::drum::Fm8DrumVoice::set_algorithm ( int index)
inline

◆ set_click_level()

void pulp::signal::drum::Fm8DrumVoice::set_click_level ( double level)
inline

◆ set_depth()

void pulp::signal::drum::Fm8DrumVoice::set_depth ( double depth)
inline

Overall modulation depth, scaling every routed connection at once.

◆ set_formant_hz()

void pulp::signal::drum::Fm8DrumVoice::set_formant_hz ( double hz)
inline

A formant bandpass on the summed carriers.

◆ set_formant_q()

void pulp::signal::drum::Fm8DrumVoice::set_formant_q ( double q)
inline

◆ set_noise_color()

void pulp::signal::drum::Fm8DrumVoice::set_noise_color ( NoiseColor color)
inline

◆ set_noise_decay_ms()

void pulp::signal::drum::Fm8DrumVoice::set_noise_decay_ms ( double ms)
inline

◆ set_noise_level()

void pulp::signal::drum::Fm8DrumVoice::set_noise_level ( double level)
inline

◆ set_operator_decay_ms()

void pulp::signal::drum::Fm8DrumVoice::set_operator_decay_ms ( int op,
double ms )
inline

Decay of one operator's own envelope, as a T60.

Operators decaying at different rates is what makes the spectrum evolve rather than simply fade – a modulator that dies first takes its sidebands with it.

◆ set_operator_feedback()

void pulp::signal::drum::Fm8DrumVoice::set_operator_feedback ( int op,
double amount )
inline

Self-feedback on one operator.

◆ set_operator_level()

void pulp::signal::drum::Fm8DrumVoice::set_operator_level ( int op,
double level )
inline

Output level of one operator, before the algorithm's routing.

◆ set_operator_ratio()

void pulp::signal::drum::Fm8DrumVoice::set_operator_ratio ( int op,
double ratio )
inline

Frequency ratio of one operator to the voice's tuning.

◆ set_operator_wave()

void pulp::signal::drum::Fm8DrumVoice::set_operator_wave ( int op,
int wave )
inline

◆ set_output_oversampling()

void pulp::signal::drum::Fm8DrumVoice::set_output_oversampling ( OutputOversampling factor)
inlineoverridevirtual

Reimplemented from pulp::signal::drum::Voice.

◆ set_transient()

void pulp::signal::drum::Fm8DrumVoice::set_transient ( int index)
inline

◆ set_tune_hz()

void pulp::signal::drum::Fm8DrumVoice::set_tune_hz ( double hz)
inline

◆ set_velocity_response()

void pulp::signal::drum::Voice::set_velocity_response ( const VelocityResponse & r)
inlineinherited

◆ state()

VoiceState pulp::signal::drum::Voice::state ( ) const
inlineinherited

◆ velocity()

float pulp::signal::drum::Voice::velocity ( ) const
inlineinherited

◆ velocity_response()

const VelocityResponse & pulp::signal::drum::Voice::velocity_response ( ) const
inlineinherited

Member Data Documentation

◆ algorithm_count

int pulp::signal::drum::Fm8DrumVoice::algorithm_count = 16
staticconstexpr

◆ algorithms

Algorithm pulp::signal::drum::Fm8DrumVoice::algorithms[algorithm_count]
staticconstexpr
Initial value:
= {
{{0, 0, 0, 0, 0, 0, 0, 0}, 0xFF},
{{0x02, 0, 0, 0, 0, 0, 0, 0}, 0xFD},
{{0x02, 0, 0x08, 0, 0, 0, 0, 0}, 0xF5},
{{0x02, 0, 0x08, 0, 0x20, 0, 0, 0}, 0xD5},
{{0x02, 0, 0x08, 0, 0x20, 0, 0x80, 0}, 0x55},
{{0x06, 0, 0, 0, 0, 0, 0, 0}, 0xF9},
{{0x0E, 0, 0, 0, 0, 0, 0, 0}, 0xF1},
{{0x1E, 0, 0, 0, 0, 0, 0, 0}, 0xE1},
{{0x02, 0x04, 0, 0, 0, 0, 0, 0}, 0xF9},
{{0x02, 0x04, 0x08, 0, 0, 0, 0, 0}, 0xF1},
{{0x02, 0x04, 0, 0x20, 0, 0, 0, 0}, 0xD9},
{{0x06, 0x18, 0, 0, 0, 0, 0, 0}, 0xE1},
{{0x02, 0x0C, 0x10, 0, 0, 0, 0, 0}, 0xE1},
{{0x02, 0x04, 0x08, 0x10, 0, 0, 0, 0}, 0xE1},
{{0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0, 0}, 0x81},
{{0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0}, 0x01},
}

The sixteen routings, ordered roughly from most parallel to most serial: 0 is eight independent carriers (additive), 15 is a single eight-deep stack (the most extreme spectrum the voice can make).

◆ operator_count

int pulp::signal::drum::Fm8DrumVoice::operator_count = 8
staticconstexpr

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