using System;
namespace ytLive.Helpers;
/// Which colour band a level falls in (DESIGN.md meter thresholds).
public enum MeterBand
{
Nominal,
Caution,
Peak,
}
/// The mathematics of the dock's segment meter, kept out of the control so it can be
/// tested: a segment ladder is only honest if the band it paints is the level it measured.
///
/// Bands follow DESIGN.md: nominal to −12 dB, caution to −3 dB, peak above. The display
/// fraction spreads −60…0 dBFS across 0…1 — the same scale AudioLevelMeter.ToDisplay uses,
/// so the ladder and the old bars cannot disagree.
public static class MeterScale
{
public const float FloorDb = -60f;
public const float CautionDb = -12f;
public const float PeakDb = -3f;
/// dBFS → 0…1 across the whole ladder.
public static float ToFraction(float dbfs)
=> Math.Clamp(1f + Math.Clamp(dbfs, FloorDb, 0f) / 60f, 0f, 1f);
/// The band boundaries are INCLUSIVE at the bottom edge (a meter reading exactly
/// −12.0 dB is cautious, exactly −3.0 is peaking), matching the ranges as DESIGN.md states
/// them: "−12 … −3" and "−3 … 0+".
public static MeterBand BandFor(float dbfs)
=> dbfs >= PeakDb ? MeterBand.Peak
: dbfs >= CautionDb ? MeterBand.Caution
: MeterBand.Nominal;
/// Peak hold: jumps up instantly to a new maximum and falls back at
/// — a moment-to-moment bar hides clipping, which is the one
/// audio fault that cannot be fixed afterwards.
public static float NextPeak(float held, float incoming, float seconds, float decayPerSecond = 0.35f)
{
if (incoming >= held) return incoming;
return Math.Max(0f, held - decayPerSecond * Math.Max(0f, seconds));
}
}