A hand clap. More...
#include <clap.hpp>
Public Member Functions | |
| ClapVoice () | |
| void | set_burst_count (int count) |
| How many bursts the train contains. | |
| void | set_burst_spacing_ms (double ms) |
| Milliseconds between bursts. | |
| void | set_burst_decay_ms (double ms) |
| How quickly each burst decays. | |
| void | set_burst_falloff (double ratio) |
| How much quieter each burst is than the one before it, as a ratio. | |
| void | set_gap_jitter (double amount) |
| How much each gap varies from the nominal spacing, 0 to 1. | |
| void | set_alternate_polarity (bool alternate) |
| Invert every other burst. | |
| void | set_stereo_width (double width) |
| Alternating burst width, 0 for mono through 1 for hard left/right. | |
| void | set_tail_level (double level) |
| Level and decay of the tail that fuses the train. | |
| void | set_tail_decay_ms (double ms) |
| void | set_cutoff_hz (double hz) |
| Centre and resonance of the bandpass the noise is heard through. | |
| void | set_resonance (double q) |
| void | set_noise_color (NoiseColor color) |
| void | set_body_level (double level) |
| Level of an optional sine under the train, at set_body_hz. | |
| void | set_body_hz (double hz) |
| 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 int | max_bursts = 8 |
| Upper bound on the burst train. | |
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. | |
| void | render_add_stereo (float *left, float *right, int num_samples) override |
| Optional stereo realization. | |
A hand clap.
A clap is not one sound. Several people clapping never land together, and even one person's clap is the hands meeting, separating slightly, and meeting again. What the ear identifies as "a clap" is that short burst train followed by the room – and the burst train is why a clap cannot be built as a single noise envelope no matter how it is shaped.
So the voice schedules a small number of noise bursts a few milliseconds apart and runs a longer, quieter tail underneath them from the first burst onward. The tail is what fuses the train into one event: without it the bursts are heard as separate ticks, and with it they are heard as one clap in a room. Widening the spacing past about 20 ms breaks that fusion, which is a usable effect and the reason the control is exposed rather than fixed.
The schedule is computed at note_on and is deterministic, so a clap renders identically for a given parameter set.
process() is the canonical mono realization. process_stereo() places successive bursts on alternating sides while keeping the tail and optional body in the centre. The two stereo channels sum to the canonical mono signal sample for sample, so a host can collapse the result without a level or timbre change.
What a mono sum does cost is decorrelation. Identically spaced, identically signed bursts are coherent with each other, so summing them combs: the train acquires a faint pitch at one over the gap that no single burst has. Two controls exist to break that up, and at least one of them wants to be on: set_gap_jitter varies the spacing per burst, and set_alternate_polarity flips every other burst. Both stay deterministic, so a render is still reproducible.
RT contract: prepare() allocates nothing; every other method allocates nothing and takes no locks.
|
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.
|
inlineoverrideprotectedvirtual |
Optional stereo realization.
The default is centre-panned and preserves exact mono-sum equivalence.
Reimplemented from pulp::signal::drum::Voice.
|
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 |
Invert every other burst.
A cheaper decorrelation than jitter and free of any timing change: opposite-signed bursts cannot reinforce each other's comb even at exactly even spacing.
|
inline |
|
inline |
Level of an optional sine under the train, at set_body_hz.
Some hardware claps carry one; it gives the voice a pitch centre and helps it sit in a mix that is otherwise busy in the clap's band.
|
inline |
How many bursts the train contains.
|
inline |
How quickly each burst decays.
|
inline |
How much quieter each burst is than the one before it, as a ratio.
The hands lose energy each time they meet, and a train at constant level reads as a machine rather than a person.
|
inline |
Milliseconds between bursts.
|
inline |
Centre and resonance of the bandpass the noise is heard through.
A clap occupies a narrow, quite high band; without the filter the voice is simply noise bursts.
|
inline |
How much each gap varies from the nominal spacing, 0 to 1.
Perfectly even bursts comb against each other and give the train a pitch; hands are never that regular anyway. Driven by a fixed sequence, so this adds irregularity without costing reproducibility.
|
inline |
|
inlineoverridevirtual |
Reimplemented from pulp::signal::drum::Voice.
|
inline |
|
inline |
Alternating burst width, 0 for mono through 1 for hard left/right.
The room tail and optional tonal body remain centred at every setting.
|
inline |
|
inline |
Level and decay of the tail that fuses the train.
|
inlineinherited |
|
inlineinherited |
|
inlineinherited |
|
inlineinherited |
|
staticconstexpr |
Upper bound on the burst train.
Beyond about eight the train stops reading as a clap and starts reading as a roll.