diff --git a/Controls/AudioDock.xaml b/Controls/AudioDock.xaml
index 2c74c56..41c4a37 100644
--- a/Controls/AudioDock.xaml
+++ b/Controls/AudioDock.xaml
@@ -18,11 +18,11 @@
-
-
+
+
@@ -30,10 +30,7 @@
-
-
-
+
@@ -42,14 +39,11 @@
-
-
+
+
-
+
+
-
@@ -82,15 +77,10 @@
-
-
@@ -99,10 +89,11 @@
-
-
+
+
+ Foreground="{StaticResource Brush.OnSurfaceVariant}" Text="{Binding GameVolumeDbLabel}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -193,13 +174,9 @@
+ Foreground="{StaticResource Brush.Tertiary}" Text="{Binding SyncOffsetLabel}"/>
-
-
diff --git a/Controls/AudioDock.xaml.cs b/Controls/AudioDock.xaml.cs
index 2a7bff9..21c910e 100644
--- a/Controls/AudioDock.xaml.cs
+++ b/Controls/AudioDock.xaml.cs
@@ -39,6 +39,16 @@ public partial class AudioDock : UserControl
vm.OpenMicPickerCommand.Execute(null);
}
+ private void TraxStepDown_Click(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.MainViewModel vm) vm.StepTrax(-1);
+ }
+
+ private void TraxStepUp_Click(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.MainViewModel vm) vm.StepTrax(+1);
+ }
+
private void SyncPreset_Click(object sender, RoutedEventArgs e)
{
if (DataContext is not ViewModels.MainViewModel vm) return;
diff --git a/ViewModels/MainViewModel.Trax.cs b/ViewModels/MainViewModel.Trax.cs
index cf24c13..900e4b8 100644
--- a/ViewModels/MainViewModel.Trax.cs
+++ b/ViewModels/MainViewModel.Trax.cs
@@ -63,7 +63,21 @@ public partial class MainViewModel
? BarOnBrush
: TraxError != null ? BarOffBrush : TraxIdleBrush;
- /// The dock's TRAX fader, in dB. Unity means the bed's design level, not the raw file.
+ /// Where TRAX starts: quiet background under the desktop bed. The ±5 dB buttons are the
+ /// control surface — a creator who wants it louder clicks up three times rather than hunting a
+ /// 6px thumb on a 148px dock. (Creator ruling 2026-10-30, from their own meter readings.)
+ public const double TraxDefaultDb = -50;
+
+ public const double TraxStepDb = 5;
+
+ /// +6 dB is the ceiling; −60 dB is silence. The buttons disable at both ends.
+ public bool CanRaiseTrax => TraxVolumeDb < Helpers.GainDb.MaxDb;
+
+ public bool CanLowerTrax => TraxVolumeDb > Helpers.GainDb.FloorDb;
+
+ public void StepTrax(int steps) => TraxVolumeDb += steps * TraxStepDb;
+
+ /// The dock's TRAX level, in dB. Unity means the bed's design level, not the raw file.
public double TraxVolumeDb
{
get => Helpers.GainDb.ToDb(_musicPlayer.UserGain);
@@ -74,6 +88,8 @@ public partial class MainViewModel
_musicPlayer.UserGain = (float)gain;
OnPropertyChanged(nameof(TraxVolumeDb));
OnPropertyChanged(nameof(TraxVolumeDbLabel));
+ OnPropertyChanged(nameof(CanRaiseTrax));
+ OnPropertyChanged(nameof(CanLowerTrax));
}
}
diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs
index 6bc4ac5..8d3d047 100644
--- a/ViewModels/MainViewModel.cs
+++ b/ViewModels/MainViewModel.cs
@@ -277,6 +277,7 @@ public partial class MainViewModel : ViewModelBase
MicSourceName = _layoutStore.LoadMicSourceName();
AudioSyncOffsetMs = _layoutStore.LoadAudioSyncOffsetMs();
MonoOutput = _layoutStore.LoadMonoOutput(); // applied to the mixer by the setter
+ _musicPlayer.UserGain = (float)Helpers.GainDb.FromDb(TraxDefaultDb); // background by default
try
{
var s = _layoutStore.GetSetting("ZoomShape");
diff --git a/ytLive.Tests/GameBayTests.cs b/ytLive.Tests/GameBayTests.cs
index 240b647..708b498 100644
--- a/ytLive.Tests/GameBayTests.cs
+++ b/ytLive.Tests/GameBayTests.cs
@@ -15,6 +15,10 @@ public class GameBayTests
{
var dock = SourceAssert.Text("Controls", "AudioDock.xaml");
Assert.Contains("Value=\"{Binding GameVolumeDb, Mode=TwoWay}\"", dock);
+ // Desktop volume sits directly beneath its meter (creator's layout ruling).
+ var desktopMeter = dock.IndexOf("Level=\"{Binding GameMeterFraction}\"", System.StringComparison.Ordinal);
+ var desktopFader = dock.IndexOf("Value=\"{Binding GameVolumeDb, Mode=TwoWay}\"", System.StringComparison.Ordinal);
+ Assert.True(desktopFader > desktopMeter, "the desktop fader belongs under the desktop ladder");
Assert.Contains("Level=\"{Binding GameMeterFraction}\"", dock);
Assert.Contains("Text=\"{Binding GameDuckLabel}\"", dock);
Assert.Contains("Visibility=\"{Binding IsGameDucking, Converter={StaticResource BoolToVis}}\"", dock);
diff --git a/ytLive.Tests/TraxBayTests.cs b/ytLive.Tests/TraxBayTests.cs
index 517098e..f5da2da 100644
--- a/ytLive.Tests/TraxBayTests.cs
+++ b/ytLive.Tests/TraxBayTests.cs
@@ -16,20 +16,25 @@ public class TraxBayTests
{
var dock = SourceAssert.Text("Controls", "AudioDock.xaml");
Assert.Contains("Text=\"{Binding TraxTrackLabel}\"", dock);
- Assert.Contains("Value=\"{Binding TraxVolumeDb, Mode=TwoWay}\"", dock);
+ // KISS (creator 2026-10-30): background music is one number and two ±5 dB buttons, not a
+ // channel strip — and it has no ladder of its own, because the desktop bed already carries it.
+ Assert.Contains("Click=\"TraxStepDown_Click\"", dock);
+ Assert.Contains("Click=\"TraxStepUp_Click\"", dock);
+ Assert.Contains("IsEnabled=\"{Binding CanLowerTrax}\"", dock);
+ Assert.Contains("IsEnabled=\"{Binding CanRaiseTrax}\"", dock);
Assert.Contains("Text=\"{Binding TraxVolumeDbLabel}\"", dock);
Assert.Contains("Binding=\"{Binding IsMusicPlaying}\" Value=\"True\"", dock);
Assert.Contains("Click=\"TraxPlayPause_Click\"", dock);
// The bay DOES carry a ladder now — the player measures its own output, so "is music making
// sound" is answerable without hearing it (a play glyph cannot answer that).
- var traxBay = dock[dock.IndexOf("Text=\"TRAX\"", System.StringComparison.Ordinal)
+ // The TRAX row lives under the desktop volume, inside the GAME bay.
+ var traxRow = dock[dock.IndexOf("Text=\"TRAX\"", System.StringComparison.Ordinal)
..dock.IndexOf("Text=\"LIP-SYNC\"", System.StringComparison.Ordinal)];
- Assert.Contains("TraxTrackLabel", traxBay);
- Assert.Contains("SegmentMeter", traxBay);
- Assert.Contains("TraxMeterFraction", traxBay);
+ Assert.Contains("TraxTrackLabel", traxRow);
+ Assert.DoesNotContain("SegmentMeter", traxRow);
- foreach (var name in new[] { "TraxVolumeDb", "TraxVolumeDbLabel", "TraxTrackLabel", "IsTraxDucking", "TraxMeterFraction" })
+ foreach (var name in new[] { "TraxVolumeDb", "TraxVolumeDbLabel", "TraxTrackLabel", "CanRaiseTrax", "CanLowerTrax" })
Assert.NotNull(typeof(MainViewModel).GetProperty(name));
// The player gained a real user level, separate from the mute-follow LocalGain.
@@ -37,7 +42,9 @@ public class TraxBayTests
Assert.NotNull(typeof(ITraxPlayer).GetProperty("Level"));
Assert.NotNull(typeof(MusicPlayer).GetProperty("UserGain"));
- // Unity means the bed's design level, and the fader's scale is the same one as the others.
+ // It starts as background: −50 dB, and the ±5 dB steps are how it moves.
+ Assert.Equal(-50, MainViewModel.TraxDefaultDb);
+ Assert.Equal(5, MainViewModel.TraxStepDb);
Assert.Equal(0, GainDb.ToDb(1f), 6);
Assert.Contains("∞", GainDb.Label(GainDb.FloorDb)); // true silence, not "-60.0"
}