Files
LlamaCasty/Services/Audio/AudioLevelMeter.cs
T

57 lines
2.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace ytLive.Services.Audio;
/// <summary>
/// Computes the smoothed mic level (0..1) from captured samples. Pure —
/// unit-tested; the <see cref="AudioMixer"/> only feeds it and forwards the
/// result. RMS-based so it tracks perceived loudness, smoothed so the meter
/// does not flicker.
/// </summary>
public sealed class AudioLevelMeter
{
private const float Smoothing = 0.2f;
private const float DecaySmoothing = 0.5f;
private float _level;
public float Level => _level;
/// <summary>
/// Maps a linear level (0..1) onto the meter's display scale: -60 dBFS..0 dBFS
/// spread linearly across 0..1 — an HONEST dBFS scale. Linear RMS of real speech 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. Inputs at or below -60 dBFS
/// (0.001 linear) read as zero — the meter never idles on background noise.
/// <para>2026-10-09: the +10 dB "input amplification" that used to sit here is gone.
/// It made a −30 dBFS voice look dialed in, and a creator's upload came out at
/// −28.5 LUFS. The speech leveler now puts voice at −20 dBFS, which lands at 2/3 of
/// the bar (the green/yellow edge); loud peaks reach the red.</para>
/// </summary>
public static float ToDisplay(float linear)
{
if (linear <= 0.001f)
return 0f;
var db = 20f * MathF.Log10(linear);
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);
var alpha = rms > _level ? Smoothing : DecaySmoothing;
_level = _level * (1 - alpha) + rms * alpha;
return _level;
}
public void Reset() => _level = 0;
}