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

A six-operator FM voice with the classic thirty-two algorithm set. More...

#include <fm6.hpp>

Classes

struct  Algorithm
 One routing. More...

Public Member Functions

 Fm6DrumVoice ()
void set_algorithm (int index)
int algorithm () const
void set_tune_hz (double hz)
void set_operator_ratio (int op, double ratio)
void set_operator_level (int op, double level)
void set_operator_decay_ms (int op, double ms)
void set_feedback (double amount)
 Depth of the routing's designated feedback operator, 0 to 1.
void set_depth (double depth)
 Overall modulation depth, scaling every routed connection.
void set_pitch_sweep_octaves (double octaves)
 Global pitch envelope: every operator's frequency is scaled together, so the spectrum keeps its shape while the whole voice sweeps.
void set_pitch_sweep_ms (double ms)
void set_formant_hz (double hz)
void set_formant_q (double q)
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 = 6
static constexpr int algorithm_count = 32
static constexpr Algorithm algorithms [algorithm_count]

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

A six-operator FM voice with the classic thirty-two algorithm set.

Six operators rather than the eight of Fm8DrumVoice, because six is what makes this instrument the thing it is. The constraint is the character: with only six operators an algorithm has to spend them, so the familiar routings are each a specific compromise between how many partials reach the output and how deeply they are modulated. A wider engine does not sound like this one with more headroom — it sounds like a different instrument. Fm8DrumVoice is the more capable engine; this is the more recognisable one.

The architecture is six operators, thirty-two fixed routings, one feedback operator per routing, and a global pitch envelope. Envelope shaping is DecayEnvelopeT, so an operator's contour is described in the same milliseconds-and-T60 terms as every other voice here rather than in device-specific rate and level units.

The routing table is transcribed from published descriptions of the instrument's architecture. Three rules the documentation states about the original algorithm set are asserted over every row, which is what makes the transcription checkable rather than merely plausible:

  • Modulation only ever flows downward. The set is laid out with the highest-numbered operator at the top, so an operator is modulated only by higher-numbered ones. This is the strongest of the three — it rejects any row whose arrows point the wrong way, which is the easiest transcription error to make and the hardest to hear.
  • At most one self-feedback loop per routing, which is why feedback_op is a single index rather than a mask.
  • Carrier outputs are scaled by the carrier count, so routings with different numbers of carriers arrive at comparable levels; the fully additive routing is scaled by a sixth. render_add does this.

Alongside those, the structural invariants: at least one carrier, no operator modulating itself through the mask, every index in range, and every operator reachable from a carrier. Algorithm 8 is additionally pinned against its documented topology as a worked example.

What none of that proves is that a given row is the routing that number names. A row can satisfy every rule above and still be the wrong shape. Anything depending on patch-level algorithm compatibility should verify the full table against the operation manual's diagrams first.

Lineage: Chowning, JAES 21(7), 1973, for the synthesis technique.

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

Constructor & Destructor Documentation

◆ Fm6DrumVoice()

pulp::signal::drum::Fm6DrumVoice::Fm6DrumVoice ( )
inline

Member Function Documentation

◆ algorithm()

int pulp::signal::drum::Fm6DrumVoice::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::Fm6DrumVoice::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::Fm6DrumVoice::on_is_active ( ) const
inlineoverrideprotectedvirtual

◆ on_note_on()

void pulp::signal::drum::Fm6DrumVoice::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::Fm6DrumVoice::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::Fm6DrumVoice::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::Fm6DrumVoice::output ( )
inline

◆ output() [2/2]

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

◆ output_oversampling()

OutputOversampling pulp::signal::drum::Fm6DrumVoice::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::Fm6DrumVoice::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::Fm6DrumVoice::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::Fm6DrumVoice::set_algorithm ( int index)
inline

◆ set_depth()

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

Overall modulation depth, scaling every routed connection.

◆ set_feedback()

void pulp::signal::drum::Fm6DrumVoice::set_feedback ( double amount)
inline

Depth of the routing's designated feedback operator, 0 to 1.

◆ set_formant_hz()

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

◆ set_formant_q()

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

◆ set_operator_decay_ms()

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

◆ set_operator_level()

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

◆ set_operator_ratio()

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

◆ set_output_oversampling()

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

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

◆ set_pitch_sweep_ms()

void pulp::signal::drum::Fm6DrumVoice::set_pitch_sweep_ms ( double ms)
inline

◆ set_pitch_sweep_octaves()

void pulp::signal::drum::Fm6DrumVoice::set_pitch_sweep_octaves ( double octaves)
inline

Global pitch envelope: every operator's frequency is scaled together, so the spectrum keeps its shape while the whole voice sweeps.

◆ set_tune_hz()

void pulp::signal::drum::Fm6DrumVoice::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::Fm6DrumVoice::algorithm_count = 32
staticconstexpr

◆ algorithms

Algorithm pulp::signal::drum::Fm6DrumVoice::algorithms[algorithm_count]
staticconstexpr

◆ operator_count

int pulp::signal::drum::Fm6DrumVoice::operator_count = 6
staticconstexpr

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