namespace ytLive.Services.Audio;
///
/// Auto-duck (TASK 9): while the mic is hot the loopback (game + music) rides
/// its volume down ~12 dB so the voice stays on top of the mix, then recovers
/// when the creator stops talking. Smooth attack/release, always-on, no knobs.
/// Pure — unit-tested.
///
public sealed class AutoDucker
{
public const float Threshold = 0.02f;
/// -12 dB: the loopback's volume while the mic is active.
public const float DuckGain = 0.25f;
private const float Attack = 0.05f;
private const float Release = 0.005f;
private float _gain = 1f;
/// The current loopback gain to apply (1 = no duck).
public float CurrentGain => _gain;
/// Feeds one mic level sample (0..1, post-filter RMS) and returns
/// the gain to apply to the loopback for that tick.
public float Update(float micLevel)
{
var target = micLevel > Threshold ? DuckGain : 1f;
_gain += (target - _gain) * (target < _gain ? Attack : Release);
if (MathF.Abs(_gain - target) < 0.001f)
_gain = target;
return _gain;
}
public void Reset() => _gain = 1f;
}