Files
gramps afe21682c2 feat(audio): the dock's dB scale, and the mono fold primitive
Unit 1 of the sound-bar refactor. The model stores linear gain; the dock shows decibels, because
"−6 dB" is readable and "0.50" is not. `Helpers/GainDb` owns the mapping:

- 0 dB = unity (the old 1.0), range −60 dB (true silence, exactly 0 gain) … +6 dB.
- Labels for the fader readout: "−∞", "-6.0", "+1.5".
- `MonoFold(L,R)` = (L+R)/2, the accessible downmix: hard-panned content becomes audible on both
  sides at half amplitude instead of absent from one. Creator context: profoundly deaf on the
  right, so single-sided audio is inaudible, not merely annoying.

Guard: GainDbTests pins unity, the −6 dB half, the exact-zero floor, the +6 dB ceiling, a
round-trip across the range, the labels, and the fold arithmetic (a right-only 0.5 arrives on the
left at 0.25).
2026-10-30 12:32:07 -07:00

45 lines
1.9 KiB
C#
Raw Permalink 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.
using System;
namespace ytLive.Helpers;
/// <summary>The fader scale for the audio dock: the model stores LINEAR gain (0..1), the dock
/// shows and edits DECIBELS, because "−6 dB" means something to a person and "0.50" does not.
///
/// <para>Range: −∞ (silence) to +6 dB of headroom above unity. 0 dB is unity gain, which is what
/// the old 1.0 meant — so a persisted 0.8 comes back as ≈ −1.9 dB and nothing about the stored
/// data changes.</para>
///
/// <para>Creator context (2026-10-30): nearly deaf on the right, so the fader number is read, not
/// heard. The scale therefore has to be the true one, not a cosmetic curve.</para></summary>
public static class GainDb
{
/// <summary>Silence floor. Anything at or below this reads as −∞ and writes as 0 gain.</summary>
public const double FloorDb = -60;
public const double MaxDb = 6;
/// <summary>Linear gain → dB, clamped to the fader's range. Zero becomes <see cref="FloorDb"/>.</summary>
public static double ToDb(double gain)
{
if (gain <= 0) return FloorDb;
var db = 20 * Math.Log10(gain);
return Math.Clamp(db, FloorDb, MaxDb);
}
/// <summary>dB → linear gain. At the floor the result is exactly 0 (true silence), so the
/// bottom of the fader cannot leave a residue of noise.</summary>
public static double FromDb(double db)
{
if (db <= FloorDb) return 0;
return Math.Pow(10, Math.Clamp(db, FloorDb, MaxDb) / 20);
}
/// <summary>"−∞", "−6.0", "+1.5" — the label beside a fader.</summary>
public static string Label(double db) =>
db <= FloorDb ? "−∞" : (db > 0 ? "+" : string.Empty) + db.ToString("0.0");
/// <summary>Mono fold: the accessible downmix. Averages the pair so hard-panned content is
/// audible on both sides at half amplitude instead of absent from one.</summary>
public static float MonoFold(float left, float right) => (left + right) * 0.5f;
}