51#ifndef DSPARK_RESTRICT
52 #if defined(__clang__) || defined(__GNUC__)
53 #define DSPARK_RESTRICT __restrict__
54 #elif defined(_MSC_VER)
55 #define DSPARK_RESTRICT __restrict
57 #define DSPARK_RESTRICT
70template <
typename T,
int MaxChannels = 16>
89 : dryBuffer_(std::move(other.dryBuffer_)),
90 capturedSamples_(other.capturedSamples_),
91 delayHist_(std::move(other.delayHist_)),
92 latencySamples_(other.latencySamples_),
93 histPos_(other.histPos_),
94 mixRule_(other.mixRule_.load(std::memory_order_relaxed)),
95 currentMix_(other.currentMix_)
100 if (
this == &other)
return *
this;
101 dryBuffer_ = std::move(other.dryBuffer_);
102 capturedSamples_ = other.capturedSamples_;
103 delayHist_ = std::move(other.delayHist_);
104 latencySamples_ = other.latencySamples_;
105 histPos_ = other.histPos_;
106 mixRule_.store(other.mixRule_.load(std::memory_order_relaxed),
107 std::memory_order_relaxed);
108 currentMix_ = other.currentMix_;
126 if (latencySamples_ > 0)
128 delayHist_.resize(spec.
numChannels, latencySamples_);
139 if (delayHist_.getNumSamples() > 0) delayHist_.clear();
141 capturedSamples_ = 0;
155 mixRule_.store(rule, std::memory_order_relaxed);
172 samples = std::max(0, samples);
173 if (samples == latencySamples_)
return;
174 latencySamples_ = samples;
175 delayHist_.resize(dryBuffer_.getNumChannels(), samples);
194 if (dryBuffer_.getNumSamples() == 0)
return;
196 const int chCount = std::min(input.getNumChannels(), dryBuffer_.getNumChannels());
197 const int nSamples = std::min(input.getNumSamples(), dryBuffer_.getNumSamples());
199 if (latencySamples_ <= 0)
201 for (
int ch = 0; ch < chCount; ++ch)
205 std::copy_n(src, nSamples, dst);
213 const int D = latencySamples_;
214 for (
int ch = 0; ch < chCount; ++ch)
220 for (
int i = 0; i < nSamples; ++i)
224 if (++pos == D) pos = 0;
227 histPos_ = (histPos_ + nSamples) % D;
230 capturedSamples_ = nSamples;
250 if (std::isnan(targetMix)) targetMix = T(0);
251 targetMix = std::clamp(targetMix, T(0), T(1));
253 const int chCount = std::min(wetBuffer.getNumChannels(), dryBuffer_.getNumChannels());
254 const int nSamples = std::min(wetBuffer.getNumSamples(), capturedSamples_);
256 if (nSamples == 0 || chCount == 0)
return;
259 if (currentMix_ < T(0)) currentMix_ = targetMix;
261 const bool needsSmoothing = std::abs(currentMix_ - targetMix) > T(1e-5);
262 const T mixStep = needsSmoothing ? (targetMix - currentMix_) / T(nSamples) : T(0);
263 const MixRule rule = mixRule_.load(std::memory_order_relaxed);
271 const T mix0 = currentMix_;
273 for (
int ch = 0; ch < chCount; ++ch)
284 const T w = std::sqrt(mix0);
285 const T d = std::sqrt(T(1) - mix0);
286 for (
int i = 0; i < nSamples; ++i)
287 wetData[i] = dryData[i] * d + wetData[i] * w;
291 for (
int i = 0; i < nSamples; ++i)
297 const T m = mix0 + mixStep * T(i);
298 const T w = std::sqrt(std::max(T(0), m));
299 const T d = std::sqrt(std::max(T(0), T(1) - m));
300 wetData[i] = dryData[i] * d + wetData[i] * w;
309 const T d = T(1) - w;
310 for (
int i = 0; i < nSamples; ++i)
311 wetData[i] = dryData[i] * d + wetData[i] * w;
315 for (
int i = 0; i < nSamples; ++i)
317 const T w = mix0 + mixStep * T(i);
318 const T d = T(1) - w;
319 wetData[i] = dryData[i] * d + wetData[i] * w;
326 if (needsSmoothing) currentMix_ = targetMix;
336 if (ch < 0 || ch >= dryBuffer_.getNumChannels())
return nullptr;
337 return dryBuffer_.getChannel(ch);
348 int capturedSamples_ = 0;
352 int latencySamples_ = 0;
356 T currentMix_ = T(-1);
Owning audio buffer and non-owning view for real-time DSP processing.
Describes the audio processing environment (sample rate, block size, channels).
Non-owning view over audio channel data.
Owning audio buffer with contiguous, 32-byte aligned storage.
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.
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).
int maxBlockSize
Maximum number of samples per processing block.