21#ifndef SBL_DSP_PRIMITIVES_DELAY_LINE_HPP_
22#define SBL_DSP_PRIMITIVES_DELAY_LINE_HPP_
38 : buffer_(buffer), max_delay_(
max_delay), write_pos_(0) {}
61 buffer_[write_pos_] = sample;
63 if (write_pos_ >= max_delay_) {
75 float read(
float delay_samples)
const {
77 if (delay_samples < 1.0f) delay_samples = 1.0f;
78 float max_f =
static_cast<float>(max_delay_ - 1);
79 if (delay_samples > max_f) delay_samples = max_f;
82 uint32_t int_delay =
static_cast<uint32_t
>(delay_samples);
83 float frac = delay_samples -
static_cast<float>(int_delay);
86 float y0 = read_at(int_delay);
87 float y1 = read_at(int_delay + 1);
90 return y0 + frac * (y1 - y0);
106 if (delay_samples < 2.0f) delay_samples = 2.0f;
107 float max_f =
static_cast<float>(max_delay_ - 2);
108 if (delay_samples > max_f) delay_samples = max_f;
110 uint32_t int_delay =
static_cast<uint32_t
>(delay_samples);
111 float frac = delay_samples -
static_cast<float>(int_delay);
114 float y0 = read_at(int_delay - 1);
115 float y1 = read_at(int_delay);
116 float y2 = read_at(int_delay + 1);
117 float y3 = read_at(int_delay + 2);
120 float a = -0.5f * y0 + 1.5f * y1 - 1.5f * y2 + 0.5f * y3;
121 float b = y0 - 2.5f * y1 + 2.0f * y2 - 0.5f * y3;
122 float c = -0.5f * y0 + 0.5f * y2;
125 return ((a * frac + b) * frac + c) * frac + d;
144 const float* delay, uint16_t frames) {
145 for (uint16_t i = 0; i < frames; ++i) {
147 out[i] =
read(delay[i]);
154 for (uint32_t i = 0; i < max_delay_; ++i) {
172 float read_at(uint32_t delay)
const {
175 uint32_t pos = (write_pos_ + max_delay_ - delay) % max_delay_;
179 float* buffer_ =
nullptr;
180 uint32_t max_delay_ = 0;
181 uint32_t write_pos_ = 0;
float read(float delay_samples) const
Read at fractional delay with linear interpolation.
DelayLine(float *buffer, uint32_t max_delay)
Construct with caller-provided buffer.
uint32_t max_delay() const
Maximum delay in samples (buffer size)
void write(float sample)
Write a sample to the delay line.
float read_cubic(float delay_samples) const
Read at fractional delay with 4-point Hermite cubic interpolation.
DelayLine()=default
Default constructor for deferred initialization (must call init() before use)
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 reset()
Zero all samples in the buffer and reset write position.
void process(const float *in, float *out, const float *delay, uint16_t frames)
Block processing with per-sample modulated delay.
Stateful, single-concern building blocks.