Γ-W1-T2 review: one restart funnel, tighter ceiling proof, effective-gain meter
Fold setLimiterEnabled's restart request into setInstrumentParams so every writer keeps the host's latency report in sync. Pin the window-sizing identity, drop the per-sample modulo, tighten the ceiling tolerance, publish the blended gain.
This commit is contained in:
@@ -114,18 +114,22 @@ float Limiter::detectTruePeak(float xl, float xr, bool stereo) {
|
||||
|
||||
float Limiter::smoothGain(float target) {
|
||||
// Sliding minimum over `window_` via a monotonic wedge. Expiring the front BEFORE the push
|
||||
// is what bounds the wedge to `window_` entries — pushing first can lap the ring.
|
||||
// is what bounds the wedge to `window_` entries — pushing first can lap the ring. Wraps by
|
||||
// compare-and-subtract, matching delayPos_/avgPos_: window_ is not a power of two, so `%`
|
||||
// would not strength-reduce on this per-sample path.
|
||||
while (wedgeCount_ > 0 &&
|
||||
wedgeIdx_[static_cast<std::size_t>(wedgeHead_)] <= pushIndex_ - window_) {
|
||||
wedgeHead_ = (wedgeHead_ + 1) % window_;
|
||||
wedgeHead_ = (wedgeHead_ + 1 == window_) ? 0 : wedgeHead_ + 1;
|
||||
--wedgeCount_;
|
||||
}
|
||||
while (wedgeCount_ > 0) {
|
||||
const int back = (wedgeHead_ + wedgeCount_ - 1) % window_;
|
||||
const int backSum = wedgeHead_ + wedgeCount_ - 1;
|
||||
const int back = (backSum >= window_) ? backSum - window_ : backSum;
|
||||
if (wedgeVal_[static_cast<std::size_t>(back)] < target) break;
|
||||
--wedgeCount_;
|
||||
}
|
||||
const int slot = (wedgeHead_ + wedgeCount_) % window_;
|
||||
const int slotSum = wedgeHead_ + wedgeCount_;
|
||||
const int slot = (slotSum >= window_) ? slotSum - window_ : slotSum;
|
||||
wedgeVal_[static_cast<std::size_t>(slot)] = target;
|
||||
wedgeIdx_[static_cast<std::size_t>(slot)] = pushIndex_;
|
||||
++wedgeCount_;
|
||||
@@ -171,7 +175,6 @@ float Limiter::process(float* left, float* right, int frames) {
|
||||
const float peak = detectTruePeak(dryL, dryR, stereo);
|
||||
const float targetGain = peak > ceiling_ ? ceiling_ / peak : 1.f;
|
||||
const float gain = smoothGain(targetGain);
|
||||
if (gain < blockMin) blockMin = gain;
|
||||
|
||||
const std::size_t slot = static_cast<std::size_t>(delayPos_);
|
||||
const float wetL = delayL_[slot] * gain;
|
||||
@@ -184,6 +187,12 @@ float Limiter::process(float* left, float* right, int frames) {
|
||||
// dry + (wet - dry) * 1.0f is not wet in floating point. At m <= 0 the buffer is left
|
||||
// untouched, which is the dry sample already in it.
|
||||
const float m = mix_;
|
||||
// The reported minimum is the gain actually reaching the output, not the limiter's raw
|
||||
// target — mid-crossfade only a fraction `m` of the reduction is audible, so the meter
|
||||
// (whose contract is "smallest gain APPLIED") must blend the same way the signal does:
|
||||
// unity at m=0, `gain` at m=1, linear between.
|
||||
const float effectiveGain = 1.f - m + m * gain;
|
||||
if (effectiveGain < blockMin) blockMin = effectiveGain;
|
||||
if (m >= 1.f) {
|
||||
left[i] = wetL;
|
||||
if (stereo) right[i] = wetR;
|
||||
|
||||
Reference in New Issue
Block a user