Reported: no activity in the master meter. Cause: `MasterLevel` measures the buffer the ENCODER receives, and that buffer only exists while the mix is being pulled — streaming or recording. Idle, nothing fills it, so the master read zero while the bays below it moved. A master that is dead between sessions is a gap in the design. Now: while a session runs the master shows the measured mix (unchanged); idle it shows an estimate computed from the same measured sources and the same gains the mix applies (`MeterScale.MonitorFraction`, pure + tested). Two ladders can therefore never tell contradictory stories about whether audio exists. Guard: MeterScaleTests pins the estimate — silence reads zero, a quiet source at unity is visible, two sources add, and gain is applied in the LINEAR domain like the mix (scaling the display fraction would be a different, wrong curve).
50 lines
2.7 KiB
C#
50 lines
2.7 KiB
C#
using Xunit;
|
||
using ytLive.Helpers;
|
||
|
||
namespace ytLive.Tests;
|
||
|
||
/// <summary>A segment ladder is only useful if the band it paints is the level it measured. The
|
||
/// thresholds are DESIGN.md's (nominal / caution / peak), and peak-hold is what makes clipping
|
||
/// visible — a bar that follows the moment hides the one audio fault that cannot be fixed later.
|
||
/// </summary>
|
||
public class MeterScaleTests
|
||
{
|
||
[Fact]
|
||
public void Bands_peak_hold_and_the_display_scale_follow_the_standard()
|
||
{
|
||
// Bands at the documented thresholds.
|
||
Assert.Equal(MeterBand.Nominal, MeterScale.BandFor(-30f));
|
||
Assert.Equal(MeterBand.Nominal, MeterScale.BandFor(-12.1f));
|
||
Assert.Equal(MeterBand.Caution, MeterScale.BandFor(-12f));
|
||
Assert.Equal(MeterBand.Caution, MeterScale.BandFor(-6f));
|
||
Assert.Equal(MeterBand.Peak, MeterScale.BandFor(-3f));
|
||
Assert.Equal(MeterBand.Peak, MeterScale.BandFor(0f));
|
||
|
||
// Display fraction: −60 dBFS is empty, 0 dBFS is full, −30 is halfway, and nothing escapes.
|
||
Assert.Equal(0f, MeterScale.ToFraction(-60f));
|
||
Assert.Equal(1f, MeterScale.ToFraction(0f));
|
||
Assert.Equal(0.5f, MeterScale.ToFraction(-30f), 4);
|
||
Assert.Equal(1f, MeterScale.ToFraction(+12f));
|
||
Assert.Equal(0f, MeterScale.ToFraction(-120f));
|
||
|
||
// Level ballistics: a jump is followed quickly, a drop falls gently — the same curve that
|
||
// makes the mic meter look fluid. Raw peaks at a 30 Hz UI tick stutter without it.
|
||
Assert.Equal(0.6f, MeterScale.NextLevel(0f, 1f), 3); // attack
|
||
Assert.True(MeterScale.NextLevel(0.9f, 0.1f) > 0.8f); // decay is gentle
|
||
Assert.True(MeterScale.NextLevel(0.1f, 0.9f) > 0.5f); // rise is fast
|
||
|
||
// The idle master estimate: sources × their gains, on the same display scale. With nothing
|
||
// playing it is zero; a quiet source at unity is visible; two sources add.
|
||
Assert.Equal(0f, MeterScale.MonitorFraction(0f, 1, 0f, 1));
|
||
Assert.True(MeterScale.MonitorFraction(0.05f, 1, 0f, 1) > 0.3f);
|
||
Assert.True(MeterScale.MonitorFraction(0.05f, 1, 0.05f, 1) > MeterScale.MonitorFraction(0.05f, 1, 0f, 1));
|
||
// Gain is applied in the LINEAR domain, like the mix does — not to the display fraction.
|
||
Assert.True(MeterScale.MonitorFraction(0.05f, 0.5, 0f, 1) < MeterScale.MonitorFraction(0.05f, 1, 0f, 1));
|
||
|
||
// Peak hold rises instantly, then decays at the stated rate.
|
||
Assert.Equal(0.8f, MeterScale.NextPeak(0.2f, 0.8f, 0.05f));
|
||
Assert.Equal(0.7f, MeterScale.NextPeak(0.8f, 0.1f, 0.1f / 0.35f), 3); // 0.1 fall at 0.35/s
|
||
Assert.Equal(0f, MeterScale.NextPeak(0.1f, 0f, 10f)); // never negative
|
||
}
|
||
}
|