Creator ruling (2026-10-30): TRAX is background music and is controlled separately from desktop and mic — so KISS it: - TRAX loses its own ladder and slider. It is one number plus ±5 dB buttons, disabled at the ends (−60 dB floor, +6 dB ceiling). - It STARTS at −50 dB, taken from the creator's own meter readings, and moves in 5 dB clicks. If that proves inaudible on their setup, six clicks lands at −20 — the buttons exist for exactly that, and `TraxDefaultDb` is one constant. - Layout follows: Desktop's volume now sits directly beneath its ladder, and the TRAX row (name, play/pause, level) sits beneath that, inside the GAME bay. The bays are MIC | GAME+TRAX | LIP-SYNC. - The metering tap on the player stays (it is how "is the music producing sound" is answered) but has no ladder of its own; the desktop bed already shows the music. Guards updated: TraxBayTests now asserts the stepper wiring and that the row has NO SegmentMeter; GameBayTests asserts the desktop fader sits below the desktop ladder.
250 lines
19 KiB
XML
250 lines
19 KiB
XML
<UserControl x:Class="ytLive.Controls.AudioDock"
|
||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
xmlns:controls="clr-namespace:ytLive.Controls">
|
||
<!-- The sound bars. Replaces the empty bottom-bar spacer; collapses to the telemetry strip
|
||
on short windows (AudioDockLayout decides the height, not this file). -->
|
||
<Border x:Name="DockRoot" Background="{StaticResource Brush.SurfaceContainer}"
|
||
BorderBrush="{StaticResource Brush.Outline}" BorderThickness="0,1,0,0">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<!-- Master FIRST: during a live session attention is on the preview, so the
|
||
master strip sits against it and the bays hang below (creator, 2026-10-30). -->
|
||
<RowDefinition Height="36"/>
|
||
<RowDefinition Height="*"/>
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Four bays: MIC | GAME | TRAX | SYNC. Filled in by the bay units. -->
|
||
<Grid x:Name="Bays" Grid.Row="1" Margin="8,4,8,6">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="1.4*"/>
|
||
<ColumnDefinition Width="*"/>
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<!-- MIC -->
|
||
<Border Grid.Column="0" Background="{StaticResource Brush.SurfaceContainerLow}" CornerRadius="{StaticResource Radius.Sm}" Margin="0,0,4,0" Padding="10,6">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="*"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0" Cursor="Hand" ToolTip="Change microphone" MouseLeftButtonUp="MicLabel_MouseLeftButtonUp">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
</Grid.ColumnDefinitions>
|
||
<Border Background="{StaticResource Brush.SurfaceContainerHighest}" CornerRadius="{StaticResource Radius.Sm}" Padding="5,1">
|
||
<TextBlock Text="MIC 1" FontSize="9" FontWeight="Bold" Foreground="{StaticResource Brush.OnSurfaceVariant}"/>
|
||
</Border>
|
||
<TextBlock Grid.Column="1" Margin="6,0,0,0" FontSize="11" VerticalAlignment="Center"
|
||
TextTrimming="CharacterEllipsis" Text="{Binding MicSourceName, TargetNullValue='No microphone'}"
|
||
Foreground="{StaticResource Brush.OnSurface}"/>
|
||
</Grid>
|
||
<controls:SegmentMeter Grid.Row="1" Height="10" Margin="0,6,0,0" Orientation="Horizontal"
|
||
Level="{Binding MicMeterFraction}" Segments="24"/>
|
||
<DockPanel Grid.Row="2" Margin="0,6,0,0" VerticalAlignment="Bottom">
|
||
<Button DockPanel.Dock="Left" Width="24" Height="20" Margin="0,0,6,0"
|
||
Style="{StaticResource YtButtonSecondary}" Padding="0"
|
||
Command="{Binding ToggleMicMuteCommand}" ToolTip="{Binding MicMuteText}">
|
||
<Ellipse Width="9" Height="9" Fill="{Binding MicStatusBrush}"/>
|
||
</Button>
|
||
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right" FontSize="10"
|
||
FontFamily="{StaticResource FontFamily.Mono}" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" Text="{Binding MicVolumeDbLabel}"/>
|
||
<Slider Minimum="-60" Maximum="6" SmallChange="0.5" LargeChange="3"
|
||
Value="{Binding MicVolumeDb, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
</DockPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- GAME / PC, then TRAX beneath it: meter -> desktop volume -> TRAX line -->
|
||
<Border Grid.Column="1" Background="{StaticResource Brush.SurfaceContainerLow}" CornerRadius="{StaticResource Radius.Sm}" Margin="4,0" Padding="10,6">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<Border Background="{StaticResource Brush.SurfaceContainerHighest}" CornerRadius="{StaticResource Radius.Sm}" Padding="5,1">
|
||
<TextBlock Text="GAME / PC" FontSize="9" FontWeight="Bold" Foreground="{StaticResource Brush.OnSurfaceVariant}"/>
|
||
</Border>
|
||
<TextBlock Grid.Column="1" Margin="6,0,0,0" FontSize="11" VerticalAlignment="Center"
|
||
TextTrimming="CharacterEllipsis" Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
||
Text="Desktop loopback · incl. TRAX"
|
||
ToolTip="Everything playing on the desktop — including TRAX, which rides the same device."/>
|
||
<Border Grid.Column="2" Background="{StaticResource Brush.SurfaceContainerHighest}"
|
||
CornerRadius="{StaticResource Radius.Sm}" Padding="5,1"
|
||
Visibility="{Binding IsGameDucking, Converter={StaticResource BoolToVis}}">
|
||
<TextBlock FontSize="9" FontWeight="Bold" FontFamily="{StaticResource FontFamily.Mono}"
|
||
Foreground="{StaticResource Brush.Secondary}" Text="{Binding GameDuckLabel}"/>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<controls:SegmentMeter Grid.Row="1" Height="10" Margin="0,6,0,4" Orientation="Horizontal"
|
||
Level="{Binding GameMeterFraction}" Segments="24"/>
|
||
|
||
<!-- Desktop volume, directly beneath its meter. -->
|
||
<DockPanel Grid.Row="2" VerticalAlignment="Center">
|
||
<Button DockPanel.Dock="Left" Width="24" Height="20" Margin="0,0,6,0"
|
||
Style="{StaticResource YtButtonSecondary}" Padding="0"
|
||
Command="{Binding ToggleGameMuteCommand}" ToolTip="Mute desktop audio">
|
||
<Ellipse Width="9" Height="9" Fill="{Binding GameMeterBrush}"/>
|
||
</Button>
|
||
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right" FontSize="10"
|
||
FontFamily="{StaticResource FontFamily.Mono}" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" Text="{Binding GameVolumeDbLabel}"/>
|
||
<Slider Minimum="-60" Maximum="6" SmallChange="0.5" LargeChange="3"
|
||
Value="{Binding GameVolumeDb, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
</DockPanel>
|
||
|
||
<!-- TRAX: background music — one number, two buttons. No meter: it is
|
||
background, and the desktop ladder already carries it. -->
|
||
<Border Grid.Row="3" Margin="0,4,0,0" Padding="6,3" CornerRadius="{StaticResource Radius.Sm}"
|
||
Background="{StaticResource Brush.SurfaceContainerLowest}">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<TextBlock Text="TRAX" FontSize="9" FontWeight="Bold" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}"/>
|
||
<TextBlock Grid.Column="1" Margin="6,0,0,0" FontSize="10" VerticalAlignment="Center"
|
||
TextTrimming="CharacterEllipsis" Foreground="{StaticResource Brush.OnSurface}"
|
||
Text="{Binding TraxTrackLabel}" Cursor="Hand"
|
||
MouseLeftButtonUp="TraxLabel_MouseLeftButtonUp"
|
||
MouseRightButtonUp="TraxLabel_MouseRightButtonUp"/>
|
||
<Button Grid.Column="2" Width="20" Height="16" Margin="4,0,0,0" Padding="0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="TraxPlayPause_Click"
|
||
ToolTip="{Binding TraxToolTip}">
|
||
<TextBlock FontSize="9" Foreground="{StaticResource Brush.OnSurface}">
|
||
<TextBlock.Style>
|
||
<Style TargetType="TextBlock">
|
||
<Setter Property="Text" Value="▶"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsMusicPlaying}" Value="True">
|
||
<Setter Property="Text" Value="❚❚"/>
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</TextBlock.Style>
|
||
</TextBlock>
|
||
</Button>
|
||
<StackPanel Grid.Column="3" Orientation="Horizontal" Margin="6,0,0,0" VerticalAlignment="Center">
|
||
<Button Content="−" Width="18" Height="16" Padding="0" FontSize="11"
|
||
Style="{StaticResource YtButtonSecondary}" Click="TraxStepDown_Click"
|
||
IsEnabled="{Binding CanLowerTrax}" ToolTip="5 dB quieter"/>
|
||
<TextBlock Width="52" TextAlignment="Center" FontSize="10" VerticalAlignment="Center"
|
||
FontFamily="{StaticResource FontFamily.Mono}"
|
||
Foreground="{StaticResource Brush.Tertiary}"
|
||
Text="{Binding TraxVolumeDbLabel}"/>
|
||
<Button Content="+" Width="18" Height="16" Padding="0" FontSize="11"
|
||
Style="{StaticResource YtButtonSecondary}" Click="TraxStepUp_Click"
|
||
IsEnabled="{Binding CanRaiseTrax}" ToolTip="5 dB louder"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- LIP-SYNC -->
|
||
<Border Grid.Column="2" Background="{StaticResource Brush.SurfaceContainerLow}" CornerRadius="{StaticResource Radius.Sm}" Margin="4,0,0,0" Padding="10,6">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="*"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
</Grid.ColumnDefinitions>
|
||
<Border Background="{StaticResource Brush.SurfaceContainerHighest}" CornerRadius="{StaticResource Radius.Sm}" Padding="5,1">
|
||
<TextBlock Text="LIP-SYNC" FontSize="9" FontWeight="Bold" Foreground="{StaticResource Brush.OnSurfaceVariant}"/>
|
||
</Border>
|
||
<TextBlock Grid.Column="1" Margin="6,0,0,0" FontSize="10" VerticalAlignment="Center"
|
||
FontFamily="{StaticResource FontFamily.Mono}" TextAlignment="Right"
|
||
Foreground="{StaticResource Brush.Tertiary}" Text="{Binding SyncOffsetLabel}"/>
|
||
</Grid>
|
||
<StackPanel Grid.Row="1" VerticalAlignment="Bottom">
|
||
<Slider Minimum="-200" Maximum="200" SmallChange="5" LargeChange="25"
|
||
TickFrequency="5" IsSnapToTickEnabled="True"
|
||
Value="{Binding AudioSyncOffsetMs, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0" HorizontalAlignment="Right">
|
||
<Button Content="0" Width="26" Height="16" FontSize="9" Padding="0" Margin="0,0,4,0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="SyncPreset_Click" Tag="0"/>
|
||
<Button Content="+50" Width="30" Height="16" FontSize="9" Padding="0" Margin="0,0,4,0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="SyncPreset_Click" Tag="50"/>
|
||
<Button Content="+100" Width="34" Height="16" FontSize="9" Padding="0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="SyncPreset_Click" Tag="100"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<!-- Master strip: always visible, even collapsed — it is the telemetry line. -->
|
||
<Border x:Name="MasterStrip" Grid.Row="0" Background="{StaticResource Brush.SurfaceContainerLowest}" Padding="12,0">
|
||
<DockPanel VerticalAlignment="Center">
|
||
<TextBlock DockPanel.Dock="Left" Text="MASTER" FontSize="10" FontWeight="SemiBold"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
|
||
|
||
<!-- One ladder for the mix, plus the imbalance flag: two ladders would cost the
|
||
width that the telemetry needs, and the flag is the case that matters. -->
|
||
<controls:SegmentMeter DockPanel.Dock="Left" Width="120" Height="8" Margin="10,0,0,0"
|
||
Orientation="Horizontal" Segments="24"
|
||
Level="{Binding MasterMeterFraction}" VerticalAlignment="Center"/>
|
||
<TextBlock DockPanel.Dock="Left" Width="62" Margin="6,0,0,0" FontSize="10"
|
||
FontFamily="{StaticResource FontFamily.Mono}" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
||
Text="{Binding MasterLevelLabel}"/>
|
||
|
||
<Border DockPanel.Dock="Left" Margin="6,0,0,0" Padding="5,1" VerticalAlignment="Center"
|
||
CornerRadius="{StaticResource Radius.Sm}" Background="{StaticResource Brush.SurfaceContainerHigh}"
|
||
Visibility="{Binding MasterImbalance, Converter={StaticResource BoolToVis}}"
|
||
ToolTip="Left and right differ by more than 6 dB — one side may be inaudible.">
|
||
<TextBlock Text="L/R" FontSize="9" FontWeight="Bold" FontFamily="{StaticResource FontFamily.Mono}"
|
||
Foreground="{StaticResource Brush.Attention}"/>
|
||
</Border>
|
||
|
||
<!-- MONO: the accessibility switch, one click, remembered. -->
|
||
<ToggleButton DockPanel.Dock="Left" Margin="10,0,0,0" Height="20" Padding="8,0"
|
||
Style="{StaticResource SegmentToggle}" IsChecked="{Binding MonoOutput, Mode=TwoWay}"
|
||
ToolTip="Fold the output to mono — both ears hear everything">
|
||
<TextBlock Text="MONO" FontSize="10" FontWeight="Bold"
|
||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
|
||
</ToggleButton>
|
||
|
||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||
<TextBlock FontSize="10" FontFamily="{StaticResource FontFamily.Mono}" Margin="0,0,12,0"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}">
|
||
<Run Text="{Binding CurrentHealth.CurrentBitrate, StringFormat={}{0:0} kbps}"/>
|
||
</TextBlock>
|
||
<TextBlock FontSize="10" FontFamily="{StaticResource FontFamily.Mono}" Margin="0,0,12,0"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}">
|
||
<Run Text="{Binding CurrentHealth.FPS, StringFormat={}{0:0} fps}"/>
|
||
</TextBlock>
|
||
<TextBlock FontSize="10" FontFamily="{StaticResource FontFamily.Mono}" Margin="0,0,12,0"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}">
|
||
<Run Text="drop "/><Run Text="{Binding CurrentHealth.DroppedFrames}"/>
|
||
</TextBlock>
|
||
<TextBlock FontSize="10" FontFamily="{StaticResource FontFamily.Mono}"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
||
Text="{Binding CurrentHealth.StreamDuration, StringFormat={}{0:hh\:mm\:ss}}"/>
|
||
</StackPanel>
|
||
</DockPanel>
|
||
</Border>
|
||
</Grid>
|
||
</Border>
|
||
</UserControl>
|