55 2.59, 3.16, 3.71, 4.31};
60 r.brightness_octaves = 1.2f;
65 void set_tune_hz(
double hz) { tune_hz_ = std::clamp(hz, 40.0, 2000.0); }
68 void set_decay_ms(
double ms) { decay_ms_ = std::clamp(ms, 50.0, 8000.0); }
72 void set_decay_tilt(
double tilt) { decay_tilt_ = std::clamp(tilt, 0.5, 1.0); }
78 high_mode_emphasis_db_ = std::clamp(db, -18.0, 18.0);
86 velocity_feedback_ = std::clamp(amount, 0.0, 1.0);
89 velocity_high_mode_db_ = std::clamp(db, 0.0, 18.0);
95 upper_highpass_hz_ = std::clamp(hz, 0.0, 8000.0);
100 void set_spread(
double amount) { spread_ = std::clamp(amount, 0.0, 1.0); }
104 inharmonicity_ = std::clamp(amount, 0.0, 1.0);
109 void set_shift_hz(
double hz) { shift_hz_ = std::clamp(hz, -400.0, 400.0); }
118 void set_tone_hz(
double hz) { tone_hz_ = std::clamp(hz, 500.0, 20000.0); }
127 variation_ = std::clamp(amount, 0.0, 1.0);
142 return output_.latency_samples();
145 return output_.oversampling();
148 output_.set_oversampling(factor);
156 const int longest =
static_cast<int>(
sample_rate / 40.0) + 4;
157 for (
auto& comb : combs_) comb.prepare(longest);
158 for (
auto& damper : dampers_) damper.prepare(
static_cast<float>(
sample_rate));
159 for (
auto&
highpass : upper_highpasses_) {
180 velocity_gain_ = response.gain(
velocity);
181 const double brightness = response.brightness_scale(
velocity);
185 if (life.reset_dsp_state) {
187 output_.reset_nonlinear_state();
189 if (life.reseed_excitation) {
190 noise_.set_seed(life.seed);
196 shifter_.set_shift_hz(shift_hz_);
197 tone_.set_cutoff(
static_cast<float>(
198 std::min(tone_hz_ * brightness, 0.49 *
sample_rate())));
199 low_cut_.set_cutoff(
static_cast<float>(low_cut_hz_));
201 strike_env_.set_attack_ms(0.0);
202 strike_env_.set_decay_time_constant_ms(strike_ms_);
203 strike_env_.trigger();
209 return strike_env_.is_active() || level_ > kSilenceLevel ||
214 for (
int i = 0;
i < num_samples; ++
i) {
215 const double strike = strike_env_.is_active() ? strike_env_.process() : 0.0;
217 double excitation = 0.0;
219 excitation =
static_cast<double>(noise_.process()) * noise_level_;
220 if (strike_level_ > 0.0) {
223 strike_phase_ += tune_hz_ * (1.0 + 3.0 * strike) /
sample_rate();
224 if (strike_phase_ >= 1.0) strike_phase_ -= std::floor(strike_phase_);
225 excitation += std::sin(2.0 * 3.14159265358979323846 * strike_phase_) *
228 excitation *= strike;
233 const auto index =
static_cast<std::size_t
>(
c);
234 const float delayed =
235 combs_[index].read(
static_cast<float>(delay_samples_[index]));
236 float damped = dampers_[index].process_lowpass(delayed);
237 if (
c >= kUpperCombFirst) {
238 const float highpassed =
239 upper_highpasses_[index].process_highpass(damped);
240 if (upper_highpass_hz_ > 0.0) damped = highpassed;
242 combs_[index].push(
static_cast<float>(
243 excitation +
static_cast<double>(damped) * feedback_[index]));
244 summed +=
static_cast<double>(delayed) * mode_gain_[index];
250 const double shifted =
252 ?
static_cast<double>(shifter_.process(
static_cast<float>(summed)))
255 const double cleared = low_cut_.process_highpass(
static_cast<float>(shifted));
256 const double toned = tone_.process_lowpass(
static_cast<float>(cleared));
257 const double shaped =
258 output_.process(
static_cast<float>(toned)) * velocity_gain_;
260 out[
i] +=
static_cast<float>(shaped);
261 level_ = std::max(std::fabs(shaped), level_ * kLevelDecay);
266 static constexpr double kSilenceLevel = 1
e-5;
267 static constexpr double kLevelDecay = 0.99995;
269 void reset_dsp_memory() {
270 for (
auto& comb : combs_) comb.reset();
271 for (
auto& damper : dampers_) damper.reset();
278 void update_combs() {
280 const auto index =
static_cast<std::size_t
>(
c);
283 const double base = 1.0 + spread_ * (
comb_ratios[index] - 1.0);
284 const double pushed =
285 base * (1.0 + inharmonicity_ * 0.01 *
static_cast<double>(
c *
c));
286 const double f = std::min(tune_hz_ * pushed, 0.45 *
sample_rate());
287 delay_samples_[index] = std::max(
sample_rate() / f, 2.0);
295 const double trips = std::max(0.001 * decay_ms_ * f, 1.0);
296 const double tilt = std::pow(decay_tilt_,
static_cast<double>(
c));
297 const double upper_position =
298 static_cast<double>(
c) /
static_cast<double>(
comb_count - 1);
299 const double velocity_ring =
300 1.0 + velocity_feedback_ * velocity_ * upper_position;
302 std::min(std::pow(10.0, -3.0 / (trips * tilt * velocity_ring)),
305 mode_gain_[index] = std::pow(
306 10.0, (high_mode_emphasis_db_ +
307 velocity_high_mode_db_ * velocity_) *
308 upper_position / 20.0);
312 const double corner =
313 std::min(0.45 *
sample_rate(), 16000.0 * std::pow(0.72,
static_cast<double>(
c)));
314 dampers_[index].set_cutoff(
static_cast<float>(corner));
315 if (
c >= kUpperCombFirst) {
316 const double highpass_corner =
318 std::max(upper_highpass_hz_, 1.0) *
319 std::pow(1.35,
static_cast<double>(
c - kUpperCombFirst)));
320 upper_highpasses_[index].set_cutoff(
321 static_cast<float>(highpass_corner));
326 static constexpr int kUpperCombFirst =
comb_count / 2;
328 double tune_hz_ = 320.0;
329 double decay_ms_ = 1800.0;
330 double decay_tilt_ = 0.93;
331 double high_mode_emphasis_db_ = 0.0;
332 double velocity_feedback_ = 0.35;
333 double velocity_high_mode_db_ = 4.0;
334 double upper_highpass_hz_ = 500.0;
335 double spread_ = 1.0;
336 double inharmonicity_ = 0.4;
337 double shift_hz_ = 45.0;
338 double noise_level_ = 0.7;
339 double strike_level_ = 0.3;
340 double strike_ms_ = 6.0;
341 double tone_hz_ = 12000.0;
342 double low_cut_hz_ = 300.0;
343 double variation_ = 0.5;
349 std::array<DelayLine, comb_count> combs_;
350 std::array<TptFilter, comb_count> dampers_;
351 std::array<TptFilter, comb_count> upper_highpasses_;
358 std::array<double, comb_count> delay_samples_{};
359 std::array<double, comb_count> feedback_{};
360 std::array<double, comb_count> mode_gain_{};
362 double velocity_gain_ = 1.0;
363 double velocity_ = 1.0;
365 double strike_phase_ = 0.0;
void reset()
Definition frequency_shifter.hpp:69
static constexpr std::uint32_t default_seed
Definition noise_source.hpp:55
void reset()
Definition tpt_filter.hpp:127
void on_prepare(double sample_rate) override
Sizes buffers and fixes coefficients.
Definition cymbal.hpp:152
void set_upper_highpass_hz(double hz)
Base cutoff of the extra high-pass stages in the upper half of the comb bank.
Definition cymbal.hpp:94
void on_reset() override
Clears every piece of state the voice carries, so is_active() becomes false and the next render start...
Definition cymbal.hpp:168
void set_spread(double amount)
How far the combs are spread from unison (0) to their full plate ratios (1).
Definition cymbal.hpp:100
int latency_samples() const noexcept override
Fixed host-rate delay introduced by this voice's selected output quality.
Definition cymbal.hpp:141
void set_decay_tilt(double tilt)
How much faster each successive comb dies than the one below it.
Definition cymbal.hpp:72
void set_noise_color(NoiseColor color)
Definition cymbal.hpp:136
void set_low_cut_hz(double hz)
Definition cymbal.hpp:119
void set_noise_level(double level)
Level of the noise part of the strike, and of the swept tone under it.
Definition cymbal.hpp:112
OutputOversampling output_oversampling() const noexcept override
Output quality at the generic voice boundary.
Definition cymbal.hpp:144
HitLifeMode hit_life() const
Definition cymbal.hpp:134
void set_output_oversampling(OutputOversampling factor) override
Definition cymbal.hpp:147
OutputStage * output_stage() noexcept override
The terminal saturation and degradation stage, at the generic voice boundary.
Definition cymbal.hpp:140
void set_strike_level(double level)
Definition cymbal.hpp:113
void set_high_mode_emphasis_db(double db)
Extra high-mode level at the top comb, interpolated from zero dB at the bottom comb.
Definition cymbal.hpp:77
void set_decay_ms(double ms)
How long the bank rings, as a T60 in milliseconds.
Definition cymbal.hpp:68
CymbalVoice()
Definition cymbal.hpp:57
OutputStage & output()
Definition cymbal.hpp:138
void set_tune_hz(double hz)
Frequency the lowest comb rings at, in Hz.
Definition cymbal.hpp:65
void set_tone_hz(double hz)
Output tone controls: a lowpass on top and a highpass to clear the mud the shifter leaves at the bott...
Definition cymbal.hpp:118
void set_hit_life(HitLifeMode mode)
Definition cymbal.hpp:133
void render_add(float *out, int num_samples) override
Adds the voice's output into out. Must add, never assign.
Definition cymbal.hpp:213
const OutputStage & output() const
Definition cymbal.hpp:139
static constexpr int comb_count
Number of parallel combs.
Definition cymbal.hpp:49
void set_shift_hz(double hz)
How many hertz every partial is shifted by.
Definition cymbal.hpp:109
void set_inharmonicity(double amount)
How far the ratios are pushed further apart still, progressively.
Definition cymbal.hpp:103
bool on_is_active() const override
Definition cymbal.hpp:208
static constexpr double comb_ratios[comb_count]
Plate-like ratios for the comb delays.
Definition cymbal.hpp:54
void on_note_on(float velocity) override
Starts a hit at the given velocity, already clamped to [0, 1].
Definition cymbal.hpp:178
void set_variation(double amount)
How much successive excitations differ, 0 (bit-identical) to 1.
Definition cymbal.hpp:126
void set_strike_ms(double ms)
Definition cymbal.hpp:114
void set_velocity_high_mode_db(double db)
Definition cymbal.hpp:88
void set_velocity_feedback(double amount)
Velocity-dependent feedback and high-mode emphasis.
Definition cymbal.hpp:85
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
The minimal in-graph frequency shifter: shift in Hz, one knob, no feedback.
HitLifeMode
Selects the intentional source of variation between otherwise identical hits.
Definition hit_life.hpp:26
@ advancing_seed_preserved_body
Definition hit_life.hpp:31
@ fixed_seed_preserved_body
Definition hit_life.hpp:30
OutputOversampling
Realtime quality choices for the shared drum output nonlinearity.
Definition output_stage.hpp:20
OutputStageT< float > OutputStage
Definition output_stage.hpp:384
@ c
Definition spectrum_trace.hpp:35
TptFilterT< float > TptFilter
Definition tpt_filter.hpp:172
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ highpass
Definition filter_morph.hpp:19
FrequencyShifterT< float > FrequencyShifter
Definition frequency_shifter.hpp:96
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
How hard a hit changes the sound, beyond changing how loud it is.
Definition velocity.hpp:33
First-order topology-preserving transform (TPT) filter.