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; }