54template <
typename SampleType =
float>
58 void prepare(
double sample_rate,
double lowest_hz = 20.0) {
59 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
60 const int longest =
static_cast<int>(sample_rate_ / std::max(lowest_hz, 1.0)) + 4;
61 loop_.prepare(longest);
62 excitation_.prepare(longest);
63 damping_.prepare(
static_cast<SampleType
>(sample_rate_));
64 dynamic_.prepare(
static_cast<SampleType
>(sample_rate_));
71 frequency_ = std::clamp(hz, 20.0, 0.25 * sample_rate_);
79 const double frequency =
80 std::clamp(hz, 20.0, 0.25 * sample_rate_);
81 modulated_loop_length_ = sample_rate_ / frequency;
87 decay_seconds_ = std::max(seconds, 0.01);
93 damping_amount_ = std::clamp(amount, 0.0, 1.0);
98 void set_stiffness(
double amount) { stiffness_ = std::clamp(amount, 0.0, 1.0); }
102 pluck_position_ = std::clamp(position, 0.0, 0.5);
111 dynamic_hz_ = std::clamp(hz, 100.0, 20000.0);
112 dynamic_.set_cutoff(
static_cast<SampleType
>(
113 std::min(dynamic_hz_, 0.49 * sample_rate_)));
120 pick_direction_ = std::clamp(amount, 0.0, 0.99);
130 stiffness_state_ = 0;
131 burst_remaining_ = 0;
145 if (restart) loop_.reset();
146 burst_remaining_ = std::max(1,
static_cast<int>(loop_length_));
152 settling_ = burst_remaining_ + 2;
157 double level()
const {
return level_; }
162 if (!active_)
return SampleType{0};
166 SampleType shaped = excitation;
167 if (burst_remaining_ > 0) {
168 excitation_.push(excitation);
169 if (pluck_position_ > 0.0) {
171 static_cast<SampleType
>(pluck_position_ * loop_length_);
172 shaped = excitation - excitation_.read(offset);
176 shaped = SampleType{0};
182 if (pick_direction_ > 0.0) {
183 const auto a =
static_cast<SampleType
>(pick_direction_);
184 pick_state_ = (SampleType{1} -
a) * shaped +
a * pick_state_;
185 shaped = pick_state_;
187 shaped = dynamic_.process_lowpass(shaped);
189 SampleType y = modulated_
191 static_cast<SampleType
>(modulated_loop_length_))
193 static_cast<SampleType
>(integer_delay_));
194 y = damping_.process_lowpass(y);
200 const SampleType out =
201 static_cast<SampleType
>(tuning_coefficient_) * y +
204 y -
static_cast<SampleType
>(tuning_coefficient_) * out;
210 if (stiffness_ > 0.0) {
211 const auto a =
static_cast<SampleType
>(-0.9 * stiffness_);
212 const SampleType out =
a * y + stiffness_state_;
213 stiffness_state_ = y -
a * out;
217 y =
static_cast<SampleType
>(loop_gain_) * y;
220 level_ = std::max(std::fabs(
static_cast<double>(y)), level_ * 0.9999);
221 if (settling_ > 0) --settling_;
222 if (settling_ == 0 && burst_remaining_ <= 0 && level_ < 1
e-6) active_ =
false;
229 update_tuning(frequency_);
235 loop_gain_ = std::pow(10.0, -3.0 / (frequency_ * decay_seconds_));
240 const double open = std::min(0.45 * sample_rate_, 18000.0);
245 const double closed = std::min(frequency_ * 6.0, open);
246 const double corner = open * std::pow(closed / open, damping_amount_);
247 damping_.set_cutoff(
static_cast<SampleType
>(corner));
262 const double ratio = frequency_ / std::max(corner, 1
e-9);
263 loop_gain_ = std::min(loop_gain_ * std::sqrt(1.0 +
ratio *
ratio), 0.9999);
266 void update_tuning(
double frequency) {
267 loop_length_ = sample_rate_ / frequency;
273 constexpr double kMinFractional = 0.1;
274 double fractional = loop_length_ - std::floor(loop_length_);
275 integer_delay_ = std::floor(loop_length_);
276 if (fractional < kMinFractional) {
278 integer_delay_ -= 1.0;
280 integer_delay_ = std::max(integer_delay_, 1.0);
282 tuning_coefficient_ = (1.0 - fractional) / (1.0 + fractional);
285 DelayLineT<SampleType> loop_;
286 DelayLineT<SampleType> excitation_;
287 TptFilterT<SampleType> damping_;
288 TptFilterT<SampleType> dynamic_;
290 double sample_rate_ = 44100.0;
291 double frequency_ = 220.0;
292 double decay_seconds_ = 2.0;
293 double damping_amount_ = 0.3;
294 double stiffness_ = 0.0;
295 double pluck_position_ = 0.25;
297 double loop_length_ = 200.0;
298 double modulated_loop_length_ = 200.0;
299 double integer_delay_ = 200.0;
300 double tuning_coefficient_ = 0.0;
301 double loop_gain_ = 0.99;
302 double dynamic_hz_ = 12000.0;
303 double pick_direction_ = 0.0;
304 bool modulated_ =
false;
305 SampleType pick_state_ = 0;
306 SampleType tuning_state_ = 0;
307 SampleType stiffness_state_ = 0;
308 int burst_remaining_ = 0;
310 bool active_ =
false;
A plucked string: a delay line fed back through a loss filter.
Definition karplus_strong.hpp:55
void set_stiffness(double amount)
Inharmonicity, 0 (a true harmonic series) to 1 (bar-like stretch).
Definition karplus_strong.hpp:98
void set_pick_direction(double amount)
Direction of the pick, 0 to 1.
Definition karplus_strong.hpp:119
void set_modulated_frequency(double hz)
Uses the delay line's continuously interpolated reader for audio-rate pitch modulation.
Definition karplus_strong.hpp:78
void set_decay_seconds(double seconds)
Time for the note to fall by 60 dB, in seconds.
Definition karplus_strong.hpp:86
void reset()
Definition karplus_strong.hpp:123
SampleType process(SampleType excitation)
Advances one sample.
Definition karplus_strong.hpp:161
void pluck(bool restart=false)
Excites the string.
Definition karplus_strong.hpp:144
void set_dynamic_bandwidth_hz(double hz)
Bandwidth of the excitation, in Hz — how hard the string is played.
Definition karplus_strong.hpp:110
bool is_active() const
Definition karplus_strong.hpp:156
void set_frequency(double hz)
Definition karplus_strong.hpp:70
void set_pluck_position(double position)
Where along the string it is excited, 0 (at the end) to 0.5 (the middle).
Definition karplus_strong.hpp:101
void prepare(double sample_rate, double lowest_hz=20.0)
Sizes the delay line for the lowest note the string will play.
Definition karplus_strong.hpp:58
double level() const
Definition karplus_strong.hpp:157
void set_damping(double amount)
How much faster high partials die than low ones, 0 (bright) to 1 (dark).
Definition karplus_strong.hpp:92
Definition additive_bank.hpp:218
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
KarplusStrongT< float > KarplusStrong
Definition karplus_strong.hpp:314
@ a
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
KarplusStrongT< double > KarplusStrong64
Definition karplus_strong.hpp:315
First-order topology-preserving transform (TPT) filter.