feat(dock): the TRAX bay — track, play/pause, level, duck badge

Unit 7. The bay shows the track name (left-click toggles playback, right-click opens the picker —
the label stays the control), a play/pause button, the duck badge, and a dB level.

No ladder here, deliberately: nothing measures the music bed, and a decorative ladder would break
the measured-not-painted rule. A test asserts the bay has no SegmentMeter.

The level is real: `ITraxPlayer.UserGain` is a new creator gain multiplied onto the fixed design
volume, separate from `LocalGain` (which follows the desktop mute). The VM's `TraxVolumeDb` is the
same dB scale as the other faders, so unity means the bed's design level.
This commit is contained in:
2026-10-30 13:15:17 -07:00
parent a1d8e761ea
commit 7339df5fcd
7 changed files with 154 additions and 5 deletions
+25
View File
@@ -63,6 +63,28 @@ public partial class MainViewModel
? BarOnBrush
: TraxError != null ? BarOffBrush : TraxIdleBrush;
/// <summary>The dock's TRAX fader, in dB. Unity means the bed's design level, not the raw file.</summary>
public double TraxVolumeDb
{
get => Helpers.GainDb.ToDb(_musicPlayer.UserGain);
set
{
var gain = Helpers.GainDb.FromDb(value);
if (Math.Abs(gain - _musicPlayer.UserGain) < 1e-6) return;
_musicPlayer.UserGain = (float)gain;
OnPropertyChanged(nameof(TraxVolumeDb));
OnPropertyChanged(nameof(TraxVolumeDbLabel));
}
}
public string TraxVolumeDbLabel => Helpers.GainDb.Label(TraxVolumeDb) + " dB";
/// <summary>Track name, or an honest "No track".</summary>
public string TraxTrackLabel => _musicPlayer.TrackName ?? "No track";
/// <summary>Same ducker signal as the game bay — the bed ducks with it.</summary>
public bool IsTraxDucking => IsGameDucking;
public string TraxToolTip => _musicPlayer.TrackName != null
? $"TRAX: {_musicPlayer.TrackName} — left-click {(IsMusicPlaying ? "pauses" : "plays")}, right-click chooses a track"
: "No track — left-click or right-click to choose background music";
@@ -81,6 +103,9 @@ public partial class MainViewModel
/// <summary>Right-click on TRAX: always open the track picker (never the OS viewer).</summary>
public void TraxRightClick() => PickTraxTrack();
/// <summary>Open the track picker (the dock's right-click on the TRAX name).</summary>
internal void ChooseTrack() => PickTraxTrack();
private void PickTraxTrack()
{
var dialog = new OpenFileDialog