26#include "../Core/DspMath.h"
67 curve =
static_cast<Curve>(std::clamp(
static_cast<int>(curve), 0,
69 curve_.store(curve, std::memory_order_relaxed);
83 if (!std::isfinite(position))
return;
84 position_.store(std::clamp(position, T(0), T(1)), std::memory_order_relaxed);
93 return position_.load(std::memory_order_relaxed);
107 [[nodiscard]]
inline T
process(T a, T b)
noexcept
109 refreshGainsFromAtomics();
110 return a * gainA_ + b * gainB_;
125 void process(
const T* inputA,
const T* inputB, T* output,
int numSamples)
noexcept
127 assert(inputA !=
nullptr && inputB !=
nullptr && output !=
nullptr);
128 assert(numSamples > 0);
130 const T oldGainA = gainA_;
131 const T oldGainB = gainB_;
133 refreshGainsFromAtomics();
135 if (oldGainA == gainA_ && oldGainB == gainB_)
140 for (
int i = 0; i < numSamples; ++i)
141 output[i] = inputA[i] * gA + inputB[i] * gB;
146 const T invSamples = T(1) /
static_cast<T
>(numSamples);
147 const T stepA = (gainA_ - oldGainA) * invSamples;
148 const T stepB = (gainB_ - oldGainB) * invSamples;
150 T currentA = oldGainA;
151 T currentB = oldGainB;
153 for (
int i = 0; i < numSamples; ++i)
157 output[i] = inputA[i] * currentA + inputB[i] * currentB;
175 const T* positions, T* output,
int numSamples)
noexcept
177 assert(inputA && inputB && positions && output);
178 assert(numSamples > 0);
180 Curve curv = curve_.load(std::memory_order_relaxed);
181 T gA = gainA_, gB = gainB_;
183 for (
int i = 0; i < numSamples; ++i)
187 T pos = std::min(T(1), std::max(T(0), positions[i]));
188 computeGains(curv, pos, gA, gB);
189 output[i] = inputA[i] * gA + inputB[i] * gB;
195 lastPos_ = std::min(T(1), std::max(T(0), positions[numSamples - 1]));
202 [[nodiscard]] T
getGainA() const noexcept {
return gainA_; }
205 [[nodiscard]] T
getGainB() const noexcept {
return gainB_; }
211 static inline void computeGains(
Curve curve, T pos, T& gA, T& gB)
noexcept
224 gA = std::sqrt(T(1) - pos);
230 T t = pos * pos * (T(3) - T(2) * pos);
248 inline void refreshGainsFromAtomics() noexcept
250 T pos = position_.load(std::memory_order_relaxed);
251 Curve curv = curve_.load(std::memory_order_relaxed);
253 if (pos != lastPos_ || curv != lastCurve_)
255 computeGains(curv, pos, gainA_, gainB_);
263 std::atomic<T> position_ { T(0) };
Artifact-free, SIMD-friendly crossfader for two audio signals.
void processAutomated(const T *inputA, const T *inputB, const T *positions, T *output, int numSamples) noexcept
Processes crossfading using a per-sample automation buffer.
void setPosition(T position) noexcept
Sets the target crossfade blend position.
Curve
Defines the amplitude response of the crossfade transition.
@ Linear
Linear interpolation. Constant amplitude, drops power at center.
@ EqualPower
Equal power interpolation (hardware SQRT). Constant power, no volume drop.
@ SCurve
Smoothstep interpolation. Slower progression at extremes.
T process(T a, T b) noexcept
Crossfades between two individual samples.
T getGainB() const noexcept
Gets the current internal gain multiplier for signal B.
T getPosition() const noexcept
Retrieves the last requested position.
T getGainA() const noexcept
Gets the current internal gain multiplier for signal A.
void setCurve(Curve curve) noexcept
Sets the crossfade curve type. Thread-safe. Can be called from the GUI thread.
void process(const T *inputA, const T *inputB, T *output, int numSamples) noexcept
Crossfades two audio buffers into an output buffer with automatic parameter smoothing.
Main namespace for the DSPark framework.