37 lines
1.2 KiB
C#
37 lines
1.2 KiB
C#
namespace ytLive.Services.Audio;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public sealed class AutoDucker
|
|
{
|
|
public const float Threshold = 0.02f;
|
|
|
|
/// <summary>-12 dB: the loopback's volume while the mic is active.</summary>
|
|
public const float DuckGain = 0.25f;
|
|
|
|
private const float Attack = 0.05f;
|
|
private const float Release = 0.005f;
|
|
|
|
private float _gain = 1f;
|
|
|
|
/// <summary>The current loopback gain to apply (1 = no duck).</summary>
|
|
public float CurrentGain => _gain;
|
|
|
|
/// <summary>Feeds one mic level sample (0..1, post-filter RMS) and returns
|
|
/// the gain to apply to the loopback for that tick.</summary>
|
|
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;
|
|
}
|