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

A hi-hat, and by extension every other struck metal in a kit. More...

#include <hat.hpp>

Public Member Functions

 HatVoice ()
void set_tune_hz (double hz)
 Frequency of the lowest oscillator in the bank, in Hz.
void set_decay_ms (double ms)
 How long the metal rings, as a T60 in milliseconds.
void set_spread (double amount)
 How far the bank is spread from unison (0) to its full inharmonic cluster (1).
void set_metal (double amount)
 Balance between filtered noise (0) and the oscillator cluster (1).
void set_grit (double amount)
 Depth of a ring modulation applied to the source before filtering, and the modulator's ratio to the tuning.
void set_grit_ratio (double ratio)
void set_cutoff_hz (double hz)
 Corner and resonance of the output filter.
void set_resonance (double q)
void set_bandpass (bool bandpass)
 Use a bandpass rather than a highpass.
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.

Static Public Attributes

static constexpr std::size_t partial_count = 6
 Number of oscillators in the metallic bank.
static constexpr double partial_ratios [partial_count]
 Frequency ratios of the TR-808's hi-hat oscillator bank, relative to its lowest oscillator.

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 hi-hat, and by extension every other struck metal in a kit.

Closed hat, open hat, ride and crash are one voice with one control changed. They are not different instruments: they are the same pair of cymbals excited the same way and allowed to ring for different lengths of time, which is exactly what set_decay_ms spans. Building them as four voices would mean four things to keep in tune with each other; building them as one means a kit pairs two instances in a choke group and gets the closed-cuts-open behaviour for free.

The metallic source is a bank of square oscillators at deliberately inharmonic frequencies. Two reasons it is squares and not sines: a square already carries only odd harmonics, so six of them at incommensurate pitches fill the spectrum densely, and the ratios below are the ones the TR-808's own six-oscillator bank used. The ratios are stored relative to the lowest oscillator rather than as absolute frequencies so that tuning the voice moves the whole cluster and preserves its character.

Blending that cluster against filtered noise is what spans the two eras of drum machine in one control: an all-oscillator hat is the 808's, an all-noise hat is closer to the sampled machines that followed.

RT contract: prepare() sizes the oscillator bank and may allocate. Everything else, including note_on and process, allocates nothing.

Constructor & Destructor Documentation

◆ HatVoice()

pulp::signal::drum::HatVoice::HatVoice ( )
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.

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

◆ on_note_on()

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

◆ output() [2/2]

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

◆ output_oversampling()

OutputOversampling pulp::signal::drum::HatVoice::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::HatVoice::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::HatVoice::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_bandpass()

void pulp::signal::drum::HatVoice::set_bandpass ( bool bandpass)
inline

Use a bandpass rather than a highpass.

Narrows the voice toward a specific pitched clank.

◆ set_cutoff_hz()

void pulp::signal::drum::HatVoice::set_cutoff_hz ( double hz)
inline

Corner and resonance of the output filter.

A hat is defined as much by what is removed as by what is generated: the source is broadband, and the highpass is what leaves only the part that reads as metal.

◆ set_decay_ms()

void pulp::signal::drum::HatVoice::set_decay_ms ( double ms)
inline

How long the metal rings, as a T60 in milliseconds.

This single control is the closed-to-open-to-ride-to-crash continuum: tens of milliseconds is a closed hat, hundreds is open, seconds is a cymbal.

◆ set_grit()

void pulp::signal::drum::HatVoice::set_grit ( double amount)
inline

Depth of a ring modulation applied to the source before filtering, and the modulator's ratio to the tuning.

Ring modulation of an already inharmonic cluster produces sum and difference tones that belong to no series at all, which is the grit a hi-hat has and a filtered square does not.

◆ set_grit_ratio()

void pulp::signal::drum::HatVoice::set_grit_ratio ( double ratio)
inline

◆ set_metal()

void pulp::signal::drum::HatVoice::set_metal ( double amount)
inline

Balance between filtered noise (0) and the oscillator cluster (1).

◆ set_noise_color()

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

◆ set_output_oversampling()

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

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

◆ set_resonance()

void pulp::signal::drum::HatVoice::set_resonance ( double q)
inline

◆ set_spread()

void pulp::signal::drum::HatVoice::set_spread ( double amount)
inline

How far the bank is spread from unison (0) to its full inharmonic cluster (1).

At zero every oscillator sits on the fundamental and the voice is a square wave; the metallic quality is entirely in the spread.

◆ set_tune_hz()

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

Frequency of the lowest oscillator in the bank, 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

Member Data Documentation

◆ partial_count

std::size_t pulp::signal::drum::HatVoice::partial_count = 6
staticconstexpr

Number of oscillators in the metallic bank.

◆ partial_ratios

double pulp::signal::drum::HatVoice::partial_ratios[partial_count]
staticconstexpr
Initial value:
= {
1.0, 1.4827, 1.8003, 2.5461, 2.6303, 3.8967}

Frequency ratios of the TR-808's hi-hat oscillator bank, relative to its lowest oscillator.

The absolute frequencies these come from are documented in the machine's service manual; they are stored as ratios here so set_tune_hz moves the cluster without changing its shape.


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