46#include "../Core/DspMath.h"
47#include "../Core/AudioSpec.h"
48#include "../Core/AudioBuffer.h"
49#include "../Core/SmoothedValue.h"
50#include "../Core/RingBuffer.h"
51#include "../Core/DenormalGuard.h"
52#include "../Core/Hilbert.h"
53#include "../Core/TruePeakDetector.h"
54#include "../Core/StateBlob.h"
208 T thresh =
threshold_.load(std::memory_order_relaxed);
209 T rat =
ratio_.load(std::memory_order_relaxed);
210 T knee =
kneeWidth_.load(std::memory_order_relaxed);
229 int maxLaSamples =
static_cast<int>(
sampleRate_ * 0.01) + 1;
237 lookaheadMs_.load(std::memory_order_relaxed), T(0), T(10)) / T(1000));
242 int maxRmsSamples =
static_cast<int>(
sampleRate_ * 0.5) + 1;
245 buf.assign(
static_cast<size_t>(maxRmsSamples), T(0));
314 if (channel < 0 || channel >=
kMaxChannels)
return input;
328 const bool advanceShared = (channel == 0);
329 T thresh, ratio, knee, makeupDb, colorAmt;
347 auto detType =
detectorType_.load(std::memory_order_relaxed);
348 auto topo =
topology_.load(std::memory_order_relaxed);
349 auto charType =
character_.load(std::memory_order_relaxed);
350 auto modeType =
mode_.load(std::memory_order_relaxed);
386 T levelDb =
detectLevel(detectorIn, channel, detTypeEff);
403 thresh, ratio, knee);
409 smoothedGR_Db =
applyBallistics(targetGR_Db, channel, splitAdaptive, law.slope);
413 T targetGR_Db =
computeGain(levelDb, thresh, ratio, knee, charType, modeType, guardDb);
428 makeup +=
computeGain(T(0), thresh, ratio, knee, charType,
432 T output = input * smoothedGainLinear;
436 const T sq = output * output;
439 const T grDepth = std::clamp(-smoothedGR_Db * T(0.1), T(0), T(1));
440 output += colorAmt *
kFetColorH2 * grDepth * (sq - dc);
468 std::fill(buf.begin(), buf.end(), T(0));
469 for (
auto& sum :
rmsSums_) sum = T(0);
500 if (!std::isfinite(dB))
return;
501 threshold_.store(dB, std::memory_order_relaxed);
507 if (!std::isfinite(ratio))
return;
508 ratio_.store(std::max(ratio, T(1)), std::memory_order_relaxed);
526 if (!std::isfinite(ms))
return;
527 attackMs_.store(std::max(ms, T(0.01)), std::memory_order_relaxed);
542 if (!std::isfinite(ms))
return;
543 releaseMs_.store(std::max(ms, T(1)), std::memory_order_relaxed);
550 if (!std::isfinite(dB))
return;
551 kneeWidth_.store(std::max(dB, T(0)), std::memory_order_relaxed);
557 if (!std::isfinite(dB))
return;
574 std::memory_order_relaxed);
594 if (!std::isfinite(amount))
return;
595 stereoLink_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
601 if (!std::isfinite(dryWet))
return;
602 mix_.store(std::clamp(dryWet, T(0), T(1)), std::memory_order_relaxed);
611 if (!std::isfinite(ms))
return;
612 lookaheadMs_.store(std::clamp(ms, T(0), T(10)), std::memory_order_relaxed);
634 if (!std::isfinite(ms))
return;
635 holdMs_.store(std::clamp(ms, T(0), T(500)), std::memory_order_relaxed);
649 if (!std::isfinite(dB))
return;
650 rangeDb_.store(std::max(dB, T(0)), std::memory_order_relaxed);
684 if (!std::isfinite(amount))
return;
685 characterColor_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
707 if (std::isfinite(cutoffHz) && cutoffHz > T(0))
708 scHpfFreq_.store(cutoffHz, std::memory_order_relaxed);
722 if (!std::isfinite(ms))
return;
753 const bool feedback =
756 if (feedback)
return 0;
760 const int lookNow =
static_cast<int>(
static_cast<T
>(
sampleRate_) * std::clamp(
761 lookaheadMs_.load(std::memory_order_relaxed), T(0), T(10)) / T(1000));
767 [[nodiscard]] std::vector<uint8_t>
getState()
const
772 w.
write(
"threshold",
static_cast<float>(
threshold_.load(std::memory_order_relaxed)));
773 w.
write(
"ratio",
static_cast<float>(
ratio_.load(std::memory_order_relaxed)));
774 w.
write(
"attack",
static_cast<float>(
attackMs_.load(std::memory_order_relaxed)));
775 w.
write(
"release",
static_cast<float>(
releaseMs_.load(std::memory_order_relaxed)));
776 w.
write(
"knee",
static_cast<float>(
kneeWidth_.load(std::memory_order_relaxed)));
777 w.
write(
"makeup",
static_cast<float>(
makeupGain_.load(std::memory_order_relaxed)));
780 w.
write(
"autoMakeupMode",
static_cast<int32_t
>(amMode));
781 w.
write(
"stereoLink",
static_cast<float>(
stereoLink_.load(std::memory_order_relaxed)));
782 w.
write(
"mix",
static_cast<float>(
mix_.load(std::memory_order_relaxed)));
783 w.
write(
"lookahead",
static_cast<float>(
lookaheadMs_.load(std::memory_order_relaxed)));
784 w.
write(
"hold",
static_cast<float>(
holdMs_.load(std::memory_order_relaxed)));
785 w.
write(
"range",
static_cast<float>(
rangeDb_.load(std::memory_order_relaxed)));
786 w.
write(
"detector",
static_cast<int32_t
>(
detectorType_.load(std::memory_order_relaxed)));
787 w.
write(
"topology",
static_cast<int32_t
>(
topology_.load(std::memory_order_relaxed)));
788 w.
write(
"character",
static_cast<int32_t
>(
character_.load(std::memory_order_relaxed)));
790 w.
write(
"mode",
static_cast<int32_t
>(
mode_.load(std::memory_order_relaxed)));
792 w.
write(
"scHpfFreq",
static_cast<float>(
scHpfFreq_.load(std::memory_order_relaxed)));
809 const int amLegacy = r.
read(
"autoMakeup",
false) ? 2 : 0;
811 std::clamp(r.
read(
"autoMakeupMode", amLegacy), 0, 2)));
838 template <
typename E>
839 [[nodiscard]]
static E
clampEnum(E value, E last)
noexcept
841 const int v = std::clamp(
static_cast<int>(value), 0,
static_cast<int>(last));
842 return static_cast<E
>(v);
859 const int nCh = std::min(audio.getNumChannels(),
kMaxChannels);
860 const int nS = audio.getNumSamples();
863 const int scCh = (sidechain.getNumSamples() >= nS)
864 ? sidechain.getNumChannels() : 0;
886 auto detType =
detectorType_.load(std::memory_order_relaxed);
887 auto topo =
topology_.load(std::memory_order_relaxed);
888 auto charType =
character_.load(std::memory_order_relaxed);
889 auto modeType =
mode_.load(std::memory_order_relaxed);
892 T sLink =
stereoLink_.load(std::memory_order_relaxed);
915 for (
int i = 0; i < nS; ++i)
923 T linkedLevel = T(-200);
928 mkupGain +=
computeGain(T(0), thresh, ratio, knee, charType,
932 for (
int ch = 0; ch < nCh; ++ch)
935 T sample = (scCh > 0) ? sidechain.getChannel(std::min(ch, scCh - 1))[i]
936 : audio.getChannel(ch)[i];
945 detectorIn = fbImplicit ? audio.getChannel(ch)[i] :
fbLastOutput_[ch];
950 T levelDb =
detectLevel(detectorIn, ch, detTypeEff);
953 if (levelDb > linkedLevel) linkedLevel = levelDb;
958 T makeupDb = mkupGain;
965 for (
int ch = 0; ch < nCh; ++ch)
968 T inputDb = chLevel + sLink * (linkedLevel - chLevel);
985 thresh, ratio, knee);
993 smoothedGR_Db =
applyBallistics(targetGR_Db, ch, splitAdaptive, law.slope);
997 T targetGR_Db =
computeGain(inputDb, thresh, ratio, knee, charType, modeType, guardDb);
1002 T smoothedGainLinear = (smoothedGR_Db == T(0)) ? T(1) :
decibelsToGain(smoothedGR_Db);
1005 if (activeLookahead > 0)
1012 input = audio.getChannel(ch)[i];
1015 T wet = input * smoothedGainLinear;
1024 const T sq = wet * wet;
1027 const T grDepth = std::clamp(-smoothedGR_Db * T(0.1), T(0), T(1));
1028 wet += colorAmt *
kFetColorH2 * grDepth * (sq - dc);
1037 audio.getChannel(ch)[i] = (mixVal < T(1))
1038 ? (input * (T(1) - mixVal) + wet * mixVal)
1041 if (ch == 0 || smoothedGR_Db < blockGR)
1042 blockGR = smoothedGR_Db;
1058 T attMs = std::max(
attackMs_.load(std::memory_order_relaxed), T(0.01));
1059 T relMs = std::max(
releaseMs_.load(std::memory_order_relaxed), T(1));
1063 auto tc = [fs](T ms) {
return std::exp(T(-1) / (fs * ms / T(1000))); };
1069 switch (
character_.load(std::memory_order_relaxed))
1103 const T rel = std::clamp(relMs, T(50), T(1100));
1121 const auto charNow =
character_.load(std::memory_order_relaxed);
1128 * (std::max(
ratio_.load(std::memory_order_relaxed), T(1)) - T(1));
1130 charAttCoeff_ = T(1) - (T(1) - rho) / (T(1) + rho * loopGain);
1144 fetDcCoeff_ = T(1) - std::exp(T(-2) * std::numbers::pi_v<T> * T(10) / fs);
1147 lookaheadMs_.load(std::memory_order_relaxed), T(0), T(10)) / T(1000));
1160 const T range =
rangeDb_.load(std::memory_order_relaxed);
1161 targetGR_Db = std::clamp(targetGR_Db, -range, range);
1167 if (std::abs(targetGR_Db) >= std::abs(held))
1172 else if (counter > 0)
1213 T level = std::abs(sample);
1221 T sq = sample * sample;
1228 if (len > 0 && len <=
static_cast<int>(buf.size()))
1233 if (++idx >= len) idx = 0;
1239 for (
int j = 0; j < len; ++j) sum += buf[j];
1243 level = std::sqrt(std::max(sum /
static_cast<T
>(len), T(0)));
1254 T pos = std::max(sample, T(0));
1255 T neg = std::max(-sample, T(0));
1274 const double mag =
hilbertDetectors_[ch].magnitude(
static_cast<double>(sample));
1275 level =
static_cast<T
>(std::max(mag, std::abs(
static_cast<double>(sample))));
1295 return ratio * (T(1) + excessDb / T(40));
1306 ? std::max(knee, T(10)) : knee;
1321 T guardDb)
const noexcept
1329 if (effectiveKnee <= T(0))
1332 if (inputDb <= thresh)
return T(0);
1333 return (thresh - inputDb) * (T(1) - T(1) / effectiveRatio);
1338 T halfKnee = effectiveKnee / T(2);
1339 T lower = thresh - halfKnee;
1340 T upper = thresh + halfKnee;
1342 if (inputDb <= lower)
return T(0);
1343 if (inputDb >= upper)
return (thresh - inputDb) * (T(1) - T(1) / effectiveRatio);
1345 T x = inputDb - lower;
1346 return (T(1) - T(1) / effectiveRatio) * x * x / (T(2) * effectiveKnee) * T(-1);
1362 T effectiveRatio = ratio;
1366 if (inputDb >= thresh)
return T(0);
1367 boost = (thresh - inputDb) * (T(1) - T(1) / effectiveRatio);
1371 T halfKnee = knee / T(2);
1372 T lower = thresh - halfKnee;
1373 T upper = thresh + halfKnee;
1375 if (inputDb >= upper)
return T(0);
1376 if (inputDb <= lower)
1378 boost = (thresh - inputDb) * (T(1) - T(1) / effectiveRatio);
1382 T x = upper - inputDb;
1383 boost = (T(1) - T(1) / effectiveRatio) * x * x / (T(2) * knee);
1387 const T silenceGuard = std::clamp((guardDb - (thresh - T(60))) / T(20), T(0), T(1));
1388 return boost * silenceGuard;
1418 const T a = r - T(1);
1421 if (eOut <= T(0))
return { T(0), T(0) };
1422 return { -a * eOut, a };
1424 const T halfW = w / T(2);
1425 if (eOut <= -halfW)
return { T(0), T(0) };
1426 if (eOut >= halfW / r)
return { -a * eOut, a };
1431 const T b = eOut + halfW;
1432 const T z = std::min(T(2) * s * b / w, T(1));
1433 const T u = std::min(T(2) * b / (T(1) + std::sqrt(T(1) - z)), w);
1434 const T su = s * u / w;
1435 return { -s * u * u / (T(2) * w), su / std::max(T(1) - su, T(1) / r) };
1455 const T eOut = outDb - thresh;
1461 for (
int pass = 0; pass < 2; ++pass)
1499 T ratio, T knee,
Character charType)
const noexcept
1503 const T halfW = w / T(2);
1504 const T a = r - T(1);
1507 T e = inDb + k - thresh;
1508 if (e <= ((w > T(0)) ? -halfW : T(0)))
1513 const T g3 = (k - effB * a * (inDb - thresh)) / (T(1) + effB * a);
1514 e = inDb + g3 - thresh;
1515 if (e >= ((w > T(0)) ? halfW / r : T(0)))
1516 return { g3, -a * e };
1522 const T q = (T(1) - effB) * s / (T(2) * w);
1523 const T b = (inDb - thresh + k) + halfW;
1524 const T disc = std::max(T(1) - T(4) * q * b, T(0));
1525 T u = T(2) * b / (T(1) + std::sqrt(disc));
1526 u = std::clamp(u, T(0), w);
1527 const T target = -s * u * u / (T(2) * w);
1528 return { k + effB * target, target };
1547 T thresh, T ratio, T knee)
noexcept
1552 const bool dual = wFast < T(1);
1563 const T range =
rangeDb_.load(std::memory_order_relaxed);
1564 const T slowTarget = std::clamp(
1567 const bool charging = slowTarget < slow;
1569 if (std::abs(slow) < T(1e-4)) slow = T(0);
1574 const T shownPrev = dual ? wFast * fast + (T(1) - wFast) * slow : fast;
1576 const bool engaging = probe < fast;
1578 const T beta = T(1) - coeff;
1580 const T k = dual ? wFast * coeff * fast + (T(1) - wFast) * slow : coeff * fast;
1581 const T effB = dual ? wFast * beta : beta;
1582 const auto sol =
solveFeedbackGain(inDb, k, effB, thresh, ratio, knee, charType);
1584 fast = sol.target + coeff * (fast - sol.target);
1585 if (std::abs(fast) < T(1e-4)) fast = T(0);
1586 T shown = dual ? wFast * fast + (T(1) - wFast) * slow : fast;
1594 if (bounded != shown)
1597 fast = dual ? (shown - (T(1) - wFast) * slow) / wFast : shown;
1627 T loopSlope = T(0)) noexcept
1637 const T outputLevel = std::min(std::abs(
fbLastOutput_[ch]), T(1));
1638 relCoeff += outputLevel * (relCoeff * relCoeff - relCoeff);
1640 if (loopSlope > T(0))
1645 const T floorCoeff = T(1) - T(0.5) / (T(1) + loopSlope);
1646 attCoeff = std::max(attCoeff, floorCoeff);
1647 relCoeff = std::max(relCoeff, floorCoeff);
1655 const bool engaging = targetGrDb < fast;
1656 fast = targetGrDb + (engaging ? attCoeff : relCoeff) * (fast - targetGrDb);
1657 if (std::abs(fast) < T(1e-4)) fast = T(0);
1666 const bool charging = targetGrDb < slow;
1668 if (std::abs(slow) < T(1e-4)) slow = T(0);
1680 const double scFreq = std::clamp(
1681 static_cast<double>(
scHpfFreq_.load(std::memory_order_relaxed)),
1718 int maxLen =
static_cast<int>(
rmsBuffers_[0].size());
Non-owning view over audio channel data.
High-fidelity modular compressor designed for real-time applications.
DetectorType
Level detection methodologies.
@ SplitPolarity
Separate envelopes for the positive and negative half-waves.
@ TruePeak
4x oversampled peak detection (ITU-R BS.1770-4 compliant).
@ Rms
Sliding-window Root-Mean-Square. Smoother, responds to average energy.
@ Peak
Instantaneous absolute value tracking. Fast and standard.
static constexpr int kMaxChannels
T charChargeCoeff_
History charge coefficient of the memory stage.
void setThreshold(T dB) noexcept
Sets the compression threshold in dB.
void setHoldTime(T ms) noexcept
Sets the gain-reduction hold time.
std::array< RingBuffer< T >, kMaxChannels > lookaheadBuffers_
Lookahead delay lines.
void prepare(const AudioSpec &spec)
Allocates buffers and initializes internal DSP state.
std::array< T, kMaxChannels > heldGrDb_
std::atomic< bool > scHpfEnabled_
HPF toggle.
std::atomic< AutoMakeupMode > autoMakeupMode_
Auto-makeup behavior.
SmoothedValue< T > makeupSmooth_
De-zippered makeup gain (dB).
std::atomic< T > releaseMs_
Release time in milliseconds.
FeedbackSolve solveFeedbackGain(T inDb, T k, T effB, T thresh, T ratio, T knee, Character charType) const noexcept
Solves one feedback ballistics step against the current input.
std::array< T, kMaxChannels > fetDcState_
Per-channel DC estimate of wet^2.
std::atomic< T > stereoLink_
Stereo linking amount (0 to 1).
T charRelCoeff_
Release coefficient (fast stage).
std::atomic< T > gainReductionDb_
Publicly readable Gain Reduction meter.
T getGainReductionDb() const noexcept
Returns current active gain reduction in dB (negative value).
static E clampEnum(E value, E last) noexcept
Clamps an enum to its valid [first, last] range.
std::array< int, kMaxChannels > holdCounters_
Character getCharacter() const noexcept
Returns the currently active character.
T computeGainUpward(T inputDb, T thresh, T ratio, T knee, T guardDb) const noexcept
Upward compression curve calculation.
SmoothedValue< T > kneeSmooth_
De-zippered knee.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
std::array< T, kMaxChannels > splitPosEnv_
Positive half-wave tracking.
std::atomic< bool > rmsWindowDirty_
Applied at the next block.
Character
Time-constant behavior and release curve shape.
void setRelease(T ms) noexcept
Sets the release time in milliseconds (clamped to >= 1 ms: below that the envelope stops smoothing at...
T detectLevel(T sample, int ch, DetectorType detType) noexcept
Computes level detection in Decibels.
T scHpfB1_
HPF internal feedback coefficient.
T charAttCoeff_
Attack coefficient of the fast envelope.
std::array< int, kMaxChannels > rmsIndices_
Write heads for RMS buffers.
T applySidechainHPF(T input, int ch) noexcept
Applies sidechain filtering for a specific channel.
void updateHpfCoefficients() noexcept
Updates the normalized DC-blocker / High-pass filter coefficients.
void setTopology(Topology topo) noexcept
Changes signal routing topology (FeedForward or FeedBack).
void setMakeupGain(T dB) noexcept
Sets manual static makeup gain in dB.
std::atomic< Character > character_
Selected ballistics.
T getCharacterColor() const noexcept
Returns the character colour amount (see setCharacterColor).
void setAutoMakeup(AutoMakeupMode mode) noexcept
Selects the automatic makeup behavior (default: Off).
std::array< std::vector< T >, kMaxChannels > rmsBuffers_
Pre-allocated RMS sliding windows.
SmoothedValue< T > thresholdSmooth_
De-zippered threshold.
T applyHoldAndRange(T targetGR_Db, int ch) noexcept
Applies the hold and range stages to the static gain target (dB).
T cachedMakeupLin_
Its linear gain.
SmoothedValue< T > mixSmooth_
De-zippered parallel mix.
void setLookahead(T ms) noexcept
Sets lookahead time in ms (0 = off, max = 10ms).
std::atomic< T > scHpfFreq_
HPF Cutoff frequency.
T computeGain(T inputDb, T thresh, T ratio, T knee, Character charType, Mode modeType, T guardDb) const noexcept
Calculates static target gain reduction based on knee and ratio.
T autoMakeupEnv_
Smoothed internal auto-makeup envelope.
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
T autoMakeupCoeff_
Auto-makeup tracking factor.
std::array< T, kMaxChannels > envSlowDb_
Slow memory envelope per channel (dB).
std::array< T, kMaxChannels > scHpfState_
HPF y[n-1] state.
void setDetector(DetectorType type) noexcept
Changes the level detection algorithm (Peak, RMS, TruePeak, Hilbert).
std::atomic< bool > timeConstantsDirty_
Coefficients need a recompute.
std::atomic< T > mix_
Wet/Dry mix (1 = full wet).
SmoothedValue< T > ratioSmooth_
De-zippered ratio.
void setAutoMakeup(bool on) noexcept
Convenience overload: true selects Adaptive, false turns auto makeup off.
static T effectiveRatioFor(Character charType, T ratio, T excessDb) noexcept
Level-dependent effective ratio of the character (Varimu grows).
T processSample(T input, int channel) noexcept
Processes a single sample on one channel.
Topology
Signal routing topology for the detector sidechain.
@ FeedForward
Detector reads uncompressed input (modern, precise, transparent).
void setRmsWindow(T ms) noexcept
Sets the RMS analysis window size in milliseconds.
std::array< T, kMaxChannels > splitNegEnv_
Negative half-wave tracking.
T makeupToGain(T makeupDb) noexcept
Makeup dB to linear, cached: the makeup is shared by every channel of a frame and constant unless aut...
static constexpr int kRmsRecomputePeriod
Re-summation interval to halt drift.
std::array< T, kMaxChannels > upwardGuardDb_
Peak-held sustained level (Upward guard).
T charFastWeight_
Fast/slow blend (1 = single envelope).
T charSlowRelCoeff_
Release coefficient of the memory stage.
void setMix(T dryWet) noexcept
Sets dry/wet balance for parallel (New York) compression (1.0 = fully wet).
void reset() noexcept
Resets all internal DSP history, states, and buffers to neutral.
void setRatio(T ratio) noexcept
Sets the compression ratio (1.0 = off, >20.0 = limiting).
T applyBallistics(T targetGrDb, int ch, bool splitAdaptive, T loopSlope=T(0)) noexcept
Smooths the static gain target with the active character's ballistics.
std::array< T, kMaxChannels > envFastDb_
Fast gain envelope per channel (dB).
void setSidechainHPF(bool enabled, T cutoffHz=T(80)) noexcept
Toggles the internal sidechain high-pass filter.
T splitDetAttCoeff_
Detector attack coefficient (sample-rate derived).
std::atomic< DetectorType > detectorType_
Selected detection method.
void setKnee(T dB) noexcept
Sets knee width in dB (0 = hard knee, >0 = soft knee).
double sampleRate_
Cached operating sample rate.
FeedbackLaw computeGainFeedback(T outDb, T thresh, T ratio, T knee, Character charType) const noexcept
Feedback target for detectors with memory (explicit path).
void setCharacterColor(T amount) noexcept
Sets the amount of the character's harmonic signature (0 to 1).
std::array< T, kMaxChannels > rmsSums_
Running sums for RMS.
std::atomic< T > lookaheadMs_
Lookahead latency target in ms.
void setCharacter(Character type) noexcept
Changes ballistics and envelope behavior character.
void prepare(double sampleRate) noexcept
Prepares the compressor using sample rate only (backward compatibility).
void processBlockImpl(AudioBufferView< T > audio, AudioBufferView< T > sidechain) noexcept
Core DSP loop executing sidechain detection, linking, ballistics, and gain application.
static constexpr T kFetColorH2
void setRange(T dB) noexcept
Limits the maximum gain change the compressor may apply.
std::array< HilbertIIR< double >, kMaxChannels > hilbertDetectors_
Zero-latency analytic pairs for detection.
std::atomic< T > rangeDb_
Max |gain change| in dB.
std::array< T, kMaxChannels > fbLastOutput_
Feedback topology history buffer.
Topology getTopology() const noexcept
Returns the currently active topology.
T cachedMakeupDb_
Last makeup converted by makeupToGain().
std::atomic< T > attackMs_
Attack time in milliseconds.
static T effectiveKneeFor(Character charType, T knee) noexcept
Physical knee floor of the character (dB).
T upwardGuardDecay_
Guard decay per sample (60 dB/s).
T fetDcCoeff_
~10 Hz DC-tracking coefficient.
AutoMakeupMode
Automatic makeup-gain behavior (applies in Downward mode).
@ Off
Manual makeup only (setMakeupGain).
int getLatency() const noexcept
Returns total processing latency in samples.
DetectorType getDetector() const noexcept
Returns the currently active detector type.
std::atomic< T > rmsWindowMsAtomic_
Control-thread published target.
T scHpfA0_
HPF normalized feedforward coefficient.
void updateRmsWindow() noexcept
Safely recalculates the RMS window size ensuring zero heap allocations.
AudioSpec spec_
Active audio environment specification.
std::atomic< Topology > topology_
Selected topology.
T applyBallisticsFeedbackImplicit(T inDb, int ch, Character charType, T thresh, T ratio, T knee) noexcept
Feedback ballistics with the loop resolved semi-implicitly.
std::array< T, kMaxChannels > channelLevelDb_
Raw detected level buffer.
static FeedbackLaw elementLaw(T eOut, T r, T w) noexcept
Downward element law for feedback detection, per output level.
std::atomic< Mode > mode_
Selected compression mode.
std::array< int, kMaxChannels > rmsRecomputeCounters_
Re-summation counters.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an audio buffer in-place using its own signal as the sidechain.
std::atomic< T > kneeWidth_
Knee width in dB.
void setMode(Mode mode) noexcept
Sets processing mode (Downward or Upward compression).
int lookaheadSamples_
Active lookahead latency in samples.
TruePeakDetector< T, kMaxChannels > truePeak_
std::atomic< T > threshold_
Threshold in dB.
void processBlock(AudioBufferView< T > audio, AudioBufferView< T > sidechain) noexcept
Processes audio with an independent external sidechain.
void updateTimeConstants(T fs) noexcept
Pre-calculates recursive exponential decay coefficients.
T rmsWindowMs_
Active RMS length in ms (audio-thread copy).
void setStereoLink(T amount) noexcept
Sets stereo linking amount (0.0 = unlinked dual mono, 1.0 = fully linked).
std::atomic< T > makeupGain_
Static makeup gain in dB.
T splitDetRelCoeff_
Detector release coefficient (sample-rate derived).
std::atomic< T > holdMs_
Gain-hold time in ms (0 = off).
void setAttack(T ms) noexcept
Sets the attack time in milliseconds.
int rmsWindowSamples_
Active RMS length in samples.
SmoothedValue< T > colorSmooth_
De-zippered character colour amount.
Mode
Processing direction.
@ Downward
Standard: Reduces dynamic range by attenuating signals above the threshold.
std::atomic< T > characterColor_
Character harmonic amount (0 to 1).
std::atomic< T > ratio_
Ratio (e.g., 4 = 4:1).
std::array< T, kMaxChannels > scHpfPrev_
HPF x[n-1] state.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
90-degree phase-differencing network (analytic-signal generator).
Zero-allocation parameter smoother for real-time audio.
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.
Per-channel 4x-oversampled inter-sample peak estimator.
T processSample(T sample, int channel) noexcept
Feeds one sample and returns the local true-peak estimate.
void reset() noexcept
Clears all channel histories. Safe on the audio thread.
Main namespace for the DSPark framework.
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
T gainToDecibels(T gain, T minusInfinityDb=T(-100)) noexcept
Converts a linear gain value to decibels.
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.
Element law result: static target (dB, <= 0) and its |slope|.
T slope
d|target| / d(level in dB): the incremental loop gain.
T target
Gain change the element commands at this output level.
Semi-implicit solve result: stepped gain and the law's target.
T target
The element law's static target at the solved level.
T gain
Blended gain (dB) after this sample's ballistics step.