Two gaps in what 7f89534 shipped, both caught by the creator's report that sound still
leads the lips:
- The mic delay had presets only (off / 150 / 200), so the value a slower camera needs —
roughly 175 ms of residual on top of the 175 ms already applied — could not be set at
all. Now a 0..500 ms slider with off / 175 / 350 presets.
- Nothing recorded which number was in force. The setting lives in the layout DB, so a
preset click left no trace in the log; "Audio live: started" now names it.
Confirmed meanwhile from the profile: Audio.MicSyncOffsetMs is 175, so the delay is being
applied — the 150 ms of digital silence at the head of the 12:22 track is the delay line
priming, which is what an applied delay looks like. The residual offset is the camera's
pipeline being slower than 175 ms, not a missing delay.
674 pass, 0 warnings.
304 lines
23 KiB
XML
304 lines
23 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="44"/>
|
||
<RowDefinition Height="*"/>
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Four bays (creator ruling 2026-11-01): MIC | GAME | TRAX | LIP-SYNC. The three
|
||
sound bays share equal width so their meters stay the same relative size; lip-sync
|
||
is narrow because its function is one slider and three presets. -->
|
||
<Grid x:Name="Bays" Grid.Row="1" Margin="8,4,8,6">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="0.45*"/>
|
||
</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="Auto"/>
|
||
</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.OnSurfaceVariant}"/>
|
||
</Grid>
|
||
<controls:SegmentMeter Grid.Row="1" Height="10" Margin="0,6,0,4" Orientation="Horizontal"
|
||
Level="{Binding MicMeterFraction}" Segments="24"/>
|
||
<DockPanel Grid.Row="2" Margin="0,6,0,0">
|
||
<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}"/>
|
||
<Grid VerticalAlignment="Center" ToolTip="0 dB is the ceiling — full input level, no boost beyond it">
|
||
<Slider Minimum="-60" Maximum="0" SmallChange="0.5" LargeChange="3"
|
||
Value="{Binding MicVolumeDb, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
</Grid>
|
||
</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"/>
|
||
</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}"/>
|
||
<Grid VerticalAlignment="Center" ToolTip="0 dB is the ceiling — full input level, no boost beyond it">
|
||
<Slider Minimum="-60" Maximum="0" SmallChange="0.5" LargeChange="3"
|
||
Value="{Binding GameVolumeDb, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
</Grid>
|
||
</DockPanel>
|
||
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- TRAX: its own bay and its own meter now (creator ruling 2026-11-01). It used to
|
||
be a strip inside the game bay because the desktop ladder "already carried it";
|
||
that hid whether the music was actually playing. -->
|
||
<Border Grid.Column="2" 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"/>
|
||
</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="TRAX" 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="{Binding TraxTrackLabel}" Cursor="Hand"
|
||
MouseLeftButtonUp="TraxLabel_MouseLeftButtonUp"
|
||
MouseRightButtonUp="TraxLabel_MouseRightButtonUp"/>
|
||
</Grid>
|
||
|
||
<controls:SegmentMeter Grid.Row="1" Height="10" Margin="0,6,0,4" Orientation="Horizontal"
|
||
Level="{Binding TraxMeterFraction}" Segments="24"/>
|
||
|
||
<DockPanel Grid.Row="2" VerticalAlignment="Center">
|
||
<Button DockPanel.Dock="Left" Width="24" Height="20" Margin="0,0,6,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 DockPanel.Dock="Right" Orientation="Horizontal" 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.OnSurfaceVariant}"
|
||
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>
|
||
</DockPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- LIP-SYNC (narrow: one slider, three presets) -->
|
||
<Border Grid.Column="3" Background="{StaticResource Brush.SurfaceContainerLow}" CornerRadius="{StaticResource Radius.Sm}" Margin="4,0,0,0" Padding="10,6">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
<!-- Row alignment across the dock (creator ruling 2026-11-01): every bay is
|
||
header / meter / controls, so the ladders sit on one horizontal line and the
|
||
volume bars on another. Lip-sync has no ladder, so it holds an empty row the
|
||
height of one (10px meter + 6/4 margins = 20) to keep its slider level with
|
||
the other three. -->
|
||
<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.OnSurfaceVariant}" Text="{Binding SyncOffsetLabel}"/>
|
||
</Grid>
|
||
<Border Grid.Row="1" Height="20"/>
|
||
<StackPanel Grid.Row="2">
|
||
<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>
|
||
<!-- Microphone-only delay: the voice waits for the camera's picture. The
|
||
whole-mix slider above would drag desktop and alert audio with it. -->
|
||
<Grid Margin="0,5,0,0">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
</Grid.ColumnDefinitions>
|
||
<Border Background="{StaticResource Brush.SurfaceContainerHighest}" CornerRadius="{StaticResource Radius.Sm}" Padding="5,1">
|
||
<TextBlock Text="MIC" 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.OnSurfaceVariant}" Text="{Binding MicSyncOffsetLabel}"/>
|
||
</Grid>
|
||
<Slider Minimum="0" Maximum="500" SmallChange="5" LargeChange="25"
|
||
TickFrequency="5" IsSnapToTickEnabled="True"
|
||
Value="{Binding MicSyncOffsetMs, Mode=TwoWay}" VerticalAlignment="Center"/>
|
||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0" HorizontalAlignment="Right">
|
||
<Button Content="off" Width="26" Height="16" FontSize="9" Padding="0" Margin="0,0,4,0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="MicSyncPreset_Click" Tag="0"/>
|
||
<Button Content="175" Width="30" Height="16" FontSize="9" Padding="0" Margin="0,0,4,0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="MicSyncPreset_Click" Tag="175"/>
|
||
<Button Content="350" Width="30" Height="16" FontSize="9" Padding="0"
|
||
Style="{StaticResource YtButtonSecondary}" Click="MicSyncPreset_Click" Tag="350"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<!-- Master strip: always visible, even collapsed. Rebuilt 2026-11-01 — the meter is
|
||
CENTRED under the preview and is the biggest one on screen; MASTER's own label is
|
||
larger than the bay labels. The kbps / fps / dropped counters that used to end this
|
||
line are gone: the live readouts already sit above the preview. -->
|
||
<Border x:Name="MasterStrip" Grid.Row="0" Background="{StaticResource Brush.SurfaceContainerLowest}" Padding="12,0">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<!-- Left: the imbalance flag, the one case that silently ruins a mix. -->
|
||
<Border Grid.Column="0" Margin="0,0,8,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>
|
||
|
||
<!-- Centre: MASTER + the long meter + its reading. The ladder stretches with the
|
||
window between 420 and 760 px — always longer than the 320-capped bay meters. -->
|
||
<Grid Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<TextBlock Grid.Column="0" Text="MASTER" FontSize="12" FontWeight="Bold" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurface}"/>
|
||
<controls:SegmentMeter Grid.Column="1" Height="14" Margin="12,0,0,0" MinWidth="420" MaxWidth="900"
|
||
Orientation="Horizontal" Segments="32"
|
||
Level="{Binding MasterMeterFraction}" VerticalAlignment="Center"/>
|
||
<TextBlock Grid.Column="2" Width="72" Margin="10,0,0,0" FontSize="12"
|
||
FontFamily="{StaticResource FontFamily.Mono}" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurface}"
|
||
Text="{Binding MasterLevelLabel}"/>
|
||
</Grid>
|
||
|
||
<!-- Right: MONO (accessibility, remembered) + the session clock. -->
|
||
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
||
<ToggleButton 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>
|
||
<TextBlock FontSize="11" FontFamily="{StaticResource FontFamily.Mono}" Margin="12,0,0,0"
|
||
VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
||
Text="{Binding CurrentHealth.StreamDuration, StringFormat={}{0:hh\:mm\:ss}}"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
</Border>
|
||
</UserControl>
|