/// Limitador final brick-wall (peak limiter con release suavizado).
/// Sin look-ahead en v0.1: aplica gain instantáneo por muestra.
pub struct Limiter {
    gain:     f32,
    rel_coef: f32,
    thr_lin:  f32,
}

impl Limiter {
    pub fn new(threshold_db: f32, sample_rate: f32) -> Self {
        let release_ms = 200.0f32;
        Self {
            gain:     1.0,
            rel_coef: (-1.0 / (release_ms * 0.001 * sample_rate)).exp(),
            thr_lin:  10f32.powf(threshold_db / 20.0),
        }
    }

    pub fn update_threshold(&mut self, threshold_db: f32) {
        self.thr_lin = 10f32.powf(threshold_db / 20.0);
    }

    /// Procesa un par de muestras y retorna la reducción de ganancia (0..1).
    #[inline]
    pub fn process(&mut self, l: &mut f32, r: &mut f32) -> f32 {
        let peak = l.abs().max(r.abs());
        let needed = if peak > self.thr_lin {
            self.thr_lin / peak
        } else {
            1.0
        };

        // Ataque instantáneo, release suavizado
        if needed < self.gain {
            self.gain = needed;
        } else {
            self.gain = self.rel_coef * self.gain + (1.0 - self.rel_coef) * needed;
        }

        *l *= self.gain;
        *r *= self.gain;

        1.0 - self.gain  // 0 = sin limitación, 1 = máxima limitación
    }
}
