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. | |
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.
|
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.
|
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.
|
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 |
Use a bandpass rather than a highpass.
Narrows the voice toward a specific pitched clank.
|
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.
|
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.
|
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.
|
inline |
|
inline |
Balance between filtered noise (0) and the oscillator cluster (1).
|
inline |
|
inlineoverridevirtual |
Reimplemented from pulp::signal::drum::Voice.
|
inline |
|
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.
|
inline |
Frequency of the lowest oscillator in the bank, in Hz.
|
inlineinherited |
|
inlineinherited |
|
inlineinherited |
|
inlineinherited |
|
staticconstexpr |
Number of oscillators in the metallic bank.
|
staticconstexpr |
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.