42#include "../Core/AudioBuffer.h"
43#include "../Core/AudioSpec.h"
44#include "../Core/Biquad.h"
45#include "../Core/DspMath.h"
46#include "../Core/Smoothers.h"
47#include "../Core/AnalogRandom.h"
48#include "../Core/DenormalGuard.h"
49#include "../Core/StateBlob.h"
70template <
typename T,
int MaxChannels = 16>
116 for (
auto& stage :
stages_) stage.reset();
130 void setLowPass(
float freq,
float Q = 0.707f,
int slopeDb = 12)
303 for (
int i = 0; i < c.numSecondOrder && i < 4; ++i) info.
qValues[i] = c.qValues[i];
324 newShape =
static_cast<Shape>(std::clamp(
static_cast<int>(newShape), 0,
349 if (!std::isfinite(freq))
return;
350 targetFreq_.store(freq, std::memory_order_relaxed);
365 if (!std::isfinite(Q))
return;
366 targetRes_.store(Q, std::memory_order_relaxed);
375 if (!std::isfinite(dB))
return;
386 if (!std::isfinite(amount))
return;
387 targetNonlinearity_.store(
static_cast<float>(std::clamp(amount, T(0), T(1))), std::memory_order_relaxed);
403 if (!std::isfinite(intensity))
return;
427 const int nCh = std::min(buffer.getNumChannels(), MaxChannels);
428 const int nS = buffer.getNumSamples();
433 for (
int ch = 0; ch < nCh; ++ch)
435 T* d = buffer.getChannel(ch);
436 for (
int i = 0; i < nS; ++i)
437 if (!std::isfinite(d[i])) d[i] = T(0);
445 const bool drift =
driftEnabled_.load(std::memory_order_relaxed);
446 const float driftInt =
driftIntensity_.load(std::memory_order_relaxed);
449 const bool dynamicPath = needSmoothing || drift || (nonLin > T(0));
451 constexpr int kChunkSize = 16;
463 f = std::clamp(f, 10.0f, nyquist);
464 q = std::max(q, 0.1f);
468 const Shape sh =
shape_.load(std::memory_order_relaxed);
469 const int sdb =
slopeDb_.load(std::memory_order_relaxed);
470 const bool mp =
matchedPeak_.load(std::memory_order_relaxed);
471 const float ssl =
shelfSlope_.load(std::memory_order_relaxed);
482 const int ns =
numStages_.load(std::memory_order_relaxed);
483 for (
int ch = 0; ch < nCh; ++ch)
485 T* channelData = buffer.getChannel(ch);
486 for (
int i = 0; i < nS; ++i)
491 double sample =
static_cast<double>(channelData[i]);
492 for (
int s = 0; s < ns; ++s)
493 sample =
stages_[s].processSampleCore(sample, ch);
494 channelData[i] =
static_cast<T
>(sample);
503 for (
int i = 0; i < nS; ++i)
510 if (i % kChunkSize == 0)
513 freq *= (1.0f + driftValue);
524 for (
int ch = 0; ch < nCh; ++ch)
525 avgAbs += std::abs(buffer.getChannel(ch)[i]);
526 avgAbs /=
static_cast<T
>(nCh);
528 const T bounded = avgAbs / (T(1) + avgAbs);
529 freq *=
static_cast<float>(T(1) + nonLin * T(2) * bounded);
542 const int ns =
numStages_.load(std::memory_order_relaxed);
543 for (
int ch = 0; ch < nCh; ++ch)
545 double sample =
static_cast<double>(buffer.getChannel(ch)[i]);
546 for (
int s = 0; s < ns; ++s)
547 sample =
stages_[s].processSampleCore(sample, ch);
548 buffer.getChannel(ch)[i] =
static_cast<T
>(sample);
567 if (channel < 0 || channel >= MaxChannels)
return input;
568 double sample =
static_cast<double>(input);
569 const int ns =
numStages_.load(std::memory_order_relaxed);
570 for (
int s = 0; s < ns; ++s)
571 sample =
stages_[s].processSampleCore(sample, channel);
572 return static_cast<T
>(sample);
596 f = std::clamp(f, 10.0f, nyquist);
597 q = std::max(q, 0.1f);
609 [[nodiscard]] std::vector<uint8_t>
getState()
const
612 w.
write(
"shape",
static_cast<int32_t
>(
shape_.load(std::memory_order_relaxed)));
627 const auto shape =
static_cast<Shape>(r.
read(
"shape", 0));
628 const float freq = r.
read(
"freq", 1000.0f);
629 const float res = r.
read(
"res", 0.707f);
630 const float gain = r.
read(
"gain", 0.0f);
674 { 0.5412f, 1.3066f },
675 { 0.6180f, 1.6180f },
676 { 0.5176f, 0.7071f, 1.9319f },
677 { 0.5549f, 0.8019f, 2.2470f },
678 { 0.5098f, 0.6013f, 0.9000f, 2.5628f }
683 result.hasFirstOrder = (result.order % 2 != 0);
684 result.numSecondOrder = result.order / 2;
685 for (
int i = 0; i < result.numSecondOrder; ++i)
686 result.qValues[i] = qTable[result.order][i];
692 int order = std::clamp(slopeDb / 6, 1,
kMaxOrder);
693 return (order + 1) / 2;
699 double f =
static_cast<double>(freq);
702 const Shape sh =
shape_.load(std::memory_order_relaxed);
703 const int sdb =
slopeDb_.load(std::memory_order_relaxed);
704 const double ssl =
static_cast<double>(
shelfSlope_.load(std::memory_order_relaxed));
709 if (cascade.hasFirstOrder)
718 stages_[stageIdx++].setCoeffs(c);
721 for (
int s = 0; s < cascade.numSecondOrder; ++s)
723 float stageQ = cascade.qValues[s];
729 s == cascade.numSecondOrder - 1)
738 stageQ *= Q / 0.707f;
758 stages_[stageIdx++].setCoeffs(c);
Main generator class for analog-style random modulation.
void prepare(double sampleRate) noexcept
Prepare the generator with the audio sample rate.
void setAnalogDefault(AnalogComponent component) noexcept
Real getNextSample() noexcept
Generate and return the next modulation sample.
Non-owning view over audio channel data.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
Professional multi-mode filter with cascaded biquad stages.
Smoothers::StateVariableSmoother freqSmoother_
void setNonlinearity(T amount) noexcept
Sets the nonlinearity amount. Thread-safe.
int getSlopeDb() const noexcept
Returns the active slope in dB/oct (LP/HP only - others = 12).
std::atomic< float > targetGain_
void setHighShelf(float freq, float gainDb, float slope=1.0f)
Configures a high-shelf EQ filter.
static constexpr int kMaxOrder
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
void setLowPass(float freq, float Q=0.707f, int slopeDb=12)
Configures a low-pass filter.
void setHighPass(float freq, float Q=0.707f, int slopeDb=12)
Configures a high-pass filter.
std::atomic< Shape > shape_
static constexpr int kMaxStages
Smoothers::LinearSmoother resSmoother_
std::atomic< float > targetFreq_
Shape
Supported filter shapes.
Smoothers::LinearSmoother gainSmoother_
void setTilt(float centerFreq, float gainDb)
Configures a tilt EQ filter (boost highs/cut lows or vice versa).
void applyParametersNow() noexcept
Applies pending parameter targets immediately and rebuilds the coefficients (no smoothing: values jum...
void setAllPass(float freq, float Q=0.707f)
Configures an all-pass filter (shifts phase, flat frequency response).
std::atomic< float > shelfSlope_
static CascadeInfo cascadeForSlope(int slopeDb, float userQ=0.707f) noexcept
Returns the exact Butterworth cascade (first-order flag + per-stage Q values) used internally for a g...
void setLowShelf(float freq, float gainDb, float slope=1.0f)
Configures a low-shelf EQ filter.
void setBandPass(float freq, float Q=0.707f)
Configures a band-pass filter (fixed 12 dB/oct).
std::atomic< bool > matchedPeak_
std::atomic< int > numStages_
std::atomic< bool > driftEnabled_
std::atomic< float > targetNonlinearity_
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void reset() noexcept
Resets the internal state of all cascaded biquads and smoothers. Prevents clicks when relocating play...
void setGain(float dB) noexcept
Sets the target gain in dB. Thread-safe.
void updateCoefficients(float freq, float Q, float gainDb) noexcept
std::atomic< float > targetRes_
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an entire block of audio data. Internally branches into static, smoothed,...
std::atomic< float > driftIntensity_
void setFrequency(float freq) noexcept
Sets the target cutoff/center frequency. Thread-safe.
static ButterworthCascade computeCascade(int slopeDb) noexcept
std::array< Biquad< T, MaxChannels >, kMaxStages > stages_
void setResonance(float Q) noexcept
Sets the target resonance/Q. Thread-safe.
bool isMatchedPeak() const noexcept
Returns whether Peak bells use the matched (de-cramped) design.
Shape getShape() const noexcept
Returns the active filter shape.
void enableAnalogDrift(AnalogRandom::AnalogComponent component, float intensity=0.5f)
Enables analog-style low-frequency modulation of the cutoff.
AnalogRandom::Generator< float > driftGen_
float lastShelfSlope_
Shelf S: a slope-only change must rebuild coeffs.
void prepare(const AudioSpec &spec)
Initializes the filter engine with the current audio specification.
void setPeaking(float freq, float gainDb, float Q=1.0f)
Configures a peaking / bell EQ filter.
T processSample(T input, int channel) noexcept
Processes a single sample without parameter smoothing or coefficient updates.
void setNotch(float freq, float Q=10.0f)
Configures a notch (band-reject) filter.
std::atomic< int > slopeDb_
void setShape(Shape newShape, int slopeDb=12) noexcept
Switches the filter topology while keeping the current frequency, resonance, and gain unchanged.
void disableAnalogDrift() noexcept
Disables analog-style drift modulation. Thread-safe.
static int slopeToStages(int slopeDb) noexcept
void setMatchedPeak(bool enabled) noexcept
Selects the Orfanidis matched (de-cramped) design for Peak bells.
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.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
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.
Stores normalised biquad coefficients (b0, b1, b2, a1, a2), always double.
static BiquadCoeffs makeFirstOrderHighPass(double sampleRate, double frequency) noexcept
First-order (6 dB/oct) high-pass filter.
static BiquadCoeffs makePeakMatched(double sampleRate, double freq, double Q, double gainDb) noexcept
Peaking filter with prescribed Nyquist gain (Orfanidis design).
static BiquadCoeffs makeHighPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
High-pass filter.
static BiquadCoeffs makeAllPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
All-pass filter.
static BiquadCoeffs makeBandPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
Band-pass filter (constant 0 dB peak gain).
static BiquadCoeffs makePeak(double sampleRate, double freq, double Q, double gainDb) noexcept
Peak (parametric EQ) filter.
static BiquadCoeffs makeFirstOrderLowPass(double sampleRate, double frequency) noexcept
First-order (6 dB/oct) low-pass filter.
static BiquadCoeffs makeTilt(double sampleRate, double pivotFreq, double gainDb) noexcept
Creates a first-order tilt filter.
static BiquadCoeffs makeLowPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
Low-pass filter.
static BiquadCoeffs makeLowShelf(double sampleRate, double freq, double gainDb, double slope=1.0) noexcept
Low-shelf filter.
static BiquadCoeffs makeHighShelf(double sampleRate, double freq, double gainDb, double slope=1.0) noexcept
High-shelf filter.
static BiquadCoeffs makeNotch(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
Notch (band-reject) filter.
float qValues[kMaxStages]
Per-stage Butterworth cascade layout for an LP/HP slope (for analysis).
Linear ramp smoother for predictable, uniform interpolation.
void reset(double sampleRate, float rampTimeMilliseconds, float initialValue=0.0f) noexcept
float getNextValue() noexcept
void setTargetValue(float newTarget) noexcept
bool isSmoothing() const noexcept
float getCurrentValue() const noexcept
Second-order state variable filter (SVF) smoother (TPT implementation).
float getCurrentValue() const noexcept
void reset(double sampleRate, float timeConstantMilliseconds, float q=0.707f, float initialValue=0.0f) noexcept
void setTargetValue(float newTarget) noexcept
float getNextValue() noexcept
bool isSmoothing() const noexcept