A tom, and the analogue electronic-drum voice it generalises. More...
#include <tom.hpp>
Classes | |
| struct | PresetData |
Public Types | |
| enum class | LadderMathProfile : std::uint8_t { reference , realtime_efficient } |
| The noise ladder's saturator, chosen per voice. More... | |
| enum class | Wave { triangle , sine } |
| Body waveform. More... | |
| enum class | Preset { generic_tom , low_tom , mid_tom , hi_tom , bass , snary , zap , cosmic } |
Public Member Functions | |
| TomVoice () | |
| void | set_tune_hz (double hz) |
| Pitch the body settles at, in Hz. | |
| void | set_bend_octaves (double octaves) |
| Depth of the downward dive in octaves, and how quickly it lands. | |
| void | set_bend_ms (double ms) |
| void | set_decay_ms (double ms) |
| How long the note rings, as a T60. Unrelated to the bend. | |
| void | set_wave (Wave wave) |
| void | set_noise_balance (double amount) |
| Balance between the oscillator (0) and the filtered noise (1). | |
| void | set_noise_cutoff_hz (double hz) |
| Corner and resonance of the four-pole lowpass the noise runs through. | |
| void | set_noise_resonance (double amount) |
| void | set_noise_color (NoiseColor color) |
| bool | set_ladder_math_profile (LadderMathProfile profile) noexcept |
| Selects the noise ladder's saturation curve between hits. | |
| LadderMathProfile | ladder_math_profile () const noexcept |
| void | set_click_level (double level) |
| The beater. | |
| void | set_click_cutoff_hz (double hz) |
| void | set_click_decay_ms (double ms) |
| void | apply_preset (Preset preset) |
| LofiChain & | body_lofi () |
| Independent degradation for the two halves of the drum. | |
| const LofiChain & | body_lofi () const |
| LofiChain & | noise_lofi () |
| const LofiChain & | noise_lofi () const |
| 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 std::array< PresetData, 8 > | presets |
| Original Pulp-authored voicings of this one SDS-V-family topology. | |
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. | |
A tom, and the analogue electronic-drum voice it generalises.
The defining property is that the pitch envelope and the amplitude envelope are decoupled. In most percussion voices the body's pitch and its level fall together, because one envelope drives both; here they are separate controls, so the pitch can finish its dive in a few tens of milliseconds while the note rings on for a second afterwards. That is the sound of the early electronic drum kits: a fast downward swoop that arrives at a pitch and then simply sustains there. Tying the two envelopes together cannot produce it – the swoop would take the level with it.
The generic tom and the electronic voice are the same topology, retuned. A short bend and a mostly-noise balance is an acoustic-sounding tom; a deep bend and a pure oscillator is the laser-like electronic voice; a long decay with a high tuning is the "cosmic" end. Presets differ only in numbers, so there is one implementation rather than one per named sound.
Velocity deepens the bend rather than only raising the level, which is the physical claim: a harder strike deflects the head further, so the pitch starts higher and falls through a wider range. The voice's default VelocityResponse enables this, and it is the clearest single example of the velocity-to-timbre contract in the whole percussion set.
The noise path runs through a four-pole resonant lowpass rather than the two-pole used elsewhere, because at high resonance a steeper filter rings enough to give the noise a pitch of its own – which is what lets the same voice cover a snare-like sound without a second oscillator.
RT contract: prepare() allocates nothing; every other method, including note_on and process, allocates nothing and takes no locks.
|
strong |
The noise ladder's saturator, chosen per voice.
Each value runs the LadderSaturation of the same meaning explicitly, so the voice does not follow the build-time kDefaultLadderSaturation: reference is LadderSaturation::exact (std::tanh) and realtime_efficient is LadderSaturation::realtime_efficient (FastMath::tanh_minimax).
| Enumerator | |
|---|---|
| reference | |
| realtime_efficient | |
|
strong |
|
strong |
|
inline |
|
inline |
|
inline |
Independent degradation for the two halves of the drum.
A tom is an oscillator body under a filtered noise skin, and the machines this voice descends from crushed those paths separately – a bit-reduced body under clean hiss reads as a different instrument from a clean body under a decimated skin. One shared chain could only ever produce the average of the two.
|
inline |
|
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.
|
inlineinherited |
Whether the voice still has something to render.
A kit uses this to skip voices entirely rather than summing silence.
|
inlinenoexcept |
|
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.
|
inline |
|
inline |
|
inlineinherited |
Starts a hit. velocity is in [0, 1]; values outside are clamped.
|
inlineoverrideprotectedvirtual |
Implements pulp::signal::drum::Voice.
|
inlineoverrideprotectedvirtual |
Starts a hit at the given velocity, already clamped to [0, 1].
Implements pulp::signal::drum::Voice.
|
inlineoverrideprotectedvirtual |
Sizes buffers and fixes coefficients.
The only hook allowed to allocate.
Implements pulp::signal::drum::Voice.
|
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.
|
inline |
|
inline |
|
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.
|
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.
|
inlinenoexceptinherited |
|
inlineinherited |
Fixes the sample rate and clears all state.
The only method a derived voice may allocate in.
|
inlineinherited |
Adds num_samples of this voice's output into out.
|
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.
|
inlineoverrideprotectedvirtual |
Adds the voice's output into out. Must add, never assign.
Implements pulp::signal::drum::Voice.
|
inlineprotectedvirtualinherited |
Optional stereo realization.
The default is centre-panned and preserves exact mono-sum equivalence.
Reimplemented in pulp::signal::drum::ClapVoice.
|
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.
|
inlineinherited |
|
inline |
|
inline |
Depth of the downward dive in octaves, and how quickly it lands.
This envelope is independent of the amplitude decay below – that separation is the whole point of the voice.
|
inline |
|
inline |
|
inline |
The beater.
Short and highpassed: a tom's attack is the stick, not the head.
|
inline |
How long the note rings, as a T60. Unrelated to the bend.
|
inlinenoexcept |
Selects the noise ladder's saturation curve between hits.
Reference is the default; realtime_efficient uses the bounded FastMath::tanh_minimax.
|
inline |
Balance between the oscillator (0) and the filtered noise (1).
|
inline |
|
inline |
Corner and resonance of the four-pole lowpass the noise runs through.
At high resonance it rings, which gives the noise a pitch.
|
inline |
|
inlineoverridevirtual |
Reimplemented from pulp::signal::drum::Voice.
|
inline |
Pitch the body settles at, in Hz.
|
inlineinherited |
|
inline |
|
inlineinherited |
|
inlineinherited |
|
inlineinherited |
|
staticconstexpr |
Original Pulp-authored voicings of this one SDS-V-family topology.
They are clean starting points, not values copied from a sample library or a commercial preset bank.