38#include "../Core/Oscillator.h"
39#include "../Core/DryWetMixer.h"
40#include "../Core/AudioSpec.h"
41#include "../Core/AudioBuffer.h"
42#include "../Core/DspMath.h"
43#include "../Core/StateBlob.h"
93 lfo_.setFrequency(
rate_.load(std::memory_order_relaxed));
98 lfoR_.setFrequency(
rate_.load(std::memory_order_relaxed));
116 const int nCh = std::min(buffer.getNumChannels(),
kMaxChannels);
117 const int nS = buffer.getNumSamples();
124 for (
int ch = 0; ch < buffer.getNumChannels(); ++ch)
126 T* d = buffer.getChannel(ch);
127 for (
int i = 0; i < nS; ++i)
128 if (!std::isfinite(d[i])) d[i] = T(0);
132 const T targetDepth =
depth_.load(std::memory_order_relaxed);
133 const T targetMix =
mix_.load(std::memory_order_relaxed);
134 const T targetFb =
feedback_.load(std::memory_order_relaxed);
135 const int stagesVal =
numStages_.load(std::memory_order_relaxed);
136 const T minF =
minFreq_.load(std::memory_order_relaxed);
137 const T maxF =
maxFreq_.load(std::memory_order_relaxed);
138 const T spreadVal =
stereoSpread_.load(std::memory_order_relaxed);
142 lfo_.setFrequency(
rate_.load(std::memory_order_relaxed));
144 lfoR_.setFrequency(
rate_.load(std::memory_order_relaxed));
152 T ph =
lfo_.getPhase() + spreadVal * T(0.5);
153 ph -= std::floor(ph);
156 const bool useSpread = (spreadVal > T(0.0001)) && (nCh >= 2);
171 const T logMin = std::log(minF);
172 const T logMax = std::log(maxF);
175 constexpr T kPi =
static_cast<T
>(std::numbers::pi);
177 auto coeffFromLfo = [&](T lfoVal)
noexcept -> T
180 T logFreq = logMin + modAmount * (logMax - logMin);
182 T cutoff = std::min(
fastExp(logFreq), nyquist);
185 T tanVal =
fastTan(kPi * cutoff * fsInv);
186 return (tanVal - T(1)) / (tanVal + T(1));
189 for (
int i = 0; i < nS; ++i)
195 const T cL = coeffFromLfo(
lfo_.getNextSample());
196 const T lfoRVal =
lfoR_.getNextSample();
197 const T cR = useSpread ? coeffFromLfo(lfoRVal) : cL;
200 for (
int ch = 0; ch < nCh; ++ch)
202 const T c = (ch & 1) ? cR : cL;
203 T sample = buffer.getChannel(ch)[i];
212 for (
int s = 0; s < stagesVal; ++s)
215 T y = c * sample + st.xPrev[ch] - c * st.yPrev[ch];
216 st.xPrev[ch] = sample;
222 buffer.getChannel(ch)[i] = sample;
227 mixer_.mixWet(buffer, targetMix);
238 stage.xPrev.fill(T(0));
239 stage.yPrev.fill(T(0));
241 for (
auto& fb :
fbState_) fb = T(0);
261 if (!std::isfinite(hz))
return;
262 rate_.store(std::clamp(hz, T(0.01), T(20)), std::memory_order_relaxed);
272 if (!std::isfinite(amount))
return;
273 depth_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
283 if (!std::isfinite(dryWet))
return;
284 mix_.store(std::clamp(dryWet, T(0), T(1)), std::memory_order_relaxed);
305 if (!std::isfinite(amount))
return;
306 feedback_.store(std::clamp(amount, T(-0.99), T(0.99)), std::memory_order_relaxed);
321 if (!std::isfinite(amount))
return;
322 stereoSpread_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
337 if (!std::isfinite(hz) || !(hz > T(0)))
return;
338 T curMin =
minFreq_.load(std::memory_order_relaxed);
339 T curMax =
maxFreq_.load(std::memory_order_relaxed);
340 T halfRange = std::sqrt(curMax / (curMin > T(0) ? curMin : T(1)));
344 const T mn = std::clamp(hz / halfRange, T(20), ceiling - T(1));
345 minFreq_.store(mn, std::memory_order_relaxed);
346 maxFreq_.store(std::clamp(hz * halfRange, mn + T(1), ceiling),
347 std::memory_order_relaxed);
358 if (!std::isfinite(minHz) || !std::isfinite(maxHz))
return;
359 T mn = std::max(minHz, T(20));
360 minFreq_.store(mn, std::memory_order_relaxed);
361 maxFreq_.store(std::max(maxHz, mn + T(1)), std::memory_order_relaxed);
372 const int w = std::clamp(
static_cast<int>(wf), 0,
375 std::memory_order_relaxed);
382 [[nodiscard]] T
getRate() const noexcept {
return rate_.load(std::memory_order_relaxed); }
386 [[nodiscard]] std::vector<uint8_t>
getState()
const
389 w.
write(
"rate",
rate_.load(std::memory_order_relaxed));
390 w.
write(
"depth",
depth_.load(std::memory_order_relaxed));
391 w.
write(
"mix",
mix_.load(std::memory_order_relaxed));
393 w.
write(
"minFreq",
minFreq_.load(std::memory_order_relaxed));
394 w.
write(
"maxFreq",
maxFreq_.load(std::memory_order_relaxed));
398 static_cast<int32_t
>(
lfoWaveform_.load(std::memory_order_relaxed)));
412 static_cast<T
>(r.
read(
"maxFreq", 6000.0f)));
416 r.
read(
"waveform", 0)));
428 std::atomic<T>
mix_ { T(0.5) };
446 std::array<T, kMaxChannels>
xPrev {};
447 std::array<T, kMaxChannels>
yPrev {};
451 std::array<FirstOrderAllpass, kMaxStages>
stages_ {};
Non-owning view over audio channel data.
Pre-allocated, SIMD-friendly dry/wet blender for real-time audio.
Band-limited oscillator featuring PolyBLEP anti-aliasing and analog-modeled integration.
Zero-latency, highly optimized allpass-based phaser.
void setFeedback(T amount) noexcept
Injects phase-shifted signal back into the input for resonance.
std::atomic< int > numStages_
std::atomic< T > feedback_
void setMix(T dryWet) noexcept
Balances the unprocessed and processed signal.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
int getStages() const noexcept
Retrieves the active stage count.
static constexpr int kMaxStages
void setDepth(T amount) noexcept
Sets how wide the frequency sweep is.
void setFrequencyRange(T minHz, T maxHz) noexcept
Explicitly defines the sweep boundaries.
void setCenterFrequency(T hz) noexcept
Defines the midpoint of the logarithmic frequency sweep.
void setStages(int count) noexcept
Configures the intensity/color of the phaser via filter stages.
void reset() noexcept
Clears internal DSP state and history buffers. Call this when transport stops or playback jumps.
void setStereoSpread(T amount) noexcept
Sets the stereo phase spread of the sweep LFO.
T getRate() const noexcept
Retrieves the current sweep rate.
std::atomic< T > stereoSpread_
std::array< FirstOrderAllpass, kMaxStages > stages_
std::atomic< T > maxFreq_
std::array< T, kMaxChannels > fbState_
std::atomic< typename Oscillator< T >::Waveform > lfoWaveform_
void prepare(const AudioSpec &spec)
Prepares the phaser and allocates internal state blocks.
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
std::atomic< T > minFreq_
void setRate(T hz) noexcept
Sets the speed of the phaser sweep.
void setLfoWaveform(typename Oscillator< T >::Waveform wf) noexcept
Changes the geometric shape of the LFO modulation.
static constexpr int kMaxChannels
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an audio block in-place.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
bool isValid() const noexcept
uint32_t processorId() const noexcept
Serializes key/value parameters into a versioned blob.
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
void write(const char *key, float value)
Writes a float parameter.
Main namespace for the DSPark framework.
T fastTan(T x) noexcept
Fast approximation of tan(x) using a Pade [5,4] rational approximant.
T fastTanh(T x) noexcept
Fast tanh approximation using Pade rational function.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
T fastExp(T x) noexcept
Fast approximation of e^x via std::exp2 (~2x faster than std::exp on MSVC).
Describes the audio environment for a DSP processor.
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
double sampleRate
Sample rate in Hz.
std::array< T, kMaxChannels > xPrev
std::array< T, kMaxChannels > yPrev