DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
NoiseGate.h
Go to the documentation of this file.
1// DSPark - Professional Audio DSP Framework
2// Copyright (c) 2026 Cristian Moresi - MIT License
3
4#pragma once
5
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"
57
58#include <algorithm>
59#include <array>
60#include <atomic>
61#include <cmath>
62#include <cstddef>
63#include <cstdint>
64#include <numbers>
65#include <utility>
66#include <vector>
67
68namespace dspark {
69
76template <FloatType T>
78{
79public:
80 ~NoiseGate() = default; // Removed virtual to prevent vptr injection in header-only DSP
81
83 enum class State
84 {
85 Closed,
86 Open,
87 Hold
88 };
89
91 enum class GateMode
92 {
93 Amplitude,
95 };
96
105 void prepare(double sampleRate) noexcept
106 {
107 if (!(sampleRate > 0.0)) return; // NaN-safe validity gate
108 sampleRate_ = sampleRate;
109 syncParams();
110 reset();
111 }
112
117 void prepare(const AudioSpec& spec) noexcept { prepare(spec.sampleRate); }
118
128 void processBlock(AudioBufferView<T> buffer) noexcept
129 {
130 DenormalGuard guard;
132
133 const int nCh = buffer.getNumChannels();
134 const int nS = buffer.getNumSamples();
135 if (nCh <= 0 || nS <= 0) return;
136
137 const bool freqMode = (cachedGateMode_ == GateMode::Frequency);
138
139 for (int i = 0; i < nS; ++i)
140 {
141 // Stereo / N-channel linked detection: peak across ALL channels.
142 // The sidechain HPF runs per channel BEFORE the peak link; channels
143 // beyond the HPF state array are detected unfiltered (sharing a
144 // filter between channels would corrupt its history).
145 T level = T(0);
146 T det0 = T(0);
147 for (int ch = 0; ch < nCh; ++ch)
148 {
149 T s = buffer.getChannel(ch)[i];
151 s = applyScHpf(s, ch);
152 if (ch == 0) det0 = s;
153 level = std::max(level, std::abs(s));
154 }
155
156 // Zero-crossing runs on the same (filtered) signal the detector
157 // sees, exactly like the per-sample path.
159
160 T envelope = computeEnvelopeFollower(level);
161 updateStateMachine(envelope);
162 T gain = getCurrentGain();
163
164 for (int ch = 0; ch < nCh; ++ch)
165 {
166 T* d = buffer.getChannel(ch);
167 // Frequency mode keeps per-channel filter state only for the first
168 // kMaxChannels; any extra channels fall back to amplitude gating.
169 d[i] = (freqMode && ch < kMaxChannels) ? applyFrequencyGate(d[i], ch)
170 : d[i] * gain;
171 }
172 }
173 }
174
185 void processBlock(AudioBufferView<T> audio, AudioBufferView<T> sidechain) noexcept
186 {
187 DenormalGuard guard;
189
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;
194
195 // NOTE: no std::assume_aligned - view pointers are not guaranteed 32-byte
196 // aligned (sub-views / driver buffers), and assuming so is UB. __restrict
197 // still conveys no-aliasing to the compiler for the mono fast path.
198 if (nCh == 1 && scCh == 1)
199 {
200 T* __restrict outL = audio.getChannel(0);
201 const T* __restrict scL = sidechain.getChannel(0);
202 for (int i = 0; i < nS; ++i)
203 outL[i] = processSampleInternal(outL[i], scL[i], 0);
204 }
205 else
206 {
207 const bool freqMode = (cachedGateMode_ == GateMode::Frequency);
208
209 for (int i = 0; i < nS; ++i)
210 {
211 T scMax = T(0);
212 T det0 = T(0);
213 for (int c = 0; c < scCh; ++c)
214 {
215 T s = sidechain.getChannel(c)[i];
217 s = applyScHpf(s, c);
218 if (c == 0) det0 = s;
219 T a = std::abs(s);
220 if (a > scMax) scMax = a;
221 }
222
224
225 T envelope = computeEnvelopeFollower(scMax);
226 updateStateMachine(envelope);
227 T gain = getCurrentGain();
228
229 for (int ch = 0; ch < nCh; ++ch)
230 {
231 T* d = audio.getChannel(ch);
232 d[i] = (freqMode && ch < kMaxChannels) ? applyFrequencyGate(d[i], ch)
233 : d[i] * gain;
234 }
235 }
236 }
237 }
238
239 // -- Parameters (Thread-Safe Setters) -----------------------------------------
240
245 void setThreshold(T dB) noexcept
246 {
247 if (!std::isfinite(dB)) return;
248 threshold_.store(dB, std::memory_order_relaxed);
249 paramsDirty_.store(true, std::memory_order_release);
250 }
251
256 void setHysteresis(T dB) noexcept
257 {
258 if (!std::isfinite(dB)) return;
259 hysteresis_.store(std::max(T(0), dB), std::memory_order_relaxed);
260 paramsDirty_.store(true, std::memory_order_release);
261 }
262
264 void setAttack(T ms) noexcept
265 {
266 if (!std::isfinite(ms)) return;
267 attackMs_.store(std::max(T(0.01), ms), std::memory_order_relaxed);
268 paramsDirty_.store(true, std::memory_order_release);
269 }
270
272 void setHold(T ms) noexcept
273 {
274 if (!std::isfinite(ms)) return;
275 holdMs_.store(std::max(T(0), ms), std::memory_order_relaxed);
276 paramsDirty_.store(true, std::memory_order_release);
277 }
278
280 void setRelease(T ms) noexcept
281 {
282 if (!std::isfinite(ms)) return;
283 releaseMs_.store(std::max(T(0.01), ms), std::memory_order_relaxed);
284 paramsDirty_.store(true, std::memory_order_release);
285 }
286
291 void setRange(T dB) noexcept
292 {
293 if (!std::isfinite(dB)) return;
294 rangeDb_.store(std::min(T(0), dB), std::memory_order_relaxed);
295 paramsDirty_.store(true, std::memory_order_release);
296 }
297
299 void setDuckMode(bool enabled) noexcept
300 {
301 duckMode_.store(enabled, std::memory_order_relaxed);
302 paramsDirty_.store(true, std::memory_order_release);
303 }
304
306 void setGateMode(GateMode mode) noexcept
307 {
308 const int m = std::clamp(static_cast<int>(mode), 0, static_cast<int>(GateMode::Frequency));
309 gateMode_.store(static_cast<GateMode>(m), std::memory_order_relaxed);
310 paramsDirty_.store(true, std::memory_order_release);
311 }
312
314 void setAdaptiveHold(bool enabled) noexcept
315 {
316 adaptiveHold_.store(enabled, std::memory_order_relaxed);
317 paramsDirty_.store(true, std::memory_order_release);
318 }
319
330 void setSidechainHPF(bool enabled, double cutoffHz = 80.0) noexcept
331 {
332 scHpfEnabled_.store(enabled, std::memory_order_relaxed);
333 if (std::isfinite(cutoffHz) && cutoffHz > 0.0)
334 scHpfFreq_.store(static_cast<T>(cutoffHz), std::memory_order_relaxed);
335 paramsDirty_.store(true, std::memory_order_release);
336 }
337
338 // -- Single Sample Processing -------------------------------------------------
339
351 [[nodiscard]] T processSample(T input) noexcept
352 {
354 return processSampleInternal(input, input, 0);
355 }
356
363 [[nodiscard]] T processSampleWithSidechain(T input, T sidechain) noexcept
364 {
366 return processSampleInternal(input, sidechain, 0);
367 }
368
372 void reset() noexcept
373 {
376 envelopeState_ = T(0);
377 holdCounter_ = 0;
378 scHpfState_.fill(T(0));
379 scHpfPrev_.fill(T(0));
380 for (int ch = 0; ch < kMaxChannels; ++ch)
381 {
382 freqLpState_[ch] = T(0);
383 freqHpState_[ch] = T(0);
384 freqHpPrev_[ch] = T(0);
385 freqLpFreq_[ch] = T(20000);
386 freqHpFreq_[ch] = T(20);
387 }
388 zeroCrossCount_ = 0;
390 prevSign_ = false;
392 }
393
394
396 [[nodiscard]] std::vector<uint8_t> getState() const
397 {
398 StateWriter w(stateId("GATE"), 1);
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)));
405 w.write("duck", duckMode_.load(std::memory_order_relaxed));
406 w.write("gateMode", static_cast<int32_t>(gateMode_.load(std::memory_order_relaxed)));
407 w.write("adaptiveHold", adaptiveHold_.load(std::memory_order_relaxed));
408 w.write("scHpf", scHpfEnabled_.load(std::memory_order_relaxed));
409 w.write("scHpfFreq", static_cast<float>(scHpfFreq_.load(std::memory_order_relaxed)));
410 return w.blob();
411 }
412
414 bool setState(const uint8_t* data, size_t size)
415 {
416 StateReader r(data, size);
417 if (!r.isValid() || r.processorId() != stateId("GATE")) return false;
418 setThreshold(static_cast<T>(r.read("threshold", -40.0f)));
419 setHysteresis(static_cast<T>(r.read("hysteresis", 4.0f)));
420 setAttack(static_cast<T>(r.read("attack", 0.5f)));
421 setHold(static_cast<T>(r.read("hold", 50.0f)));
422 setRelease(static_cast<T>(r.read("release", 100.0f)));
423 setRange(static_cast<T>(r.read("range", -80.0f)));
424 setDuckMode(r.read("duck", false));
425 setGateMode(static_cast<GateMode>(r.read("gateMode", 0)));
426 setAdaptiveHold(r.read("adaptiveHold", false));
427 setSidechainHPF(r.read("scHpf", false),
428 static_cast<double>(r.read("scHpfFreq", 80.0f)));
429 return true;
430 }
431
432protected:
433 static constexpr int kMaxChannels = 2;
434
435 void syncParamsIfDirty() noexcept
436 {
437 // Plain load first: the exchange RMW is only paid when a publication
438 // is actually pending (this runs per sample in the per-sample path).
439 if (paramsDirty_.load(std::memory_order_acquire)
440 && paramsDirty_.exchange(false, std::memory_order_acquire))
441 syncParams();
442 }
443
444 void syncParams() noexcept
445 {
446 T fs = static_cast<T>(sampleRate_);
447 if (!(fs > T(0))) return; // NaN-safe (prepare() already gates the rate)
448
449 // Cache logical switches to prevent atomic loads in hot path
450 cachedDuck_ = duckMode_.load(std::memory_order_relaxed);
451 cachedGateMode_ = gateMode_.load(std::memory_order_relaxed);
452 cachedAdaptiveHold_ = adaptiveHold_.load(std::memory_order_relaxed);
453 cachedScHpfEnabled_ = scHpfEnabled_.load(std::memory_order_relaxed);
454
455 // Compute thresholds in LINEAR domain to eliminate log10 in hot path
456 T thDb = threshold_.load(std::memory_order_relaxed);
457 T hystDb = hysteresis_.load(std::memory_order_relaxed);
460 cachedRangeLinear_ = decibelsToGain(rangeDb_.load(std::memory_order_relaxed));
461
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)));
466
467 // Fixed ~2ms release for the detector envelope (prevents bass chattering)
468 detectorReleaseCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.002)));
469
470 // Parameter smoothing coefficient for Frequency Mode (prevents hardcoded 0.001 dependency)
471 freqSmoothCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.02))); // 20ms smoothing
472
473 // Cap the sample count in double before the cast (a cast of an
474 // out-of-int-range double is undefined behaviour).
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));
477
478 // The setter rejects invalid cutoffs; the clamp keeps the coefficient
479 // in the stable range even against a hostile in-memory value.
480 const double scFreq = std::clamp(
481 static_cast<double>(scHpfFreq_.load(std::memory_order_relaxed)),
482 1.0, sampleRate_ * 0.45);
483 scHpfCoeff_ = static_cast<T>(std::exp(-std::numbers::pi * 2.0 * scFreq / static_cast<double>(fs)));
484 scHpfA0_ = (T(1) + scHpfCoeff_) / T(2); // Normalization to prevent high-frequency boost
485
486 cachedNyquist_ = static_cast<T>(fs * 0.5);
487 cachedFsInvPi2_ = static_cast<T>(std::numbers::pi * 2.0 / fs);
488 }
489
493 [[nodiscard]] T computeEnvelopeFollower(T rawLevel) noexcept
494 {
495 if (rawLevel > envelopeState_)
496 envelopeState_ = rawLevel; // Instant attack for precise triggering
497 else
499
500 return envelopeState_;
501 }
502
503 void updateStateMachine(T envelopeLinear) noexcept
504 {
505 bool above = envelopeLinear > cachedThresholdLinear_;
506 bool below = envelopeLinear < cachedCloseThresholdLinear_;
507
508 if (cachedDuck_)
509 std::swap(above, below);
510
511 int effectiveHoldSamples = holdSamples_;
512 if (cachedAdaptiveHold_ && estimatedPeriod_ > effectiveHoldSamples)
513 effectiveHoldSamples = estimatedPeriod_;
514
515 switch (state_)
516 {
517 case State::Closed:
518 if (above) state_ = State::Open;
519 break;
520
521 case State::Open:
522 if (below)
523 {
525 holdCounter_ = effectiveHoldSamples;
526 }
527 break;
528
529 case State::Hold:
530 if (above)
531 {
533 }
534 else
535 {
536 --holdCounter_;
537 if (holdCounter_ <= 0)
539 }
540 break;
541 }
542 }
543
544 void updateZeroCrossing(T sample) noexcept
545 {
546 bool sign = sample >= T(0);
547 if (sign != prevSign_)
549 prevSign_ = sign;
550
553 {
554 if (zeroCrossCount_ > 0)
556 else
558
559 zeroCrossCount_ = 0;
561 }
562 }
563
564 [[nodiscard]] inline T getCurrentGain() noexcept
565 {
566 T targetGain = (state_ == State::Open || state_ == State::Hold) ? T(1) : cachedRangeLinear_;
567
568 // Fast path for established states to avoid unnecessary math
569 if (targetGain == gateGain_) return gateGain_;
570
571 T coeff = (targetGain > gateGain_) ? attackCoeff_ : releaseCoeff_;
572 gateGain_ += coeff * (targetGain - gateGain_);
573 return gateGain_;
574 }
575
576 [[nodiscard]] T applyFrequencyGate(T input, int ch) noexcept
577 {
578 T gateOpenness = gateGain_;
579
580 T targetLp = T(20) + (cachedNyquist_ - T(20)) * gateOpenness;
581 T targetHp = T(20) + (cachedNyquist_ * T(0.4)) * (T(1) - gateOpenness);
582
583 freqLpFreq_[ch] += freqSmoothCoeff_ * (targetLp - freqLpFreq_[ch]);
584 freqHpFreq_[ch] += freqSmoothCoeff_ * (targetHp - freqHpFreq_[ch]);
585
586 // Bilinear/Euler approximation to avoid std::exp() in the audio inner loop
587 T lpCoeff = std::min(T(1), freqLpFreq_[ch] * cachedFsInvPi2_);
588 freqLpState_[ch] += lpCoeff * (input - freqLpState_[ch]);
589
590 T hpCoeff = T(1) - std::min(T(1), freqHpFreq_[ch] * cachedFsInvPi2_);
591 T hpOut = hpCoeff * (freqHpState_[ch] + freqLpState_[ch] - freqHpPrev_[ch]);
592
593 freqHpPrev_[ch] = freqLpState_[ch];
594 freqHpState_[ch] = hpOut;
595
596 return hpOut;
597 }
598
600 [[nodiscard]] T applyScHpf(T input, int ch) noexcept
601 {
602 T output = scHpfA0_ * (input - scHpfPrev_[ch]) + scHpfCoeff_ * scHpfState_[ch];
603 scHpfPrev_[ch] = input;
604 scHpfState_[ch] = output;
605 return output;
606 }
607
608 [[nodiscard]] T processSampleInternal(T input, T sidechain, int ch) noexcept
609 {
611 sidechain = applyScHpf(sidechain, ch);
612
613 T rawLevel = std::abs(sidechain);
614
616 updateZeroCrossing(sidechain);
617
618 T envelope = computeEnvelopeFollower(rawLevel);
619 updateStateMachine(envelope);
620
622 {
623 (void)getCurrentGain();
624 return applyFrequencyGate(input, ch);
625 }
626
627 return input * getCurrentGain();
628 }
629
630 double sampleRate_ = 48000.0;
631
632 std::atomic<T> threshold_ { T(-40) };
633 std::atomic<T> hysteresis_ { T(4) };
634 std::atomic<T> attackMs_ { T(0.5) };
635 std::atomic<T> holdMs_ { T(50) };
636 std::atomic<T> releaseMs_ { T(100) };
637 std::atomic<T> rangeDb_ { T(-80) };
638 std::atomic<bool> duckMode_ { false };
639 std::atomic<GateMode> gateMode_ { GateMode::Amplitude };
640 std::atomic<bool> adaptiveHold_ { false };
641 std::atomic<bool> paramsDirty_ { true };
642
643 std::atomic<bool> scHpfEnabled_ { false };
644 std::atomic<T> scHpfFreq_ { T(80) };
645
646 // Cached internal variables
649 T cachedRangeLinear_ = T(0.0001);
650 bool cachedDuck_ = false;
654 T cachedNyquist_ = T(24000);
656
657 static constexpr int kMaxScChannels = 16;
658 T scHpfCoeff_ = T(0.995);
659 T scHpfA0_ = T(0.9975);
660 std::array<T, kMaxScChannels> scHpfState_ {};
661 std::array<T, kMaxScChannels> scHpfPrev_ {};
662
663 T attackCoeff_ = T(0);
666
667 // Fast Envelope Follower State
670
672 T gateGain_ = T(0);
674
676 std::array<T, kMaxChannels> freqLpState_ {};
677 std::array<T, kMaxChannels> freqHpState_ {};
678 std::array<T, kMaxChannels> freqHpPrev_ {};
679 std::array<T, kMaxChannels> freqLpFreq_ {};
680 std::array<T, kMaxChannels> freqHpFreq_ {};
681
682 static constexpr int kZeroCrossWindow = 2048;
685 bool prevSign_ = false;
687};
688
689} // namespace dspark
Non-owning view over audio channel data.
Definition AudioBuffer.h:50
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
High-performance noise gate with state machine, hysteresis, and zero-allocation processing.
Definition NoiseGate.h:78
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
Definition NoiseGate.h:414
void prepare(double sampleRate) noexcept
Prepares the noise gate for processing.
Definition NoiseGate.h:105
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an AudioBufferView in-place.
Definition NoiseGate.h:128
std::array< T, kMaxScChannels > scHpfPrev_
Definition NoiseGate.h:661
std::array< T, kMaxChannels > freqLpState_
Definition NoiseGate.h:676
T applyFrequencyGate(T input, int ch) noexcept
Definition NoiseGate.h:576
T processSample(T input) noexcept
Processes a single mono sample.
Definition NoiseGate.h:351
void setSidechainHPF(bool enabled, double cutoffHz=80.0) noexcept
Configures sidechain High-Pass filter.
Definition NoiseGate.h:330
void setRange(T dB) noexcept
Sets maximum attenuation range.
Definition NoiseGate.h:291
std::array< T, kMaxScChannels > scHpfState_
Definition NoiseGate.h:660
void processBlock(AudioBufferView< T > audio, AudioBufferView< T > sidechain) noexcept
Processes audio with an external sidechain signal.
Definition NoiseGate.h:185
T processSampleWithSidechain(T input, T sidechain) noexcept
Processes a mono sample using an external sidechain.
Definition NoiseGate.h:363
static constexpr int kZeroCrossWindow
Definition NoiseGate.h:682
void setAttack(T ms) noexcept
Sets attack time in milliseconds. Non-finite values are ignored.
Definition NoiseGate.h:264
void updateStateMachine(T envelopeLinear) noexcept
Definition NoiseGate.h:503
void setRelease(T ms) noexcept
Sets release time in milliseconds. Non-finite values are ignored.
Definition NoiseGate.h:280
void prepare(const AudioSpec &spec) noexcept
Prepares from AudioSpec (unified API).
Definition NoiseGate.h:117
static constexpr int kMaxChannels
Definition NoiseGate.h:433
std::atomic< T > threshold_
Definition NoiseGate.h:632
void setHold(T ms) noexcept
Sets hold time in milliseconds. Non-finite values are ignored.
Definition NoiseGate.h:272
std::array< T, kMaxChannels > freqLpFreq_
Definition NoiseGate.h:679
void setGateMode(GateMode mode) noexcept
Selects the processing mode (Amplitude or Frequency; wild enum values clamp).
Definition NoiseGate.h:306
void updateZeroCrossing(T sample) noexcept
Definition NoiseGate.h:544
GateMode
Operating mode for the gate processing.
Definition NoiseGate.h:92
@ Frequency
Gatelope-style: narrows bandpass dynamically instead of direct gain reduction.
@ Amplitude
Standard amplitude gain reduction (default).
~NoiseGate()=default
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
Definition NoiseGate.h:396
std::array< T, kMaxChannels > freqHpState_
Definition NoiseGate.h:677
void syncParamsIfDirty() noexcept
Definition NoiseGate.h:435
std::atomic< T > holdMs_
Definition NoiseGate.h:635
T processSampleInternal(T input, T sidechain, int ch) noexcept
Definition NoiseGate.h:608
std::atomic< T > releaseMs_
Definition NoiseGate.h:636
T getCurrentGain() noexcept
Definition NoiseGate.h:564
std::atomic< GateMode > gateMode_
Definition NoiseGate.h:639
std::array< T, kMaxChannels > freqHpFreq_
Definition NoiseGate.h:680
State
Gate state machine phases.
Definition NoiseGate.h:84
@ 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_
Definition NoiseGate.h:637
GateMode cachedGateMode_
Definition NoiseGate.h:651
std::atomic< T > attackMs_
Definition NoiseGate.h:634
T computeEnvelopeFollower(T rawLevel) noexcept
Updates envelope state to prevent intra-cycle chattering.
Definition NoiseGate.h:493
static constexpr int kMaxScChannels
Definition NoiseGate.h:657
std::atomic< T > hysteresis_
Definition NoiseGate.h:633
void setHysteresis(T dB) noexcept
Sets the hysteresis amount (gap between open and close thresholds).
Definition NoiseGate.h:256
T applyScHpf(T input, int ch) noexcept
Per-channel one-pole sidechain high-pass (unity gain at Nyquist).
Definition NoiseGate.h:600
void setThreshold(T dB) noexcept
Sets the opening threshold.
Definition NoiseGate.h:245
std::atomic< T > scHpfFreq_
Definition NoiseGate.h:644
void setDuckMode(bool enabled) noexcept
Toggles ducking mode (invert gate logic).
Definition NoiseGate.h:299
std::array< T, kMaxChannels > freqHpPrev_
Definition NoiseGate.h:678
void reset() noexcept
Resets DSP state (clears filters, sets state to Closed).
Definition NoiseGate.h:372
void setAdaptiveHold(bool enabled) noexcept
Toggles adaptive hold based on zero-crossing rate.
Definition NoiseGate.h:314
std::atomic< bool > paramsDirty_
Definition NoiseGate.h:641
std::atomic< bool > duckMode_
Definition NoiseGate.h:638
void syncParams() noexcept
Definition NoiseGate.h:444
std::atomic< bool > adaptiveHold_
Definition NoiseGate.h:640
std::atomic< bool > scHpfEnabled_
Definition NoiseGate.h:643
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
Definition StateBlob.h:160
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
Definition StateBlob.h:203
bool isValid() const noexcept
Definition StateBlob.h:198
uint32_t processorId() const noexcept
Definition StateBlob.h:199
Serializes key/value parameters into a versioned blob.
Definition StateBlob.h:52
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
Definition StateBlob.h:104
void write(const char *key, float value)
Writes a float parameter.
Definition StateBlob.h:70
Main namespace for the DSPark framework.
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
Definition DspMath.h:65
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
Definition StateBlob.h:639
Describes the audio environment for a DSP processor.
Definition AudioSpec.h:35