80 assert(maxSamples > 0);
84 constexpr int kMaxCapacity = 1 << 30;
85 if (maxSamples > kMaxCapacity) maxSamples = kMaxCapacity;
88 while (capacity_ < maxSamples)
91 mask_ = capacity_ - 1;
96 size_t bytes =
static_cast<size_t>(capacity_) *
sizeof(T);
97 bytes = (bytes + 31u) & ~
static_cast<size_t>(31u);
100 buffer_.reset(
static_cast<T*
>(
101 #
if defined(_MSC_VER)
102 _aligned_malloc(bytes, 32)
104 std::aligned_alloc(32, bytes)
109 if (!buffer_) { capacity_ = 0; mask_ = 0; writePos_ = 0;
return; }
121 std::fill_n(buffer_.get(), capacity_, T(0));
129 inline void push(T sample)
noexcept
131 if (capacity_ == 0) [[unlikely]]
return;
132 buffer_[writePos_] = sample;
133 writePos_ = (writePos_ + 1) & mask_;
146 void pushBlock(
const T* samples,
int numSamples)
noexcept
148 if (capacity_ == 0 || numSamples <= 0) [[unlikely]]
return;
150 if (numSamples >= capacity_)
153 samples += numSamples - capacity_;
154 std::memcpy(buffer_.get(), samples,
static_cast<size_t>(capacity_) *
sizeof(T));
159 const int firstSpan = std::min(numSamples, capacity_ - writePos_);
160 std::memcpy(buffer_.get() + writePos_, samples,
161 static_cast<size_t>(firstSpan) *
sizeof(T));
163 const int remaining = numSamples - firstSpan;
165 std::memcpy(buffer_.get(), samples + firstSpan,
166 static_cast<size_t>(remaining) *
sizeof(T));
168 writePos_ = (writePos_ + numSamples) & mask_;
179 [[nodiscard]]
inline T
read(
int delaySamples)
const noexcept
181 if (capacity_ == 0) [[unlikely]]
return T(0);
182 assert(delaySamples >= 0 && delaySamples < capacity_
183 &&
"RingBuffer::read: delay out of range");
185 int idx = (writePos_ - 1 - delaySamples) & mask_;
209 template <InterpMethod Method = InterpMethod::Cubic>
212 if (capacity_ == 0) [[unlikely]]
return T(0);
214 int intDelay =
static_cast<int>(delaySamples);
215 T frac = delaySamples -
static_cast<T
>(intDelay);
217 assert(delaySamples >= T(0) && intDelay + 2 < capacity_);
221 T s0 =
read(intDelay);
222 T s1 =
read(intDelay + 1);
223 return s0 + frac * (s1 - s0);
227 assert(delaySamples >= T(1) &&
"4-point interpolators require delay >= 1.0");
231 s[0] =
read(intDelay - 1);
232 s[1] =
read(intDelay);
233 s[2] =
read(intDelay + 1);
234 s[3] =
read(intDelay + 2);
249 [[nodiscard]]
int getCapacity() const noexcept {
return capacity_; }
250 [[nodiscard]]
const T*
data() const noexcept {
return buffer_.get(); }
254 struct AlignedDeleter {
255 void operator()(T* ptr)
const {
256 #if defined(_MSC_VER)
264 std::unique_ptr<T[], AlignedDeleter> buffer_;
Core mathematical utilities for digital signal processing.
Fractional-position interpolation for delay lines, tables and buffers.
Power-of-two circular buffer with compile-time interpolated read access.
T read(int delaySamples) const noexcept
Reads a sample at an integer delay.
void prepare(int maxSamples) noexcept
Allocates the ring buffer with the given capacity.
const T * data() const noexcept
void pushBlock(const T *samples, int numSamples) noexcept
Pushes a block of samples into the buffer.
void push(T sample) noexcept
Pushes a single sample into the buffer.
int getCapacity() const noexcept
RingBuffer() noexcept=default
Constructs an empty ring buffer. No allocation happens until prepare() is called, honouring the frame...
void reset() noexcept
Clears the buffer to zero without deallocating. Safe to call from the audio thread.
T readInterpolated(T delaySamples) const noexcept
Reads a sample at a fractional delay using the specified interpolation.
int getWritePosition() const noexcept
Main namespace for the DSPark framework.
T interpolateHermite(T y0, T y1, T y2, T y3, T frac) noexcept
4-point, 3rd-order Hermite interpolation (optimized x-form).
T interpolateLagrange(T y0, T y1, T y2, T y3, T frac) noexcept
4-point Lagrange interpolation from discrete samples.
InterpMethod
Interpolation method for fractional-sample reads.
@ Lagrange
4-point Lagrange (highest accuracy, static delay)
@ Linear
2-point linear (fast, lower quality)
@ Hermite
4-point Hermite (smooth transients, best for modulated delay)
@ Cubic
4-point Catmull-Rom (good balance)