45#include "../Core/DspMath.h"
46#include "../Core/AudioSpec.h"
47#include "../Core/AudioBuffer.h"
48#include "../Core/RingBuffer.h"
49#include "../Core/SmoothedValue.h"
50#include "../Core/DenormalGuard.h"
51#include "../Core/TruePeakDetector.h"
52#include "../Core/StateBlob.h"
93 void prepare(
double sampleRate,
int numChannels = 2,
double initialLookaheadMs = -1.0)
95 if (!(sampleRate > 0.0))
return;
110 const int maxLookaheadSamples =
static_cast<int>(std::min(maxLaExact, 1.0e7)) + 1;
117 dl.prepare(maxLookaheadSamples * 2);
119 if (std::isfinite(initialLookaheadMs) && initialLookaheadMs > 0.0)
120 lookaheadMs_.store(std::clamp(
static_cast<T
>(initialLookaheadMs),
122 std::memory_order_relaxed);
131 const T ceilDb =
ceilingDb_.load(std::memory_order_relaxed);
172 const int nCh = std::min(buffer.getNumChannels(),
numChannels_);
173 const int nS = buffer.getNumSamples();
182 const T relMs = std::max(
releaseMs_.load(std::memory_order_relaxed), T(1));
186 for (
int ch = 0; ch < nCh; ++ch)
187 chData[ch] = buffer.getChannel(ch);
189 for (
int i = 0; i < nS; ++i)
202 for (
int ch = 0; ch < nCh; ++ch)
204 T sample = chData[ch][i];
208 if (chPeak > peak) peak = chPeak;
226 for (
int ch = 0; ch < nCh; ++ch)
235 out = std::clamp(out, -ceiling, ceiling);
240 if (std::abs(out) > clipCeil)
267 if (channel < 0 || channel >=
numChannels_)
return input;
293 else if (channel == 0)
301 const T relMs = std::max(
releaseMs_.load(std::memory_order_relaxed), T(1));
307 out = std::clamp(out, -ceiling, ceiling);
312 if (std::abs(out) > clipCeil)
342 if (!std::isfinite(dB))
return;
343 ceilingDb_.store(dB, std::memory_order_relaxed);
355 if (!std::isfinite(ms))
return;
356 releaseMs_.store(std::max(T(1), ms), std::memory_order_relaxed);
371 if (!std::isfinite(ms))
return;
373 std::memory_order_relaxed);
417 [[nodiscard]] std::vector<uint8_t>
getState()
const
420 w.
write(
"ceiling",
static_cast<float>(
ceilingDb_.load(std::memory_order_relaxed)));
421 w.
write(
"release",
static_cast<float>(
releaseMs_.load(std::memory_order_relaxed)));
422 w.
write(
"lookahead",
static_cast<float>(
lookaheadMs_.load(std::memory_order_relaxed)));
456 const double clampedMs = std::clamp(
static_cast<double>(ms), 0.5,
kMaxLookaheadMs);
457 const double exact =
sampleRate_ * clampedMs / 1000.0;
458 return std::max(1,
static_cast<int>(std::min(exact, 1.0e7)));
476 const T ceilDb =
ceilingDb_.load(std::memory_order_relaxed);
483 const T relMs = std::max(
releaseMs_.load(std::memory_order_relaxed), T(1));
497 inline void smoothGain(T targetGain,
bool adaptive, T relMs)
noexcept
516 baseFactor = T(1) + std::min(durationMs / T(100), T(2));
518 T adaptedRelease = relMs * baseFactor;
522 coeff = T(1) / (T(1) + (
static_cast<T
>(
sampleRate_) * adaptedRelease / T(1000)));
539 T sign = (out >= T(0)) ? T(1) : T(-1);
540 T excess = std::abs(out) - clipCeil;
541 T blend = T(1) / (T(1) + excess * T(10));
542 out = sign * (clipCeil * blend + std::abs(out) * (T(1) - blend));
544 const T hardCeil = clipCeil * T(1.05);
545 if (std::abs(out) > hardCeil)
546 out = std::clamp(out, -hardCeil, hardCeil);
Non-owning view over audio channel data.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
High-performance brickwall lookahead limiter.
void setLookahead(T ms) noexcept
Sets the lookahead time dynamically.
void prepare(double sampleRate, int numChannels=2, double initialLookaheadMs=-1.0)
Allocates memory and prepares the limiter for processing.
void setRelease(T ms) noexcept
Sets the base release time.
bool isAdaptiveReleaseEnabled() const noexcept
std::atomic< T > lookaheadMs_
T processSample(T input, int channel) noexcept
Processes a single sample of one channel.
T getGainReductionDb() const noexcept
Current gain reduction in dB (metering; unsynchronized read).
T lookaheadCurrent_
Smoothed read offset (glides on live changes).
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void prepare(const AudioSpec &spec)
Prepares from AudioSpec (unified API).
int maxLimitSamples_
Adaptive-release duration cap (2 s).
void setSafetyClip(bool enabled) noexcept
Enables the post-limiter soft-knee safety clipper. RT-Safe.
std::atomic< bool > lookaheadDirty_
void syncParameters() noexcept
std::atomic< bool > truePeakEnabled_
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
int getLatency() const noexcept
Reports the processing latency (the lookahead) in samples.
void smoothGain(T targetGain, bool adaptive, T relMs) noexcept
int lookaheadSamplesFor(T ms) const noexcept
std::atomic< bool > safetyClipEnabled_
void reset() noexcept
Resets the internal state (delays, gain reduction). RT-Safe.
void updateReleaseCoefficient() noexcept
SmoothedValue< T > ceilingSmooth_
int lookaheadSamples_
Audio-side lookahead (consumed from lookaheadMs_).
T applySafetyClipper(T out, T clipCeil) const noexcept
void applyLookaheadTarget() noexcept
void setTruePeak(bool enabled) noexcept
Enables 4x oversampled ISP true-peak detection. RT-Safe.
bool isSafetyClipEnabled() const noexcept
std::vector< RingBuffer< T > > delayLines_
std::atomic< T > releaseMs_
void setAdaptiveRelease(bool enabled) noexcept
Enables program-dependent adaptive release. RT-Safe.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an AudioBufferView in-place.
T getLookahead() const noexcept
std::atomic< bool > adaptiveRelease_
bool isTruePeakEnabled() const noexcept
static constexpr T kSafetyClipCeiling
-0.3 dBFS
static constexpr double kMaxLookaheadMs
static constexpr int kMaxChannels
std::atomic< T > ceilingDb_
void setCeiling(T dB) noexcept
Sets the absolute output ceiling.
T lastCeilingDb_
Change detector for the ceiling pow skip.
TruePeakDetector< T, kMaxChannels > truePeak_
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.
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).
double sampleRate
Sample rate in Hz.