52#include "../Core/DspMath.h"
53#include "../Core/AudioSpec.h"
54#include "../Core/AudioBuffer.h"
55#include "../Core/DenormalGuard.h"
56#include "../Core/StateBlob.h"
107 if (!(sampleRate > 0.0))
return;
133 const int nCh = buffer.getNumChannels();
134 const int nS = buffer.getNumSamples();
135 if (nCh <= 0 || nS <= 0)
return;
139 for (
int i = 0; i < nS; ++i)
147 for (
int ch = 0; ch < nCh; ++ch)
149 T s = buffer.getChannel(ch)[i];
152 if (ch == 0) det0 = s;
153 level = std::max(level, std::abs(s));
164 for (
int ch = 0; ch < nCh; ++ch)
166 T* d = buffer.getChannel(ch);
190 const int nCh = audio.getNumChannels();
191 const int nS = audio.getNumSamples();
192 const int scCh = sidechain.getNumChannels();
193 if (nCh <= 0 || nS <= 0)
return;
198 if (nCh == 1 && scCh == 1)
200 T* __restrict outL = audio.getChannel(0);
201 const T* __restrict scL = sidechain.getChannel(0);
202 for (
int i = 0; i < nS; ++i)
209 for (
int i = 0; i < nS; ++i)
213 for (
int c = 0; c < scCh; ++c)
215 T s = sidechain.getChannel(c)[i];
218 if (c == 0) det0 = s;
220 if (a > scMax) scMax = a;
229 for (
int ch = 0; ch < nCh; ++ch)
231 T* d = audio.getChannel(ch);
247 if (!std::isfinite(dB))
return;
248 threshold_.store(dB, std::memory_order_relaxed);
258 if (!std::isfinite(dB))
return;
259 hysteresis_.store(std::max(T(0), dB), std::memory_order_relaxed);
266 if (!std::isfinite(ms))
return;
267 attackMs_.store(std::max(T(0.01), ms), std::memory_order_relaxed);
274 if (!std::isfinite(ms))
return;
275 holdMs_.store(std::max(T(0), ms), std::memory_order_relaxed);
282 if (!std::isfinite(ms))
return;
283 releaseMs_.store(std::max(T(0.01), ms), std::memory_order_relaxed);
293 if (!std::isfinite(dB))
return;
294 rangeDb_.store(std::min(T(0), dB), std::memory_order_relaxed);
301 duckMode_.store(enabled, std::memory_order_relaxed);
308 const int m = std::clamp(
static_cast<int>(mode), 0,
static_cast<int>(
GateMode::Frequency));
333 if (std::isfinite(cutoffHz) && cutoffHz > 0.0)
334 scHpfFreq_.store(
static_cast<T
>(cutoffHz), std::memory_order_relaxed);
396 [[nodiscard]] std::vector<uint8_t>
getState()
const
399 w.
write(
"threshold",
static_cast<float>(
threshold_.load(std::memory_order_relaxed)));
400 w.
write(
"hysteresis",
static_cast<float>(
hysteresis_.load(std::memory_order_relaxed)));
401 w.
write(
"attack",
static_cast<float>(
attackMs_.load(std::memory_order_relaxed)));
402 w.
write(
"hold",
static_cast<float>(
holdMs_.load(std::memory_order_relaxed)));
403 w.
write(
"release",
static_cast<float>(
releaseMs_.load(std::memory_order_relaxed)));
404 w.
write(
"range",
static_cast<float>(
rangeDb_.load(std::memory_order_relaxed)));
406 w.
write(
"gateMode",
static_cast<int32_t
>(
gateMode_.load(std::memory_order_relaxed)));
409 w.
write(
"scHpfFreq",
static_cast<float>(
scHpfFreq_.load(std::memory_order_relaxed)));
428 static_cast<double>(r.
read(
"scHpfFreq", 80.0f)));
440 &&
paramsDirty_.exchange(
false, std::memory_order_acquire))
447 if (!(fs > T(0)))
return;
456 T thDb =
threshold_.load(std::memory_order_relaxed);
457 T hystDb =
hysteresis_.load(std::memory_order_relaxed);
462 T attMs = std::max(
attackMs_.load(std::memory_order_relaxed), T(0.01));
463 T relMs = std::max(
releaseMs_.load(std::memory_order_relaxed), T(0.01));
464 attackCoeff_ = T(1) - std::exp(T(-1) / (fs * attMs / T(1000)));
465 releaseCoeff_ = T(1) - std::exp(T(-1) / (fs * relMs / T(1000)));
475 T hMs = std::max(
holdMs_.load(std::memory_order_relaxed), T(0));
476 holdSamples_ =
static_cast<int>(std::min(fs *
static_cast<double>(hMs) / 1000.0, 1.0e9));
480 const double scFreq = std::clamp(
481 static_cast<double>(
scHpfFreq_.load(std::memory_order_relaxed)),
483 scHpfCoeff_ =
static_cast<T
>(std::exp(-std::numbers::pi * 2.0 * scFreq /
static_cast<double>(fs)));
509 std::swap(above, below);
546 bool sign = sample >= T(0);
581 T targetHp = T(20) + (
cachedNyquist_ * T(0.4)) * (T(1) - gateOpenness);
613 T rawLevel = std::abs(sidechain);
Non-owning view over audio channel data.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
High-performance noise gate with state machine, hysteresis, and zero-allocation processing.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void prepare(double sampleRate) noexcept
Prepares the noise gate for processing.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an AudioBufferView in-place.
std::array< T, kMaxScChannels > scHpfPrev_
std::array< T, kMaxChannels > freqLpState_
T applyFrequencyGate(T input, int ch) noexcept
T processSample(T input) noexcept
Processes a single mono sample.
void setSidechainHPF(bool enabled, double cutoffHz=80.0) noexcept
Configures sidechain High-Pass filter.
void setRange(T dB) noexcept
Sets maximum attenuation range.
std::array< T, kMaxScChannels > scHpfState_
void processBlock(AudioBufferView< T > audio, AudioBufferView< T > sidechain) noexcept
Processes audio with an external sidechain signal.
T processSampleWithSidechain(T input, T sidechain) noexcept
Processes a mono sample using an external sidechain.
static constexpr int kZeroCrossWindow
void setAttack(T ms) noexcept
Sets attack time in milliseconds. Non-finite values are ignored.
void updateStateMachine(T envelopeLinear) noexcept
void setRelease(T ms) noexcept
Sets release time in milliseconds. Non-finite values are ignored.
void prepare(const AudioSpec &spec) noexcept
Prepares from AudioSpec (unified API).
static constexpr int kMaxChannels
std::atomic< T > threshold_
void setHold(T ms) noexcept
Sets hold time in milliseconds. Non-finite values are ignored.
std::array< T, kMaxChannels > freqLpFreq_
void setGateMode(GateMode mode) noexcept
Selects the processing mode (Amplitude or Frequency; wild enum values clamp).
void updateZeroCrossing(T sample) noexcept
GateMode
Operating mode for the gate processing.
@ Frequency
Gatelope-style: narrows bandpass dynamically instead of direct gain reduction.
@ Amplitude
Standard amplitude gain reduction (default).
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
T cachedCloseThresholdLinear_
std::array< T, kMaxChannels > freqHpState_
void syncParamsIfDirty() noexcept
T processSampleInternal(T input, T sidechain, int ch) noexcept
std::atomic< T > releaseMs_
T getCurrentGain() noexcept
std::atomic< GateMode > gateMode_
std::array< T, kMaxChannels > freqHpFreq_
State
Gate state machine phases.
@ Closed
Gate is fully closed (applying range attenuation).
@ Hold
Gate is open but counting down hold time before closing.
@ Open
Gate is fully open (passing audio).
std::atomic< T > rangeDb_
std::atomic< T > attackMs_
T computeEnvelopeFollower(T rawLevel) noexcept
Updates envelope state to prevent intra-cycle chattering.
static constexpr int kMaxScChannels
std::atomic< T > hysteresis_
void setHysteresis(T dB) noexcept
Sets the hysteresis amount (gap between open and close thresholds).
T applyScHpf(T input, int ch) noexcept
Per-channel one-pole sidechain high-pass (unity gain at Nyquist).
void setThreshold(T dB) noexcept
Sets the opening threshold.
std::atomic< T > scHpfFreq_
void setDuckMode(bool enabled) noexcept
Toggles ducking mode (invert gate logic).
std::array< T, kMaxChannels > freqHpPrev_
void reset() noexcept
Resets DSP state (clears filters, sets state to Closed).
void setAdaptiveHold(bool enabled) noexcept
Toggles adaptive hold based on zero-crossing rate.
std::atomic< bool > paramsDirty_
std::atomic< bool > duckMode_
void syncParams() noexcept
std::atomic< bool > adaptiveHold_
std::atomic< bool > scHpfEnabled_
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 decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
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.