28#include "Interpolation.h"
84 assert(maxSamples > 0);
88 constexpr int kMaxCapacity = 1 << 30;
89 if (maxSamples > kMaxCapacity) maxSamples = kMaxCapacity;
92 while (capacity_ < maxSamples)
95 mask_ = capacity_ - 1;
100 size_t bytes =
static_cast<size_t>(capacity_) *
sizeof(T);
101 bytes = (bytes + 31u) & ~
static_cast<size_t>(31u);
104 buffer_.reset(
static_cast<T*
>(
108 _aligned_malloc(bytes, 32)
110 std::aligned_alloc(32, bytes)
115 if (!buffer_) { capacity_ = 0; mask_ = 0; writePos_ = 0;
return; }
127 std::fill_n(buffer_.get(), capacity_, T(0));
135 inline void push(T sample)
noexcept
137 if (capacity_ == 0) [[unlikely]]
return;
138 buffer_[writePos_] = sample;
139 writePos_ = (writePos_ + 1) & mask_;
152 void pushBlock(
const T* samples,
int numSamples)
noexcept
154 if (capacity_ == 0 || numSamples <= 0) [[unlikely]]
return;
156 if (numSamples >= capacity_)
159 samples += numSamples - capacity_;
160 std::memcpy(buffer_.get(), samples,
static_cast<size_t>(capacity_) *
sizeof(T));
165 const int firstSpan = std::min(numSamples, capacity_ - writePos_);
166 std::memcpy(buffer_.get() + writePos_, samples,
167 static_cast<size_t>(firstSpan) *
sizeof(T));
169 const int remaining = numSamples - firstSpan;
171 std::memcpy(buffer_.get(), samples + firstSpan,
172 static_cast<size_t>(remaining) *
sizeof(T));
174 writePos_ = (writePos_ + numSamples) & mask_;
185 [[nodiscard]]
inline T
read(
int delaySamples)
const noexcept
187 if (capacity_ == 0) [[unlikely]]
return T(0);
188 assert(delaySamples >= 0 && delaySamples < capacity_
189 &&
"RingBuffer::read: delay out of range");
191 int idx = (writePos_ - 1 - delaySamples) & mask_;
215 template <InterpMethod Method = InterpMethod::Cubic>
218 if (capacity_ == 0) [[unlikely]]
return T(0);
220 int intDelay =
static_cast<int>(delaySamples);
221 T frac = delaySamples -
static_cast<T
>(intDelay);
223 assert(delaySamples >= T(0) && intDelay + 2 < capacity_);
227 T s0 =
read(intDelay);
228 T s1 =
read(intDelay + 1);
229 return s0 + frac * (s1 - s0);
233 assert(delaySamples >= T(1) &&
"4-point interpolators require delay >= 1.0");
237 s[0] =
read(intDelay - 1);
238 s[1] =
read(intDelay);
239 s[2] =
read(intDelay + 1);
240 s[3] =
read(intDelay + 2);
255 [[nodiscard]]
int getCapacity() const noexcept {
return capacity_; }
256 [[nodiscard]]
const T*
data() const noexcept {
return buffer_.get(); }
260 struct AlignedDeleter {
261 void operator()(T* ptr)
const {
270 std::unique_ptr<T[], AlignedDeleter> buffer_;
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)