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"
91 lfo_.setFrequency(
rate_.load(std::memory_order_relaxed));
95 lfoR_.setFrequency(
rate_.load(std::memory_order_relaxed));
113 const int nCh = std::min(buffer.getNumChannels(),
kMaxChannels);
114 const int nS = buffer.getNumSamples();
121 for (
int ch = 0; ch < buffer.getNumChannels(); ++ch)
123 T* d = buffer.getChannel(ch);
124 for (
int i = 0; i < nS; ++i)
125 if (!std::isfinite(d[i])) d[i] = T(0);
129 const T targetDepth =
depth_.load(std::memory_order_relaxed);
130 const T targetMix =
mix_.load(std::memory_order_relaxed);
131 const T targetFb =
feedback_.load(std::memory_order_relaxed);
132 const int stagesVal =
numStages_.load(std::memory_order_relaxed);
133 const T minF =
minFreq_.load(std::memory_order_relaxed);
134 const T maxF =
maxFreq_.load(std::memory_order_relaxed);
135 const T spreadVal =
stereoSpread_.load(std::memory_order_relaxed);
139 lfo_.setFrequency(
rate_.load(std::memory_order_relaxed));
141 lfoR_.setFrequency(
rate_.load(std::memory_order_relaxed));
149 T ph =
lfo_.getPhase() + spreadVal * T(0.5);
150 ph -= std::floor(ph);
153 const bool useSpread = (spreadVal > T(0.0001)) && (nCh >= 2);
168 const T logMin = std::log(minF);
169 const T logMax = std::log(maxF);
172 constexpr T kPi =
static_cast<T
>(std::numbers::pi);
174 auto coeffFromLfo = [&](T lfoVal)
noexcept -> T
177 T logFreq = logMin + modAmount * (logMax - logMin);
179 T cutoff = std::min(
fastExp(logFreq), nyquist);
182 T tanVal =
fastTan(kPi * cutoff * fsInv);
183 return (tanVal - T(1)) / (tanVal + T(1));
186 for (
int i = 0; i < nS; ++i)
192 const T cL = coeffFromLfo(
lfo_.getNextSample());
193 const T lfoRVal =
lfoR_.getNextSample();
194 const T cR = useSpread ? coeffFromLfo(lfoRVal) : cL;
197 for (
int ch = 0; ch < nCh; ++ch)
199 const T c = (ch & 1) ? cR : cL;
200 T sample = buffer.getChannel(ch)[i];
209 for (
int s = 0; s < stagesVal; ++s)
212 T y = c * sample + st.xPrev[ch] - c * st.yPrev[ch];
213 st.xPrev[ch] = sample;
219 buffer.getChannel(ch)[i] = sample;
224 mixer_.mixWet(buffer, targetMix);
235 stage.xPrev.fill(T(0));
236 stage.yPrev.fill(T(0));
238 for (
auto& fb :
fbState_) fb = T(0);
258 if (!std::isfinite(hz))
return;
259 rate_.store(std::clamp(hz, T(0.01), T(20)), std::memory_order_relaxed);
269 if (!std::isfinite(amount))
return;
270 depth_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
280 if (!std::isfinite(dryWet))
return;
281 mix_.store(std::clamp(dryWet, T(0), T(1)), std::memory_order_relaxed);
302 if (!std::isfinite(amount))
return;
303 feedback_.store(std::clamp(amount, T(-0.99), T(0.99)), std::memory_order_relaxed);
318 if (!std::isfinite(amount))
return;
319 stereoSpread_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
334 if (!std::isfinite(hz) || !(hz > T(0)))
return;
335 T curMin =
minFreq_.load(std::memory_order_relaxed);
336 T curMax =
maxFreq_.load(std::memory_order_relaxed);
337 T halfRange = std::sqrt(curMax / (curMin > T(0) ? curMin : T(1)));
341 const T mn = std::clamp(hz / halfRange, T(20), ceiling - T(1));
342 minFreq_.store(mn, std::memory_order_relaxed);
343 maxFreq_.store(std::clamp(hz * halfRange, mn + T(1), ceiling),
344 std::memory_order_relaxed);
355 if (!std::isfinite(minHz) || !std::isfinite(maxHz))
return;
356 T mn = std::max(minHz, T(20));
357 minFreq_.store(mn, std::memory_order_relaxed);
358 maxFreq_.store(std::max(maxHz, mn + T(1)), std::memory_order_relaxed);
369 const int w = std::clamp(
static_cast<int>(wf), 0,
372 std::memory_order_relaxed);
379 [[nodiscard]] T
getRate() const noexcept {
return rate_.load(std::memory_order_relaxed); }
383 [[nodiscard]] std::vector<uint8_t>
getState()
const
386 w.
write(
"rate",
rate_.load(std::memory_order_relaxed));
387 w.
write(
"depth",
depth_.load(std::memory_order_relaxed));
388 w.
write(
"mix",
mix_.load(std::memory_order_relaxed));
390 w.
write(
"minFreq",
minFreq_.load(std::memory_order_relaxed));
391 w.
write(
"maxFreq",
maxFreq_.load(std::memory_order_relaxed));
395 static_cast<int32_t
>(
lfoWaveform_.load(std::memory_order_relaxed)));
409 static_cast<T
>(r.
read(
"maxFreq", 6000.0f)));
413 r.
read(
"waveform", 0)));
425 std::atomic<T>
mix_ { T(0.5) };
443 std::array<T, kMaxChannels>
xPrev {};
444 std::array<T, kMaxChannels>
yPrev {};
448 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