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
+4
View File
@@ -8,6 +8,10 @@ public interface ITraxPlayer : IDisposable
string? TrackName { get; }
bool IsPlaying { get; }
float LocalGain { get; set; }
/// <summary>The creator's TRAX level (1 = the bed's fixed design level). Separate from
/// <see cref="LocalGain"/>, which follows the desktop mute; the dock's fader moves this.</summary>
float UserGain { get; set; }
void Load(string path);
void Play();
void Pause();
+14 -1
View File
@@ -41,6 +41,19 @@ public sealed class MusicPlayer : ITraxPlayer
/// <summary>Local gain multiplier from the desktop/game audio bar (0 = mute,
/// 1 = full bed). Applied live on top of the fixed <see cref="MusicVolume"/>.</summary>
private float _userGain = 1f;
/// <summary>Creator-set level, multiplied onto the fixed design volume.</summary>
public float UserGain
{
get => _userGain;
set
{
_userGain = Math.Clamp(value, 0f, 2f);
if (_output != null) _output.Volume = MusicVolume * _userGain * LocalGain;
}
}
public float LocalGain
{
get => _localGain;
@@ -61,7 +74,7 @@ public sealed class MusicPlayer : ITraxPlayer
Stop();
TrackPath = path;
_reader = new MediaFoundationReader(path);
_volume = new VolumeWaveProvider16(_reader) { Volume = MusicVolume * _localGain };
_volume = new VolumeWaveProvider16(_reader) { Volume = MusicVolume * _userGain * _localGain };
_output = new WaveOutEvent();
_output.PlaybackStopped += OnPlaybackStopped;
_output.Init(_volume);