64 r.bend_octaves = 0.15f;
65 r.brightness_octaves = 1.5f;
71 click_.set_level(0.3);
72 click_.set_cutoff_hz(4000.0);
73 click_.set_decay_ms(2.0);
74 noise_layer_.set_decay_ms(60.0);
81 apply_circuit_tuning();
87 tune_hz_ = std::clamp(hz, 20.0, 400.0);
88 apply_circuit_tuning();
105 pitch_sweep_oct_ = std::clamp(octaves, 0.0, 6.0);
130 return output_.latency_samples();
133 return output_.oversampling();
136 output_.set_oversampling(factor);
147 void set_fm_amount(
double amount) { fm_amount_ = std::clamp(amount, 0.0, 8.0); }
155 authored_components_ =
c;
156 apply_circuit_tuning();
194 circuit_drive_ = std::clamp(amount, 0.0, 1.0);
204 circuit_life_.set_mode(mode);
220 shaper_.set_cutoff(
static_cast<float>(kShaperHz));
222 tone_.set_cutoff(
static_cast<float>(kToneHz));
229 noise_layer_.reset();
233 circuit_.set_attack_shunt(
false);
236 circuit_life_.reset();
243 pulse_remaining_ = 0;
244 shunt_remaining_ = 0;
245 excite_remaining_ = 0;
257 output_.reset_nonlinear_state();
260 velocity_gain_ = response.gain(
velocity);
261 bend_octaves_ = pitch_sweep_oct_ + response.bend(
velocity);
262 brightness_ = response.brightness_scale(
velocity);
270 pitch_env_.set_decay_time_constant_ms(pitch_sweep_ms_);
271 pitch_env_.set_attack_ms(0.0);
272 pitch_env_.trigger();
274 body_env_.set_attack_ms(0.4);
275 body_env_.set_decay_t60_ms(body_decay_ms_);
279 click_.trigger(brightness_);
280 noise_layer_.trigger();
283 switch (body_mode_) {
290 resonator_.set_frequency(tune_hz_);
291 resonator_.set_t60_ms(body_decay_ms_);
295 excite_remaining_ = std::max(
304 reset_circuit_memory();
306 noise_.set_seed(circuit_life.
seed);
310 circuit_excitation_gain_ =
311 1.0 + 0.03 *
static_cast<double>(noise_.white());
312 pulse_remaining_ = std::max(
313 1,
static_cast<int>(0.001 * circuit_pulse_ms_ *
sample_rate()));
315 static_cast<int>(0.001 * circuit_attack_ms_ *
sample_rate());
316 circuit_.set_attack_shunt(shunt_remaining_ > 0);
323 return body_env_.is_active() || layers_active() ||
324 pulse_remaining_ > 0 || ring_level_ > kRingSilenceLevel ||
328 return resonator_.is_ringing() || layers_active() || excite_remaining_ > 0 ||
331 return body_env_.is_active() || layers_active() || output_.has_tail();
335 for (
int i = 0;
i < num_samples; ++
i) {
336 const double body_env = body_env_.process();
339 switch (body_mode_) {
345 const double click = click_.process(
static_cast<double>(noise_.white()));
346 const double noise = noise_layer_.process(
static_cast<double>(noise_.process()));
347 const double sub = sub_.process(tune_hz_, body_env);
349 const double mixed =
body + click + noise + sub;
350 out[
i] +=
static_cast<float>(
351 output_.process(
static_cast<float>(mixed)) * velocity_gain_);
354 circuit_.snap_denormals();
365 void apply_circuit_tuning() {
368 : authored_components_);
371 void reset_circuit_memory() {
378 pulse_remaining_ = 0;
379 shunt_remaining_ = 0;
382 double render_oscillator_body(
double body_env) {
383 const double pitch_env = pitch_env_.
process();
384 const double f = tune_hz_ * std::exp2(bend_octaves_ * pitch_env);
386 double modulation = 0.0;
387 if (fm_amount_ > 0.0) {
389 if (mod_phase_ >= 1.0) mod_phase_ -= std::floor(mod_phase_);
390 modulation = fm_amount_ * std::sin(2.0 * 3.14159265358979323846 * mod_phase_);
394 if (phase_ >= 1.0) phase_ -= std::floor(phase_);
396 const double angle = 2.0 * 3.14159265358979323846 * phase_ + modulation;
397 const double shape = triangle_ ? triangle_from_phase(angle) :
std::sin(angle);
398 return shape * body_env;
401 double render_resonant_body() {
402 double excitation = 0.0;
403 if (excite_remaining_ > 0) {
404 excitation =
static_cast<double>(noise_.white());
407 return static_cast<double>(resonator_.process(
static_cast<float>(excitation)));
410 double render_circuit_body() {
416 if (pulse_remaining_ > 0) {
420 const double shaped = shaper_.process_lowpass(
static_cast<float>(
pulse));
421 const double diode = shaped >= 0.0
423 : kDiodeVt * (std::exp(shaped / kDiodeVt) - 1.0);
425 if (shunt_remaining_ > 0) {
427 if (shunt_remaining_ == 0) circuit_.set_attack_shunt(
false);
430 const double drive_volts =
431 kPulseVoltsMin + circuit_drive_ * (kPulseVoltsMax - kPulseVoltsMin);
432 const auto ring = circuit_.process(
433 diode * drive_volts * circuit_excitation_gain_, feedback_z_, 0.0);
442 feedback_z_ = -circuit_feedback_ * kMaxLoopGain * kFeedbackKnee *
443 std::tanh(kSwellGain *
ring.vbt / kFeedbackKnee);
446 tone_.process_lowpass(
static_cast<float>(
ring.vbt)) * kCircuitOutputScale;
451 ring_level_ = std::max(std::fabs(out), ring_level_ * kRingFollowerDecay);
455 static double triangle_from_phase(
double angle) {
456 const double normalised =
457 angle / (2.0 * 3.14159265358979323846) - std::floor(angle / (2.0 * 3.14159265358979323846));
458 return 2.0 * std::fabs(2.0 * normalised - 1.0) - 1.0;
461 bool layers_active()
const {
return click_.is_active() || noise_layer_.is_active(); }
478 static constexpr double kPulseVoltsMin = 1.0;
479 static constexpr double kPulseVoltsMax = 1.7;
480 static constexpr double kCircuitOutputScale = 0.1;
481 static constexpr double kShaperHz = 120.0;
482 static constexpr double kToneHz = 4000.0;
483 static constexpr double kDiodeVt = 0.03;
484 static constexpr double kSwellGain = 1.1;
485 static constexpr double kFeedbackKnee = 8.0;
488 static constexpr double kMaxLoopGain = 0.86;
490 static constexpr double kRingFollowerDecay = 0.9995;
491 static constexpr double kRingSilenceLevel = 1
e-5;
495 double tune_hz_ = 55.0;
498 BridgedTComponents authored_components_{};
499 double body_decay_ms_ = 400.0;
500 double pitch_sweep_oct_ = 2.0;
501 double pitch_sweep_ms_ = 30.0;
502 bool triangle_ =
false;
503 double fm_amount_ = 0.0;
504 double fm_ratio_ = 1.0;
506 double circuit_feedback_ = 0.85;
507 double circuit_drive_ = 0.3;
508 double circuit_attack_ms_ = 4.0;
509 double circuit_pulse_ms_ = 2.0;
515 NoiseLayer noise_layer_;
518 BridgedTResonator circuit_;
522 double velocity_gain_ = 1.0;
523 double bend_octaves_ = 2.0;
524 double brightness_ = 1.0;
530 double mod_phase_ = 0.0;
531 double feedback_z_ = 0.0;
532 double ring_level_ = 0.0;
533 double circuit_excitation_gain_ = 1.0;
534 int pulse_remaining_ = 0;
535 int shunt_remaining_ = 0;
536 int excite_remaining_ = 0;
void set_components(const BridgedTComponents &c) noexcept
Definition bridged_t_resonator.hpp:204
void set_attack_shunt(bool active) noexcept
Ground Q43's collector, shorting out R165.
Definition bridged_t_resonator.hpp:242
void reset() noexcept
Clear the ring.
Definition bridged_t_resonator.hpp:231
SampleType process()
Next envelope sample in [0, peak].
Definition decay_envelope.hpp:125
static constexpr std::uint32_t default_seed
Definition noise_source.hpp:55
void reset()
Definition tpt_filter.hpp:127
void set_oscillator_triangle(bool triangle)
Triangle instead of sine.
Definition kick.hpp:143
OutputOversampling output_oversampling() const noexcept override
Output quality at the generic voice boundary.
Definition kick.hpp:132
void set_click_decay_ms(double ms)
Definition kick.hpp:114
void set_fm_ratio(double ratio)
Definition kick.hpp:148
void set_pitch_sweep_octaves(double octaves)
Depth of the downward pitch sweep, in octaves, and how quickly it settles.
Definition kick.hpp:104
OutputStage * output_stage() noexcept override
The terminal saturation and degradation stage, at the generic voice boundary.
Definition kick.hpp:128
void set_circuit_attack_ms(double ms)
How long the attack shunt grounds the transistor's collector after a trigger.
Definition kick.hpp:174
void set_tune_hz(double hz)
Fundamental of the body, in Hz.
Definition kick.hpp:86
HitLifeMode circuit_hit_life() const
Definition kick.hpp:206
void set_sub_level(double level)
Level of the sub layer, a sine an octave below the body gated by the body's own envelope.
Definition kick.hpp:123
void set_circuit_drive(double amount)
How hard the trigger drives the network, 0 to 1.
Definition kick.hpp:193
void on_reset() override
Clears every piece of state the voice carries, so is_active() becomes false and the next render start...
Definition kick.hpp:225
KickVoice()
Definition kick.hpp:58
void set_body_decay_ms(double ms)
How long the body rings, as a T60 in milliseconds.
Definition kick.hpp:92
void set_noise_color(NoiseColor color)
Definition kick.hpp:119
void on_note_on(float velocity) override
Starts a hit at the given velocity, already clamped to [0, 1].
Definition kick.hpp:248
void set_click_tone_hz(double hz)
Definition kick.hpp:113
bool on_is_active() const override
Definition kick.hpp:321
void on_prepare(double sample_rate) override
Sizes buffers and fixes coefficients.
Definition kick.hpp:209
void set_circuit_pulse_ms(double ms)
Width of the trigger pulse into the pulse shaper, in milliseconds.
Definition kick.hpp:177
OutputStage & output()
The saturation and degradation stage the voice ends with.
Definition kick.hpp:126
void set_noise_level(double level)
Level and decay of the noise layer.
Definition kick.hpp:117
void set_noise_decay_ms(double ms)
Definition kick.hpp:118
int latency_samples() const noexcept override
Fixed host-rate delay introduced by this voice's selected output quality.
Definition kick.hpp:129
void set_circuit_sigh(bool enabled)
Disconnect the leakage path that produces the pitch sigh.
Definition kick.hpp:199
void set_circuit_hit_life(HitLifeMode mode)
Select whether circuit retriggers restart, vary their excitation, or preserve the network memory.
Definition kick.hpp:203
void set_body(KickBody body)
Definition kick.hpp:79
void set_circuit_feedback(double gain)
Regeneration around the network, 0 to 1.
Definition kick.hpp:169
void render_add(float *out, int num_samples) override
Adds the voice's output into out. Must add, never assign.
Definition kick.hpp:334
const OutputStage & output() const
Definition kick.hpp:127
KickBody body() const
Definition kick.hpp:83
void set_circuit_components(const BridgedTComponents &c)
Component values of the bridged-T network.
Definition kick.hpp:154
void set_fm_amount(double amount)
Phase-modulation depth and the modulator's frequency ratio.
Definition kick.hpp:147
void set_click_level(double level)
Level of the attack click, and the corner of the one-pole that tones it.
Definition kick.hpp:112
void set_output_oversampling(OutputOversampling factor) override
Definition kick.hpp:135
void set_pitch_sweep_ms(double ms)
Definition kick.hpp:107
double sample_rate() const
Definition voice.hpp:105
void set_velocity_response(const VelocityResponse &r)
Definition voice.hpp:129
float velocity() const
Definition voice.hpp:104
const VelocityResponse & velocity_response() const
Definition voice.hpp:130
@ ring
Definition string.hpp:18
HitLifeMode
Selects the intentional source of variation between otherwise identical hits.
Definition hit_life.hpp:26
@ preserved_state
Definition hit_life.hpp:29
OutputOversampling
Realtime quality choices for the shared drum output nonlinearity.
Definition output_stage.hpp:20
KickBody
How a kick's body is produced.
Definition kick.hpp:20
@ resonant
A struck resonance.
Definition kick.hpp:29
@ oscillator
A swept oscillator.
Definition kick.hpp:24
@ circuit
The TR-808's bridged-T network in its op-amp feedback loop.
Definition kick.hpp:37
OutputStageT< float > OutputStage
Definition output_stage.hpp:384
@ c
Definition spectrum_trace.hpp:35
TwoPoleResonatorT< float > TwoPoleResonator
Definition two_pole_resonator.hpp:118
TptFilterT< float > TptFilter
Definition tpt_filter.hpp:172
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
BridgedTComponents bridged_t_retuned_to(const BridgedTComponents &c, double target_hz) noexcept
Quality factor of the bridged-T network alone, with its three inputs grounded.
Definition bridged_t_resonator.hpp:143
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ triangle
Definition lfo_sampling.hpp:55
@ pulse
a ramp then a hold: sine becomes pulse-like
Definition phase_distortion.hpp:11
NoiseColor
Spectral tilt of a NoiseSourceT, named by the usual colour convention.
Definition noise_source.hpp:17
DecayEnvelopeT< double > DecayEnvelope64
Definition decay_envelope.hpp:201
NoiseSourceT< float > NoiseSource
Definition noise_source.hpp:279
Component values of the TR-808 bass drum's bridged-T network, in SI units (ohms, farads).
Definition bridged_t_resonator.hpp:21
Definition hit_life.hpp:34
bool reseed_excitation
Definition hit_life.hpp:36
std::uint32_t seed
Definition hit_life.hpp:35
bool reset_dsp_state
Definition hit_life.hpp:37
How hard a hit changes the sound, beyond changing how loud it is.
Definition velocity.hpp:33
First-order topology-preserving transform (TPT) filter.