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

A plucked or struck string, as a percussion voice. More...

#include <string.hpp>

Public Member Functions

 StringVoice ()
void set_tune_hz (double hz)
 Pitch of the string, in Hz.
void set_decay_seconds (double seconds)
 Time for the note to fall by 60 dB, in seconds.
void set_damping (double amount)
 How much faster the upper partials die than the fundamental.
void set_stiffness (double amount)
 Inharmonicity from string stiffness, 0 (a true harmonic series) to 1.
void set_pluck_position (double position)
 Where along the string it is excited, 0 (at the end) to 0.5 (the middle).
void set_exciter_ms (double ms)
 Length of the excitation burst in milliseconds.
void set_brightness_hz (double hz)
 Bandwidth of the excitation in Hz at zero velocity, before the velocity response opens it.
void set_pick_direction (double amount)
 Darkens the attack without retuning the string.
void set_restart_on_hit (bool restart)
 Whether a new hit restarts the string or adds to what is still ringing.
void set_modulation (StringModulation mode)
 Optional post-body modulation.
void set_modulation_mix (double mix)
void set_modulation_ratio (double ratio)
void set_fm_depth_octaves (double octaves)
void set_lpg_amount (double amount)
 Amount of the lowpass-gate stage after modulation.
LowpassGate & gate ()
void set_noise_color (NoiseColor color)
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.

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 plucked or struck string, as a percussion voice.

The body is a delay line fed back through its own losses, so the harmonics are not written down anywhere – they are the frequencies whose period divides the loop, and everything else cancels going round. That is what separates this from an oscillator bank: the excitation is part of the sound, so the same string struck with a stick and plucked with a fingertip are genuinely different, and a second hit during the tail adds to what is still travelling rather than replacing it.

It sits in the percussion set because a struck string is percussion – a hammered dulcimer, a kalimba, a piano. What makes it a voice rather than a raw KarplusStrongT is the lifecycle every other drum here has: an excitation whose length and colour are controls, velocity that reaches timbre rather than only level, and the shared output stage.

Velocity drives the string's dynamic-level filter, which is the physical claim: playing harder puts more high-frequency energy into the string, so a hard note is brighter as well as louder. That is the same coupling the tom makes with its bend and the FM voices make with their index.

RT contract: prepare() allocates the delay lines. Everything else, including note_on and process, allocates nothing and takes no locks.

Constructor & Destructor Documentation

◆ StringVoice()

pulp::signal::drum::StringVoice::StringVoice ( )
inline

Member Function Documentation

◆ 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.

◆ gate()

LowpassGate & pulp::signal::drum::StringVoice::gate ( )
inline

◆ 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::StringVoice::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::StringVoice::on_is_active ( ) const
inlineoverrideprotectedvirtual

◆ on_note_on()

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

◆ output() [2/2]

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

◆ output_oversampling()

OutputOversampling pulp::signal::drum::StringVoice::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::StringVoice::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::StringVoice::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_brightness_hz()

void pulp::signal::drum::StringVoice::set_brightness_hz ( double hz)
inline

Bandwidth of the excitation in Hz at zero velocity, before the velocity response opens it.

Deliberately well below the audio band. This is the floor the velocity coupling works up from, so setting it wide leaves velocity nothing to do – the excitation is already passing everything and a hard hit sounds like a soft one turned up. A dark floor is what makes the coupling audible.

◆ set_damping()

void pulp::signal::drum::StringVoice::set_damping ( double amount)
inline

How much faster the upper partials die than the fundamental.

◆ set_decay_seconds()

void pulp::signal::drum::StringVoice::set_decay_seconds ( double seconds)
inline

Time for the note to fall by 60 dB, in seconds.

Derived per round trip, so a high note decays faster at the same setting – as a real string does.

◆ set_exciter_ms()

void pulp::signal::drum::StringVoice::set_exciter_ms ( double ms)
inline

Length of the excitation burst in milliseconds.

Short is a hard mallet; longer is a fingertip.

◆ set_fm_depth_octaves()

void pulp::signal::drum::StringVoice::set_fm_depth_octaves ( double octaves)
inline

◆ set_lpg_amount()

void pulp::signal::drum::StringVoice::set_lpg_amount ( double amount)
inline

Amount of the lowpass-gate stage after modulation.

Zero is a transparent bypass; one applies the full coupled level/brightness decay.

◆ set_modulation()

void pulp::signal::drum::StringVoice::set_modulation ( StringModulation mode)
inline

Optional post-body modulation.

mix crossfades from the unmodified string to the selected mode. FM bends the delay length, Ring multiplies by a sine, and Sync substitutes a slave saw reset by the string pitch. The independent FM path is armed on the next hit after FM is selected; this keeps the default and non-FM realtime paths at one string model.

◆ set_modulation_mix()

void pulp::signal::drum::StringVoice::set_modulation_mix ( double mix)
inline

◆ set_modulation_ratio()

void pulp::signal::drum::StringVoice::set_modulation_ratio ( double ratio)
inline

◆ set_noise_color()

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

◆ set_output_oversampling()

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

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

◆ set_pick_direction()

void pulp::signal::drum::StringVoice::set_pick_direction ( double amount)
inline

Darkens the attack without retuning the string.

◆ set_pluck_position()

void pulp::signal::drum::StringVoice::set_pluck_position ( double position)
inline

Where along the string it is excited, 0 (at the end) to 0.5 (the middle).

A comb, not a level: it removes the partials with a node at that point.

◆ set_restart_on_hit()

void pulp::signal::drum::StringVoice::set_restart_on_hit ( bool restart)
inline

Whether a new hit restarts the string or adds to what is still ringing.

Adding is the physical behaviour and is why a fast repeated figure does not sound like one sample fired twice.

◆ set_stiffness()

void pulp::signal::drum::StringVoice::set_stiffness ( double amount)
inline

Inharmonicity from string stiffness, 0 (a true harmonic series) to 1.

This is what separates a struck bar or a piano from a guitar.

◆ set_tune_hz()

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

Pitch of the string, in Hz.

◆ 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

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