43#include "../Core/AudioBuffer.h"
44#include "../Core/AudioSpec.h"
45#include "../Core/Biquad.h"
46#include "../Core/DspMath.h"
47#include "../Core/Smoothers.h"
48#include "../Core/AnalogRandom.h"
49#include "../Core/DenormalGuard.h"
50#include "../Core/StateBlob.h"
71template <
typename T,
int MaxChannels = 16>
117 for (
auto& stage :
stages_) stage.reset();
119 if (
const std::uint64_t seed =
driftSeed_.load(std::memory_order_relaxed); seed != 0)
134 void setLowPass(
float freq,
float Q = 0.707f,
int slopeDb = 12)
296 if (!std::isfinite(slope))
return;
297 shelfSlope_.store(std::clamp(slope, 0.0001f, 1.0f), std::memory_order_relaxed);
303 return shelfSlope_.load(std::memory_order_relaxed);
331 for (
int i = 0; i < c.numSecondOrder && i < 4; ++i) info.
qValues[i] = c.qValues[i];
352 newShape =
static_cast<Shape>(std::clamp(
static_cast<int>(newShape), 0,
377 if (!std::isfinite(freq))
return;
378 targetFreq_.store(freq, std::memory_order_relaxed);
393 if (!std::isfinite(Q))
return;
394 targetRes_.store(Q, std::memory_order_relaxed);
403 if (!std::isfinite(dB))
return;
414 if (!std::isfinite(amount))
return;
415 targetNonlinearity_.store(
static_cast<float>(std::clamp(amount, T(0), T(1))), std::memory_order_relaxed);
431 if (!std::isfinite(intensity))
return;
458 driftSeed_.store(seed, std::memory_order_relaxed);
466 return driftSeed_.load(std::memory_order_relaxed);
481 const int nCh = std::min(buffer.getNumChannels(), MaxChannels);
482 const int nS = buffer.getNumSamples();
487 for (
int ch = 0; ch < nCh; ++ch)
489 T* d = buffer.getChannel(ch);
490 for (
int i = 0; i < nS; ++i)
491 if (!std::isfinite(d[i])) d[i] = T(0);
499 const bool drift =
driftEnabled_.load(std::memory_order_relaxed);
500 const float driftInt =
driftIntensity_.load(std::memory_order_relaxed);
503 const bool dynamicPath = needSmoothing || drift || (nonLin > T(0));
505 constexpr int kChunkSize = 16;
517 f = std::clamp(f, 10.0f, nyquist);
518 q = std::max(q, 0.1f);
522 const Shape sh =
shape_.load(std::memory_order_relaxed);
523 const int sdb =
slopeDb_.load(std::memory_order_relaxed);
524 const bool mp =
matchedPeak_.load(std::memory_order_relaxed);
525 const float ssl =
shelfSlope_.load(std::memory_order_relaxed);
536 const int ns =
numStages_.load(std::memory_order_relaxed);
537 for (
int ch = 0; ch < nCh; ++ch)
539 T* channelData = buffer.getChannel(ch);
540 for (
int i = 0; i < nS; ++i)
545 double sample =
static_cast<double>(channelData[i]);
546 for (
int s = 0; s < ns; ++s)
547 sample =
stages_[s].processSampleCore(sample, ch);
548 channelData[i] =
static_cast<T
>(sample);
557 for (
int i = 0; i < nS; ++i)
564 if (i % kChunkSize == 0)
567 freq *= (1.0f + driftValue);
578 for (
int ch = 0; ch < nCh; ++ch)
579 avgAbs += std::abs(buffer.getChannel(ch)[i]);
580 avgAbs /=
static_cast<T
>(nCh);
582 const T bounded = avgAbs / (T(1) + avgAbs);
583 freq *=
static_cast<float>(T(1) + nonLin * T(2) * bounded);
596 const int ns =
numStages_.load(std::memory_order_relaxed);
597 for (
int ch = 0; ch < nCh; ++ch)
599 double sample =
static_cast<double>(buffer.getChannel(ch)[i]);
600 for (
int s = 0; s < ns; ++s)
601 sample =
stages_[s].processSampleCore(sample, ch);
602 buffer.getChannel(ch)[i] =
static_cast<T
>(sample);
621 if (channel < 0 || channel >= MaxChannels)
return input;
622 double sample =
static_cast<double>(input);
623 const int ns =
numStages_.load(std::memory_order_relaxed);
624 for (
int s = 0; s < ns; ++s)
625 sample =
stages_[s].processSampleCore(sample, channel);
626 return static_cast<T
>(sample);
650 f = std::clamp(f, 10.0f, nyquist);
651 q = std::max(q, 0.1f);
663 [[nodiscard]] std::vector<uint8_t>
getState()
const
666 w.
write(
"shape",
static_cast<int32_t
>(
shape_.load(std::memory_order_relaxed)));
681 const auto shape =
static_cast<Shape>(r.
read(
"shape", 0));
682 const float freq = r.
read(
"freq", 1000.0f);
683 const float res = r.
read(
"res", 0.707f);
684 const float gain = r.
read(
"gain", 0.0f);
728 { 0.5412f, 1.3066f },
729 { 0.6180f, 1.6180f },
730 { 0.5176f, 0.7071f, 1.9319f },
731 { 0.5549f, 0.8019f, 2.2470f },
732 { 0.5098f, 0.6013f, 0.9000f, 2.5628f }
737 result.hasFirstOrder = (result.order % 2 != 0);
738 result.numSecondOrder = result.order / 2;
739 for (
int i = 0; i < result.numSecondOrder; ++i)
740 result.qValues[i] = qTable[result.order][i];
746 int order = std::clamp(slopeDb / 6, 1,
kMaxOrder);
747 return (order + 1) / 2;
753 double f =
static_cast<double>(freq);
756 const Shape sh =
shape_.load(std::memory_order_relaxed);
757 const int sdb =
slopeDb_.load(std::memory_order_relaxed);
758 const double ssl =
static_cast<double>(
shelfSlope_.load(std::memory_order_relaxed));
763 if (cascade.hasFirstOrder)
772 stages_[stageIdx++].setCoeffs(c);
775 for (
int s = 0; s < cascade.numSecondOrder; ++s)
777 float stageQ = cascade.qValues[s];
783 s == cascade.numSecondOrder - 1)
792 stageQ *= Q / 0.707f;
812 stages_[stageIdx++].setCoeffs(c);
Main generator class for analog-style random modulation.
void reseed(std::uint64_t newSeed) noexcept
Request a lock-free reseed of the internal PRNG.
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_
float getShelfSlope() const noexcept
Returns the shelf slope S used by the shelf shapes.
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_
std::atomic< std::uint64_t > driftSeed_
0 = the instance's unique seed.
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 setShelfSlope(float slope) noexcept
Sets the shelf slope S on its own (LowShelf/HighShelf shapes).
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.
std::uint64_t getDriftSeed() const noexcept
The drift seed set by setDriftSeed(); 0 when the drift uses the instance's own unique seed.
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 setDriftSeed(std::uint64_t seed) noexcept
Makes the analog drift reproducible.
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 analog-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
Analog-matched ("de-cramped") peaking filter (Vicanek 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