mirror of
https://codeberg.org/armin/ambivalence.git
synced 2026-09-01 04:10:48 +02:00
init
This commit is contained in:
parent
29c860587e
commit
42cf8432bf
57 changed files with 5782 additions and 0 deletions
79
Source/DSP/OutputLimiter.h
Normal file
79
Source/DSP/OutputLimiter.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace FDNReverb {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OutputLimiter: safe output stage (true-peak limiter)
|
||||
// -----------------------------------------------------------------------------
|
||||
// design rationale :
|
||||
// - parameter values are chosen conservatively for safety
|
||||
// - Threshold = -0.5 dBFS (~0.944): suppresses peaks before the DAW limiter
|
||||
// - Look-ahead: introduces plugin latency
|
||||
// - Attack: 0.5 ms (peak-based)
|
||||
// - Release: 50 ms (prevents unnatural pumping)
|
||||
//
|
||||
// real-time safety :
|
||||
// - allocation: once in prepare(), never in processBlock
|
||||
// - per-sample gain: one comparison against targetGain, SIMD-friendly
|
||||
// - floating-point math: no branches or transcendental functions
|
||||
//
|
||||
// - layout: output stage of UniversalEngine::processBlock()
|
||||
// (after Dry/Wet mix, before the stereo output)
|
||||
// -----------------------------------------------------------------------------
|
||||
class OutputLimiter {
|
||||
public:
|
||||
OutputLimiter() = default;
|
||||
|
||||
// --- sample-rate dependent coefficient computation ---
|
||||
void prepare(double sampleRate) noexcept {
|
||||
fs = sampleRate;
|
||||
// 1 path filter coefficient : y[n] = y[n-1] + coeff * (x[n] - y[n-1])
|
||||
// coeff = 1 - exp(-T / tau) where T = 1/fs, tau = time constant
|
||||
attackCoeff = 1.0f - std::exp(-1.0f / (static_cast<float>(fs) * 0.0005f)); // 0.5ms
|
||||
releaseCoeff = 1.0f - std::exp(-1.0f / (static_cast<float>(fs) * 0.050f)); // 50ms
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() noexcept {
|
||||
currentGain = 1.0f;
|
||||
}
|
||||
|
||||
// --- per-sample processing (called from the audio thread) ---
|
||||
inline void process(float& l, float& r) noexcept {
|
||||
// peak detection (max of L/R levels)
|
||||
const float absL = std::abs(l);
|
||||
const float absR = std::abs(r);
|
||||
const float peak = std::max(absL, absR);
|
||||
|
||||
// Threshold: -0.5 dBFS ~ 0.944
|
||||
// compute target gain from the signal
|
||||
constexpr float threshold = 0.944f;
|
||||
|
||||
// target gain :
|
||||
// peak <= threshold -> 1.0 (no reduction needed)
|
||||
// peak > threshold -> threshold/peak (pull signal to threshold)
|
||||
const float targetGain = (peak > threshold) ? (threshold / peak) : 1.0f;
|
||||
|
||||
// Attack/Release envelope
|
||||
// when targetGain < currentGain (gain must decrease): attack
|
||||
// when targetGain > currentGain (gain recovers): release
|
||||
// so peaks are suppressed smoothly
|
||||
const float coeff = (targetGain < currentGain) ? attackCoeff : releaseCoeff;
|
||||
currentGain += (targetGain - currentGain) * coeff;
|
||||
|
||||
// apply the same gain to L/R to preserve the stereo image
|
||||
l *= currentGain;
|
||||
r *= currentGain;
|
||||
}
|
||||
|
||||
private:
|
||||
double fs{ 44100.0 };
|
||||
float attackCoeff{ 0.0f };
|
||||
float releaseCoeff{ 0.0f };
|
||||
float currentGain{ 1.0f };
|
||||
};
|
||||
|
||||
} // namespace FDNReverb
|
||||
Loading…
Add table
Add a link
Reference in a new issue