48template <
typename T,
int MaxViewChannels = 16>
63 requires std::is_convertible_v<U*, 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];
99 template <
typename U,
int OtherChannels>
100 requires (std::is_convertible_v<U*, T*> && OtherChannels <= MaxViewChannels)
104 for (
int ch = 0; ch < numChannels_; ++ch)
113 assert(ch >= 0 && ch < numChannels_);
114 return channels_[ch];
133 assert(startSample >= 0);
135 assert(startSample + length <= numSamples_);
138 sub.numChannels_ = numChannels_;
139 sub.numSamples_ = length;
140 for (
int ch = 0; ch < numChannels_; ++ch)
141 sub.channels_[ch] = channels_[ch] + startSample;
153 static_assert(!std::is_const_v<T>,
"Cannot clear a const view");
154 const auto bytes =
static_cast<std::size_t
>(numSamples_) *
sizeof(T);
158 if (bytes == 0)
return;
159 for (
int ch = 0; ch < numChannels_; ++ch)
160 std::memset(channels_[ch], 0, bytes);
174 template <
typename U,
int M>
177 static_assert(!std::is_const_v<T>,
"Cannot copy into a const view");
179 const int chCount = std::min(numChannels_, src.getNumChannels());
180 const int nSamples = std::min(numSamples_, src.getNumSamples());
182 for (
int ch = 0; ch < chCount; ++ch)
184 T* dst = channels_[ch];
185 const U* s = src.getChannel(ch);
187 if constexpr (std::is_same_v<std::remove_const_t<U>, T>)
189 std::memmove(dst, s,
static_cast<std::size_t
>(nSamples) *
sizeof(T));
193 for (
int i = 0; i < nSamples; ++i)
194 dst[i] =
static_cast<T
>(s[i]);
211 template <
typename U,
int M>
214 static_assert(!std::is_const_v<T>,
"Cannot add into a const view");
217 const int nSamples = std::min(numSamples_, src.
getNumSamples());
219 for (
int ch = 0; ch < chCount; ++ch)
221 T* dst = channels_[ch];
224 if constexpr (std::is_same_v<std::remove_const_t<U>, T>)
230 for (
int i = 0; i < nSamples; ++i)
231 dst[i] +=
static_cast<T
>(s[i]) * gain;
242 static_assert(!std::is_const_v<T>,
"Cannot apply gain to a const view");
243 for (
int ch = 0; ch < numChannels_; ++ch)
256 using Value = std::remove_const_t<T>;
257 Value peak = Value(0);
258 for (
int ch = 0; ch < numChannels_; ++ch)
261 if (chPeak > peak) peak = chPeak;
267 std::array<T*, MaxViewChannels> channels_ {};
268 int numChannels_ = 0;
274static_assert(std::is_trivially_copyable_v<AudioBufferView<float>>,
275 "AudioBufferView must remain trivially copyable");
276static_assert(std::is_trivially_copyable_v<AudioBufferView<const float>>,
277 "AudioBufferView must remain trivially copyable");
290template <
typename T,
int MaxChannels = 16>
293 static constexpr std::size_t kAlignment = 32;
294 static_assert(std::has_single_bit(kAlignment),
"Alignment must be a power of two");
295 static_assert(std::is_same_v<T, std::remove_cv_t<T>> && std::is_trivially_copyable_v<T>,
296 "AudioBuffer requires a non-const, trivially copyable sample type");
308 : rawData_(std::exchange(other.rawData_,
nullptr))
309 , channelPtrs_(std::exchange(other.channelPtrs_, {}))
310 , numChannels_(std::exchange(other.numChannels_, 0))
311 , numSamples_(std::exchange(other.numSamples_, 0))
312 , allocatedBytes_(std::exchange(other.allocatedBytes_, 0))
313 , strideBytes_(std::exchange(other.strideBytes_, 0))
322 rawData_ = std::exchange(other.rawData_,
nullptr);
323 channelPtrs_ = std::exchange(other.channelPtrs_, {});
324 numChannels_ = std::exchange(other.numChannels_, 0);
325 numSamples_ = std::exchange(other.numSamples_, 0);
326 allocatedBytes_ = std::exchange(other.allocatedBytes_, 0);
327 strideBytes_ = std::exchange(other.strideBytes_, 0);
345 void resize(
int numChannels,
int numSamples)
347 assert(numChannels >= 0 && numChannels <= MaxChannels);
348 assert(numSamples >= 0);
352 numChannels = std::clamp(numChannels, 0, MaxChannels);
353 if (numSamples < 0) numSamples = 0;
355 const auto samplesBytes =
static_cast<std::size_t
>(numSamples) *
sizeof(T);
356 const auto stride = alignUp(samplesBytes, kAlignment);
357 const auto totalBytes = stride *
static_cast<std::size_t
>(numChannels);
360 if (totalBytes > allocatedBytes_)
363 rawData_ =
static_cast<T*
>(::operator
new(totalBytes, std::align_val_t(kAlignment)));
364 allocatedBytes_ = totalBytes;
367 numChannels_ = numChannels;
368 numSamples_ = numSamples;
369 strideBytes_ = stride;
371 auto* base =
reinterpret_cast<char*
>(rawData_);
374 for (
int ch = 0; ch < numChannels; ++ch)
375 channelPtrs_[ch] =
reinterpret_cast<T*
>(base + stride *
static_cast<std::size_t
>(ch));
378 for (
int ch = numChannels; ch < MaxChannels; ++ch)
379 channelPtrs_[ch] =
nullptr;
392 return { channelPtrs_.data(), numChannels_, numSamples_ };
398 return { channelPtrs_.data(), numChannels_, numSamples_ };
406 assert(ch >= 0 && ch < numChannels_);
407 return channelPtrs_[ch];
413 assert(ch >= 0 && ch < numChannels_);
414 return channelPtrs_[ch];
433 if (rawData_ ==
nullptr)
return;
434 for (
int ch = 0; ch < numChannels_; ++ch)
435 std::memset(channelPtrs_[ch], 0, strideBytes_);
439 [[nodiscard]]
static constexpr std::size_t alignUp(std::size_t value, std::size_t alignment)
noexcept
441 return (value + alignment - 1) & ~(alignment - 1);
444 void deallocate() noexcept
448 ::operator
delete(rawData_, std::align_val_t(kAlignment));
462 channelPtrs_.fill(
nullptr);
468 T* rawData_ =
nullptr;
469 std::array<T*, MaxChannels> channelPtrs_ {};
470 int numChannels_ = 0;
472 std::size_t allocatedBytes_ = 0;
473 std::size_t strideBytes_ = 0;
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, OtherChannels > &other) noexcept
Converts compatible sample types and widens pointer-array capacity.
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.