namespace ytLive.Services.Audio; /// /// Computes the smoothed mic level (0..1) from captured samples. Pure — /// unit-tested; the only feeds it and forwards the /// result. RMS-based so it tracks perceived loudness, smoothed so the meter /// does not flicker. /// public sealed class AudioLevelMeter { private const float Smoothing = 0.2f; private float _level; public float Level => _level; /// /// Maps a linear level (0..1) onto the meter's display scale: -60 dBFS..0 dBFS /// spread linearly across 0..1, with +10 dB of input amplification. Linear RMS /// of real speech or game audio is ~0.01..0.1 (-40..-20 dBFS), which leaves a /// flat (linear) meter looking dead; the log scale makes typical levels occupy /// the bar and the amplification pushes real speech peaks into the red zone /// (0.8+) at maxed volume instead of hovering at its edge. Inputs at or below /// -60 dBFS (0.001 linear) read as zero — the meter never idles on background noise. /// public static float ToDisplay(float linear) { if (linear <= 0.001f) return 0f; var db = 20f * MathF.Log10(linear) + 10f; return Math.Clamp(1f + db / 60f, 0f, 1f); } public float Push(AudioSample sample) { if (sample.Samples.Length == 0) return _level; double sumSquares = 0; var count = 0; foreach (var value in sample.Samples) { sumSquares += value * value; count++; } var rms = (float)Math.Sqrt(sumSquares / count); _level = _level * (1 - Smoothing) + rms * Smoothing; return _level; } public void Reset() => _level = 0; }