31#include "../Core/AudioBuffer.h"
32#include "../Core/AudioSpec.h"
33#include "../Core/DspMath.h"
34#include "../Core/SimdOps.h"
35#include "../Core/StateBlob.h"
66 static_assert(std::atomic<T>::is_always_lock_free,
67 "AutoGain requires a lock-free float type for thread safety in the audio path.");
80 if (!spec.isValid())
return;
82 sampleRate_ = spec.sampleRate;
83 numChannels_ = spec.numChannels;
97 if (std::min(buffer.getNumChannels(), numChannels_) <= 0 ||
98 buffer.getNumSamples() <= 0)
100 refLevelDb_ = measureRmsDb(buffer);
110 const int numCh = std::min(buffer.getNumChannels(), numChannels_);
111 const int numSamples = buffer.getNumSamples();
113 if (numSamples == 0 || numCh == 0)
return;
115 T outLevelDb = measureRmsDb(buffer);
116 T targetDb = refLevelDb_ - outLevelDb;
119 if (std::isnan(targetDb))
123 const T maxComp = maxCompensation_.load(std::memory_order_relaxed);
124 targetDb = std::clamp(targetDb, -maxComp, maxComp);
127 if (refLevelDb_ < SILENCE_THRESH_DB && outLevelDb < SILENCE_THRESH_DB)
132 const T smoothSecs = smoothTimeSecs_.load(std::memory_order_relaxed);
133 const T alpha =
static_cast<T
>(std::exp(-
static_cast<double>(numSamples)
134 / (sampleRate_ *
static_cast<double>(smoothSecs))));
135 const T endCompensationDb = targetDb + (compensationDb_ - targetDb) * alpha;
140 const T gainStep = (endGain - startGain) /
static_cast<T
>(numSamples);
144 for (
int ch = 0; ch < numCh; ++ch)
146 T* data = buffer.getChannel(ch);
147 for (
int i = 0; i < numSamples; ++i)
149 data[i] *= (startGain +
static_cast<T
>(i) * gainStep);
154 compensationDb_ = endCompensationDb;
162 refLevelDb_ = SILENCE_THRESH_DB;
163 compensationDb_ = T(0);
179 if (!std::isfinite(dB))
return;
180 maxCompensation_.store(std::abs(dB), std::memory_order_relaxed);
191 if (!std::isfinite(ms))
return;
192 smoothTimeSecs_.store(std::max<T>(ms * T(0.001), T(0.001)),
193 std::memory_order_relaxed);
199 return maxCompensation_.load(std::memory_order_relaxed);
205 return smoothTimeSecs_.load(std::memory_order_relaxed) * T(1000);
209 [[nodiscard]] std::vector<uint8_t>
getState()
const
214 w.
write(
"maxComp",
static_cast<float>(maxCompensation_.load(std::memory_order_relaxed)));
237 const int numCh = std::min(buffer.getNumChannels(), numChannels_);
238 const int numSamples = buffer.getNumSamples();
239 if (numCh <= 0 || numSamples <= 0)
return SILENCE_THRESH_DB;
242 const int totalSamples = numSamples * numCh;
244 for (
int ch = 0; ch < numCh; ++ch)
248 T meanSq = std::max<T>(sumSq /
static_cast<T
>(totalSamples), T(1e-15));
252 static constexpr T SILENCE_THRESH_DB = T(-90);
254 double sampleRate_ = 44100.0;
255 int numChannels_ = 0;
257 std::atomic<T> smoothTimeSecs_{ T(0.100) };
258 T refLevelDb_ = SILENCE_THRESH_DB;
259 T compensationDb_ = T(0);
261 std::atomic<T> maxCompensation_{ T(12) };
Non-owning view over audio channel data.
Block-adaptive automatic gain compensation with SIMD-friendly linear interpolation.
void reset() noexcept
Hard resets the internal state to avoid feedback loops or stale measurements.
T getSmoothingTime() const noexcept
void pushReference(AudioBufferView< T > buffer) noexcept
Snapshots the input level. Must be called BEFORE processing.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void prepare(const AudioSpec &spec) noexcept
Prepares the auto-gain processor.
void setSmoothingTime(T ms) noexcept
Sets the smoothing time constant.
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
void compensate(AudioBufferView< T > buffer) noexcept
Measures output level and applies smoothed gain compensation. Must be called AFTER processing.
T getMaxCompensation() const noexcept
T getCompensationDb() const noexcept
Returns the current internal compensation in dB. Useful for UI metering.
void setMaxCompensation(T dB) noexcept
Thread-safe assignment of the maximum allowed compensation limit.
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.
float sumOfSquares(const float *DSPARK_RESTRICT data, int count) noexcept
Returns the sum of squared samples (energy).
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.