48#include "AudioBuffer.h"
57#ifndef DSPARK_RESTRICT
58 #if defined(__clang__) || defined(__GNUC__)
59 #define DSPARK_RESTRICT __restrict__
60 #elif defined(_MSC_VER)
61 #define DSPARK_RESTRICT __restrict
63 #define DSPARK_RESTRICT
76template <
typename T,
int MaxChannels = 16>
95 : dryBuffer_(std::move(other.dryBuffer_)),
96 capturedSamples_(other.capturedSamples_),
97 delayHist_(std::move(other.delayHist_)),
98 latencySamples_(other.latencySamples_),
99 histPos_(other.histPos_),
100 mixRule_(other.mixRule_.load(std::memory_order_relaxed)),
101 currentMix_(other.currentMix_),
102 maxMixStep_(other.maxMixStep_)
107 if (
this == &other)
return *
this;
108 dryBuffer_ = std::move(other.dryBuffer_);
109 capturedSamples_ = other.capturedSamples_;
110 delayHist_ = std::move(other.delayHist_);
111 latencySamples_ = other.latencySamples_;
112 histPos_ = other.histPos_;
113 mixRule_.store(other.mixRule_.load(std::memory_order_relaxed),
114 std::memory_order_relaxed);
115 currentMix_ = other.currentMix_;
116 maxMixStep_ = other.maxMixStep_;
135 ?
static_cast<T
>(1.0 / std::max(1.0, spec.
sampleRate * kMinRampSeconds))
141 if (latencySamples_ > 0)
156 capturedSamples_ = 0;
170 mixRule_.store(rule, std::memory_order_relaxed);
187 samples = std::max(0, samples);
188 if (samples == latencySamples_)
return;
189 latencySamples_ = samples;
211 const int chCount = std::min(input.getNumChannels(), dryBuffer_.
getNumChannels());
212 const int nSamples = std::min(input.getNumSamples(), dryBuffer_.
getNumSamples());
214 if (latencySamples_ <= 0)
216 for (
int ch = 0; ch < chCount; ++ch)
218 const T* DSPARK_RESTRICT src = input.getChannel(ch);
219 T* DSPARK_RESTRICT dst = dryBuffer_.
getChannel(ch);
220 std::copy_n(src, nSamples, dst);
228 const int D = latencySamples_;
229 for (
int ch = 0; ch < chCount; ++ch)
231 const T* DSPARK_RESTRICT src = input.getChannel(ch);
232 T* DSPARK_RESTRICT dst = dryBuffer_.
getChannel(ch);
233 T* DSPARK_RESTRICT hist = delayHist_.
getChannel(ch);
235 for (
int i = 0; i < nSamples; ++i)
239 if (++pos == D) pos = 0;
242 histPos_ = (histPos_ + nSamples) % D;
245 capturedSamples_ = nSamples;
269 if (std::isnan(targetMix)) targetMix = T(0);
270 targetMix = std::clamp(targetMix, T(0), T(1));
272 const int chCount = std::min(wetBuffer.getNumChannels(), dryBuffer_.
getNumChannels());
273 const int nSamples = std::min(wetBuffer.getNumSamples(), capturedSamples_);
275 if (nSamples == 0 || chCount == 0)
return;
278 if (currentMix_ < T(0)) currentMix_ = targetMix;
280 const bool needsSmoothing = std::abs(currentMix_ - targetMix) > T(1e-5);
282 bool rateLimited =
false;
285 mixStep = (targetMix - currentMix_) / T(nSamples);
286 if (std::abs(mixStep) > maxMixStep_)
288 mixStep = mixStep > T(0) ? maxMixStep_ : -maxMixStep_;
292 const MixRule rule = mixRule_.load(std::memory_order_relaxed);
301 const T mix0 = needsSmoothing ? currentMix_ : targetMix;
303 for (
int ch = 0; ch < chCount; ++ch)
305 T* DSPARK_RESTRICT wetData = wetBuffer.getChannel(ch);
306 const T* DSPARK_RESTRICT dryData = dryBuffer_.
getChannel(ch);
314 const T w = std::sqrt(mix0);
315 const T d = std::sqrt(T(1) - mix0);
316 for (
int i = 0; i < nSamples; ++i)
317 wetData[i] = dryData[i] * d + wetData[i] * w;
321 for (
int i = 0; i < nSamples; ++i)
327 const T m = mix0 + mixStep * T(i);
328 const T w = std::sqrt(std::max(T(0), m));
329 const T d = std::sqrt(std::max(T(0), T(1) - m));
330 wetData[i] = dryData[i] * d + wetData[i] * w;
339 const T d = T(1) - w;
340 for (
int i = 0; i < nSamples; ++i)
341 wetData[i] = dryData[i] * d + wetData[i] * w;
345 for (
int i = 0; i < nSamples; ++i)
347 const T w = mix0 + mixStep * T(i);
348 const T d = T(1) - w;
349 wetData[i] = dryData[i] * d + wetData[i] * w;
357 currentMix_ = rateLimited
358 ? std::clamp(mix0 + mixStep * T(nSamples), T(0), T(1))
381 int capturedSamples_ = 0;
385 int latencySamples_ = 0;
389 T currentMix_ = T(-1);
390 T maxMixStep_ = T(1);
392 static constexpr double kMinRampSeconds = 0.02;
Non-owning view over audio channel data.
Owning audio buffer with contiguous, 32-byte aligned storage.
int getNumChannels() const noexcept
Returns the number of active channels.
T * getChannel(int ch) noexcept
Returns a pointer to the sample data.
int getNumSamples() const noexcept
Returns the number of samples per channel.
void resize(int numChannels, int numSamples)
Allocates the buffer for the given dimensions.
void clear() noexcept
Clears all channels, including SIMD alignment padding. Ensures any SIMD over-read will consume pure z...
Pre-allocated, SIMD-friendly dry/wet blender for real-time audio.
void setLatencyCompensation(int samples)
Delays the captured dry signal to compensate for an effect's internal latency (e.g....
int getLatencyCompensation() const noexcept
Returns the configured dry-path latency compensation in samples.
MixRule
Defines the mathematical curve used for mixing.
int getDryCapturedSamples() const noexcept
Returns the number of samples valid from the last pushDry() call.
void mixWet(AudioBufferView< T > wetBuffer, T targetMix) noexcept
Blends the stored dry signal with the current (wet) buffer in-place.
void reset() noexcept
Resets the internal buffer and smoothing states to zero.
int getDryNumChannels() const noexcept
Returns the internal capacity of channels in the dry buffer.
DryWetMixer() noexcept=default
DryWetMixer & operator=(DryWetMixer &&other) noexcept
void pushDry(const AudioBufferView< const T > &input) noexcept
Captures a snapshot of the dry (unprocessed) signal.
void setMixRule(MixRule rule) noexcept
Sets the mathematical curve to use during the mix phase.
void prepare(const AudioSpec &spec)
Allocates the internal dry buffer for the given audio spec.
DryWetMixer & operator=(const DryWetMixer &)=delete
const T * getDryChannel(int ch) const noexcept
Retrieves a read-only pointer to the captured dry channel data.
DryWetMixer(const DryWetMixer &)=delete
Main namespace for the DSPark framework.
Describes the audio environment for a DSP processor.
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).
int maxBlockSize
Maximum number of samples per processing block.
double sampleRate
Sample rate in Hz.