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

The lifecycle every percussion voice shares. More...

#include <voice.hpp>

Public Member Functions

virtual ~Voice ()=default
 Voice ()=default
 Voice (const Voice &)=delete
Voice & operator= (const Voice &)=delete
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
virtual int latency_samples () const noexcept
 Fixed host-rate delay introduced by this voice's selected output quality.
virtual OutputOversampling output_oversampling () const noexcept
 Output quality at the generic voice boundary.
virtual void set_output_oversampling (OutputOversampling)
virtual OutputStage * output_stage () noexcept
 The terminal saturation and degradation stage, at the generic voice boundary.
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.

Protected Member Functions

virtual void on_prepare (double sample_rate)=0
 Sizes buffers and fixes coefficients.
virtual void on_reset ()=0
 Clears every piece of state the voice carries, so is_active() becomes false and the next render starts from silence.
virtual void on_note_on (float velocity)=0
 Starts a hit at the given velocity, already clamped to [0, 1].
virtual bool on_is_active () const =0
virtual void render_add (float *out, int num_samples)=0
 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

The lifecycle every percussion voice shares.

Three parts of that lifecycle are implemented here rather than left to each voice, because each of them is a correctness rule that is easy to get subtly wrong in a way nobody notices until a specific arrangement exposes it:

  • process() adds. A voice writes into a buffer that may already hold another voice's output, so a kit is a sum rather than a mixing pass with its own scratch buffers. A voice that assigned instead of added would silence whichever voice rendered before it, which sounds like a choke group misfiring and is hard to trace back to the assignment.
  • Choking fades. A closed hi-hat cutting an open one is an amplitude step, and a step is broadband: cutting a ringing voice instantly puts a click exactly where the new hit lands, which reads as a bad sample rather than as a choke. The base class renders through a short linear fade so every voice gets this without implementing it.
  • Velocity reaches timbre. note_on stores the velocity and the response curve together, so a voice implementation has the timbral terms in hand at the point it needs them and cannot accidentally wire velocity to gain alone.

A voice implements the four on_* hooks and render_add. It must not override the public methods; they carry the rules above.

RT contract: prepare() may allocate in a derived voice; everything else, including process(), note_on(), and choke(), allocates nothing and takes no locks.

Constructor & Destructor Documentation

◆ ~Voice()

virtual pulp::signal::drum::Voice::~Voice ( )
virtualdefault

◆ Voice() [1/2]

pulp::signal::drum::Voice::Voice ( )
default

◆ Voice() [2/2]

pulp::signal::drum::Voice::Voice ( const Voice & )
delete

Member Function Documentation

◆ choke()

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

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
inline

Whether the voice still has something to render.

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

◆ latency_samples()

virtual int pulp::signal::drum::Voice::latency_samples ( ) const
inlinevirtualnoexcept

◆ note_on()

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

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

◆ on_is_active()

◆ on_note_on()

◆ on_prepare()

◆ on_reset()

◆ operator=()

Voice & pulp::signal::drum::Voice::operator= ( const Voice & )
delete

◆ output_oversampling()

virtual OutputOversampling pulp::signal::drum::Voice::output_oversampling ( ) const
inlinevirtualnoexcept

◆ output_stage() [1/2]

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

◆ output_stage() [2/2]

virtual OutputStage * pulp::signal::drum::Voice::output_stage ( )
inlinevirtualnoexcept

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 in pulp::signal::drum::ClapVoice, pulp::signal::drum::CymbalVoice, pulp::signal::drum::Fm6DrumVoice, pulp::signal::drum::Fm8DrumVoice, pulp::signal::drum::FmDrumVoice, pulp::signal::drum::HatVoice, pulp::signal::drum::KickVoice, pulp::signal::drum::MembraneVoice, pulp::signal::drum::SnareVoice, pulp::signal::drum::StringVoice, pulp::signal::drum::TomVoice, and pulp::signal::drum::ZapVoice.

◆ prepare()

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

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 )
inline

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 )
inline

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()

◆ render_add_stereo()

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

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 ( )
inline

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
inline

◆ set_output_oversampling()

◆ set_velocity_response()

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

◆ state()

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

◆ velocity()

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

◆ velocity_response()

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

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