131 void init(
float* pool, uint32_t pool_size) {
135 memset(pool, 0, pool_size *
sizeof(
float));
180 pool_size_ = pool_size;
188 constexpr float TWO_PI = 6.283185307f;
201 if (damp > 1.0f) damp = 1.0f;
202 if (damp < 0.0f) damp = 0.0f;
206 float coeff = 1.0f - damp;
212 uint32_t samples =
static_cast<uint32_t
>(
213 ms *
static_cast<float>(sample_rate_) / 1000.0f);
217 predelay_samples_ = samples;
227 if (
mix > 1.0f)
mix = 1.0f;
228 if (
mix < 0.0f)
mix = 0.0f;
232 float decay()
const {
return decay_; }
234 float mix()
const {
return mix_; }
235 float width()
const {
return width_; }
244 void process(
float* left,
float* right, uint16_t frames) {
245 const float wet = mix_;
246 const float dry = 1.0f - wet;
248 for (uint16_t i = 0; i < frames; ++i) {
249 float in_l = left[i];
250 float in_r = right[i];
252 float input = (in_l + in_r) * 0.5f;
256 if (predelay_samples_ == 0) {
259 predelayed = predelay_read();
261 predelay_write(input);
264 float limited = bandwidth_.
process(predelayed);
267 float diffused = limited;
268 for (
int ap = 0; ap < 4; ++ap) {
269 diffused = input_ap_[ap].
process(diffused);
273 mod_sin_ += mod_inc_ * mod_cos_;
274 mod_cos_ -= mod_inc_ * mod_sin_;
283 float tank_l_in = diffused + decay_ * tank_r_out_;
284 float tank_r_in = diffused + decay_ * tank_l_out_;
288 float tl = tank_l_ap1_.
process(tank_l_in);
291 tank_l_dl1_.
write(tl);
295 tank_l_dl2_.
write(tl);
296 tank_l_out_ = tl_dl2;
299 float tr = tank_r_ap1_.
process(tank_r_in);
302 tank_r_dl1_.
write(tr);
306 tank_r_dl2_.
write(tr);
307 tank_r_out_ = tr_dl2;
315 + tap_from(tank_r_dl1_buf_,
320 + tap_from(tank_r_dl2_buf_,
323 - tap_from(tank_l_dl1_buf_,
328 - tap_from(tank_l_dl2_buf_,
334 + tap_from(tank_l_dl1_buf_,
339 + tap_from(tank_l_dl2_buf_,
342 - tap_from(tank_r_dl1_buf_,
347 - tap_from(tank_r_dl2_buf_,
354 float mono = (out_l + out_r) * 0.5f;
355 out_l = mono + width_ * (out_l - mono);
356 out_r = mono + width_ * (out_r - mono);
358 left[i] = dry * in_l + wet * out_l;
359 right[i] = dry * in_r + wet * out_r;
365 for (uint32_t i = 0; i < pool_size_; ++i) pool_[i] = 0.0f;
368 for (
int i = 0; i < 4; ++i) input_ap_[i].clear();
383 tank_l_damp_.
reset();
384 tank_r_damp_.
reset();
393 void predelay_write(
float sample) {
394 predelay_buf_[predelay_wp_] = sample;
399 float predelay_read()
const {
400 if (predelay_samples_ == 0) {
401 return predelay_buf_[predelay_wp_];
403 uint32_t pos = (predelay_wp_ >= predelay_samples_)
404 ? predelay_wp_ - predelay_samples_
405 : predelay_wp_ + plate::PREDELAY_MAX - predelay_samples_;
406 return predelay_buf_[pos];
409 static float tap_from(
const float* buf, uint32_t buf_size,
410 uint32_t write_pos, uint32_t tap_offset) {
411 if (tap_offset >= buf_size) tap_offset = buf_size - 1;
412 uint32_t pos = (write_pos >= tap_offset)
413 ? write_pos - tap_offset
414 : write_pos + buf_size - tap_offset;
418 float* pool_ =
nullptr;
419 uint32_t pool_size_ = 0;
421 float* predelay_buf_ =
nullptr;
423 float* tank_l_ap1_buf_ =
nullptr;
424 float* tank_l_dl1_buf_ =
nullptr;
425 float* tank_l_ap2_buf_ =
nullptr;
426 float* tank_l_dl2_buf_ =
nullptr;
428 float* tank_r_ap1_buf_ =
nullptr;
429 float* tank_r_dl1_buf_ =
nullptr;
430 float* tank_r_ap2_buf_ =
nullptr;
431 float* tank_r_dl2_buf_ =
nullptr;
444 uint32_t predelay_wp_ = 0;
446 float tank_l_out_ = 0.0f;
447 float tank_r_out_ = 0.0f;
449 dsp::OnePole tank_l_damp_;
450 dsp::OnePole tank_r_damp_;
451 dsp::OnePole bandwidth_;
454 float mod_sin_ = 0.0f;
455 float mod_cos_ = 1.0f;
456 float mod_inc_ = 0.0f;
459 float damping_ = 0.3f;
462 uint32_t predelay_samples_ = 0;
463 uint32_t sample_rate_ = 48000;
uint32_t write_pos() const
Current write position (for external tap reads)
void clear()
Zero the buffer and reset write position.
void init(float *buffer, uint32_t delay)
Initialize after default construction.
void set_feedback(float g)
Set feedback coefficient.
float process(float x)
Process a single sample.
void init(float *buffer, uint32_t max_delay)
Initialize after default construction.
uint32_t write_pos() const
Current write position (for external tap reads)
void clear()
Zero all samples in the buffer and reset write position.
float read_cubic(float delay_samples) const
Read at fractional delay with 4-point Hermite cubic interpolation.
void write(float sample)
Write a sample to the delay line.
void reset()
Reset filter state to zero.
float process(float x)
Process a single sample.
void set_coefficient(float a)
Set filter coefficient directly.
void set_frequency(float freq_hz, uint32_t sr=48000)
Compute coefficient from cutoff frequency (cold path)