48template <
typename T,
int MaxViewChannels = 16>
65 static_assert(std::is_convertible_v<U*, T*>,
"Pointer type U* must be convertible to T*");
66 assert(numChannels >= 0 && numChannels <= MaxViewChannels);
71 numChannels_ = std::clamp(numChannels, 0, MaxViewChannels);
72 numSamples_ = numSamples >= 0 ? numSamples : 0;
74 for (
int ch = 0; ch < numChannels_; ++ch)
75 channels_[ch] = channelPtrs[ch];
89 : numChannels_(other.getNumChannels()), numSamples_(other.getNumSamples())
91 static_assert(std::is_convertible_v<U*, T*>,
"Cannot convert view (e.g., const to non-const)");
93 for (
int ch = 0; ch < numChannels_; ++ch)
94 channels_[ch] = other.getChannel(ch);
102 assert(ch >= 0 && ch < numChannels_);
103 return channels_[ch];
122 assert(startSample >= 0);
124 assert(startSample + length <= numSamples_);
127 sub.numChannels_ = numChannels_;
128 sub.numSamples_ = length;
129 for (
int ch = 0; ch < numChannels_; ++ch)
130 sub.channels_[ch] = channels_[ch] + startSample;
142 static_assert(!std::is_const_v<T>,
"Cannot clear a const view");
143 const auto bytes =
static_cast<std::size_t
>(numSamples_) *
sizeof(T);
144 for (
int ch = 0; ch < numChannels_; ++ch)
145 std::memset(channels_[ch], 0, bytes);
159 template <
typename U,
int M>
162 static_assert(!std::is_const_v<T>,
"Cannot copy into a const view");
164 const int chCount = std::min(numChannels_, src.getNumChannels());
165 const int nSamples = std::min(numSamples_, src.getNumSamples());
167 for (
int ch = 0; ch < chCount; ++ch)
169 T* dst = channels_[ch];
170 const U* s = src.getChannel(ch);
172 if constexpr (std::is_same_v<std::remove_const_t<U>, T>)
174 std::memmove(dst, s,
static_cast<std::size_t
>(nSamples) *
sizeof(T));
178 for (
int i = 0; i < nSamples; ++i)
179 dst[i] =
static_cast<T
>(s[i]);
196 template <
typename U,
int M>
199 static_assert(!std::is_const_v<T>,
"Cannot add into a const view");
202 const int nSamples = std::min(numSamples_, src.
getNumSamples());
204 for (
int ch = 0; ch < chCount; ++ch)
206 T* dst = channels_[ch];
209 if constexpr (std::is_same_v<std::remove_const_t<U>, T>)
215 for (
int i = 0; i < nSamples; ++i)
216 dst[i] +=
static_cast<T
>(s[i]) * gain;
227 static_assert(!std::is_const_v<T>,
"Cannot apply gain to a const view");
228 for (
int ch = 0; ch < numChannels_; ++ch)
241 using Value = std::remove_const_t<T>;
242 Value peak = Value(0);
243 for (
int ch = 0; ch < numChannels_; ++ch)
246 if (chPeak > peak) peak = chPeak;
252 std::array<T*, MaxViewChannels> channels_ {};
253 int numChannels_ = 0;
259static_assert(std::is_trivially_copyable_v<AudioBufferView<float>>,
260 "AudioBufferView must remain trivially copyable");
261static_assert(std::is_trivially_copyable_v<AudioBufferView<const float>>,
262 "AudioBufferView must remain trivially copyable");
275template <
typename T,
int MaxChannels = 16>
278 static constexpr std::size_t kAlignment = 32;
279 static_assert(std::has_single_bit(kAlignment),
"Alignment must be a power of two");
280 static_assert(std::is_same_v<T, std::remove_cv_t<T>> && std::is_trivially_copyable_v<T>,
281 "AudioBuffer requires a non-const, trivially copyable sample type");
293 : rawData_(std::exchange(other.rawData_,
nullptr))
294 , channelPtrs_(std::exchange(other.channelPtrs_, {}))
295 , numChannels_(std::exchange(other.numChannels_, 0))
296 , numSamples_(std::exchange(other.numSamples_, 0))
297 , allocatedBytes_(std::exchange(other.allocatedBytes_, 0))
298 , strideBytes_(std::exchange(other.strideBytes_, 0))
307 rawData_ = std::exchange(other.rawData_,
nullptr);
308 channelPtrs_ = std::exchange(other.channelPtrs_, {});
309 numChannels_ = std::exchange(other.numChannels_, 0);
310 numSamples_ = std::exchange(other.numSamples_, 0);
311 allocatedBytes_ = std::exchange(other.allocatedBytes_, 0);
312 strideBytes_ = std::exchange(other.strideBytes_, 0);
330 void resize(
int numChannels,
int numSamples)
332 assert(numChannels >= 0 && numChannels <= MaxChannels);
333 assert(numSamples >= 0);
337 numChannels = std::clamp(numChannels, 0, MaxChannels);
338 if (numSamples < 0) numSamples = 0;
340 const auto samplesBytes =
static_cast<std::size_t
>(numSamples) *
sizeof(T);
341 const auto stride = alignUp(samplesBytes, kAlignment);
342 const auto totalBytes = stride *
static_cast<std::size_t
>(numChannels);
345 if (totalBytes > allocatedBytes_)
348 rawData_ =
static_cast<T*
>(::operator
new(totalBytes, std::align_val_t(kAlignment)));
349 allocatedBytes_ = totalBytes;
352 numChannels_ = numChannels;
353 numSamples_ = numSamples;
354 strideBytes_ = stride;
356 auto* base =
reinterpret_cast<char*
>(rawData_);
359 for (
int ch = 0; ch < numChannels; ++ch)
360 channelPtrs_[ch] =
reinterpret_cast<T*
>(base + stride *
static_cast<std::size_t
>(ch));
363 for (
int ch = numChannels; ch < MaxChannels; ++ch)
364 channelPtrs_[ch] =
nullptr;
377 return { channelPtrs_.data(), numChannels_, numSamples_ };
383 return { channelPtrs_.data(), numChannels_, numSamples_ };
391 assert(ch >= 0 && ch < numChannels_);
392 return channelPtrs_[ch];
398 assert(ch >= 0 && ch < numChannels_);
399 return channelPtrs_[ch];
418 if (rawData_ ==
nullptr)
return;
419 for (
int ch = 0; ch < numChannels_; ++ch)
420 std::memset(channelPtrs_[ch], 0, strideBytes_);
424 [[nodiscard]]
static constexpr std::size_t alignUp(std::size_t value, std::size_t alignment)
noexcept
426 return (value + alignment - 1) & ~(alignment - 1);
429 void deallocate() noexcept
433 ::operator
delete(rawData_, std::align_val_t(kAlignment));
447 channelPtrs_.fill(
nullptr);
453 T* rawData_ =
nullptr;
454 std::array<T*, MaxChannels> channelPtrs_ {};
455 int numChannels_ = 0;
457 std::size_t allocatedBytes_ = 0;
458 std::size_t strideBytes_ = 0;
SIMD-accelerated buffer operations for real-time audio processing.
Non-owning view over audio channel data.
void clear() const noexcept
Fills the valid sample range of all channels with zeros.
int getNumSamples() const noexcept
Returns the number of samples per channel.
int getNumChannels() const noexcept
Returns the number of channels in this view.
AudioBufferView() noexcept=default
Creates an empty view (0 channels, 0 samples).
void addFrom(const AudioBufferView< U, M > &src, T gain=T(1)) const noexcept
Adds samples from a source view into this view with optional gain.
T * getChannel(int ch) const noexcept
Returns a pointer to the sample data for the given channel.
AudioBufferView(const AudioBufferView< U, MaxViewChannels > &other) noexcept
Converting constructor allowing mutable to const view conversions.
void copyFrom(const AudioBufferView< U, M > &src) const noexcept
Safely copies samples from a source view into this view.
AudioBufferView getSubView(int startSample, int length) const noexcept
Returns a zero-copy sub-view starting at an offset.
std::remove_const_t< T > getPeakLevel() const noexcept
Returns the peak absolute sample value across all channels.
void applyGain(T gain) const noexcept
Multiplies all samples in all valid channels by a gain factor.
Owning audio buffer with contiguous, 32-byte aligned storage.
AudioBuffer & operator=(const AudioBuffer &)=delete
AudioBufferView< T, MaxChannels > toView() noexcept
Returns a non-owning mutable view of this buffer. The view's channel capacity is propagated from MaxC...
int getNumChannels() const noexcept
Returns the number of active channels.
AudioBuffer(const AudioBuffer &)=delete
T * getChannel(int ch) noexcept
Returns a pointer to the sample data.
int getNumSamples() const noexcept
Returns the number of samples per channel.
AudioBufferView< const T, MaxChannels > toView() const noexcept
Returns a non-owning const view of this buffer.
AudioBuffer(AudioBuffer &&other) noexcept
C++20 idiomatic move constructor.
void resize(int numChannels, int numSamples)
Allocates the buffer for the given dimensions.
const T * getChannel(int ch) const noexcept
Const overload.
void clear() noexcept
Clears all channels, including SIMD alignment padding. Ensures any SIMD over-read will consume pure z...
AudioBuffer & operator=(AudioBuffer &&other) noexcept
C++20 idiomatic move assignment.
void addWithGain(float *DSPARK_RESTRICT dst, const float *DSPARK_RESTRICT src, float gain, int count) noexcept
Adds source samples scaled by a gain factor into a destination buffer.
float peakLevel(const float *DSPARK_RESTRICT data, int count) noexcept
Returns the peak absolute sample value in a buffer.
void applyGain(float *DSPARK_RESTRICT data, float gain, int count) noexcept
Multiplies all samples in a buffer by a gain factor.
Main namespace for the DSPark framework.