TASK 24 post-pause polish batch (creator's 8 review issues, 2026-08-15): (1) desktop/game audio volume slider + (2) mute were no-ops — the AudioMixer's gain Func seams defaulted to unity because the VM never passed them; new AudioGainProvider (Services/Audio/) wires MicMuted/MicVolume/GameMuted/GameAudioVolume into the mixer at construction, read live each mix tick, so sliders and mutes are stream-honest; the game bar also scales TRAX locally via new MusicPlayer.LocalGain (UpdateTraxLocalGain on every game volume/mute change + on load) so the creator HEARS the controls work on the music in the headphones. (3) TRAX played once then stopped — OnPlaybackStopped only looped on Position>=Length (unreliable for MediaFoundationReader); now any clean stop rewinds + replays, errors/explicit stops surface via PlaybackEnded; unused using dropped. (4+6) TRAX moved right of Socials in the footer's left cluster (centered under scenes/sources); the mic cluster is now MIC + meter + mute + volume. (5) mic source persists — LayoutStore gains a Settings key/value table (SaveMicSourceName/LoadMicSourceName); the VM saves on pick and restores before the mixer's first Start, so a restart reconnects the same vetted device (green) or reports it missing (yellow). (7) Backdrop's missing trashcan shifted the edit/eye icons right — new HiddenBoolToVisibilityConverter keeps the trash column reserved (Hidden, not Collapsed) so every source row's icons stay in fixed columns. (8) a 1px hairline with top/bottom padding separates scenes from sources in the left panel. Docs in the same commit: ai.md (gains honest + drive TRAX locally, TRAX loops, mic persistence, footer layout), TASKS.md TASK 24, HANDOFF.md shipped state, ViewModels/index.md. ONE integration test: AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback (real mixer + provider gains through the pipe harness: scaled loopback audible, silence after mute). Build 0 warnings, 197 tests passing

This commit is contained in:
2026-08-15 10:23:49 -07:00
parent 86fcd868a8
commit a0a8331569
11 changed files with 369 additions and 93 deletions
+38 -28
View File
@@ -33,6 +33,8 @@
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
<Helpers:NotNullToVisibilityConverter x:Key="NotNullToVis"
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
<Helpers:HiddenBoolToVisibilityConverter x:Key="HiddenBoolToVis"
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
<Helpers:EnumToBoolConverter x:Key="EnumToBool"
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
@@ -148,6 +150,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
@@ -205,8 +208,12 @@
</ListBox.ItemTemplate>
</Helpers:FocusPreservingListBox>
<!-- Visual separation between the two listboxes: a hairline
with breathing room on either side (the creator's fix). -->
<Rectangle Grid.Row="2" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"/>
<!-- Sources (for active scene) -->
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,8,0,0">
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="SOURCES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
<Button ToolTip="Add Source" Style="{StaticResource IconButton}" Margin="4,-4,0,0"
Click="AddSourceButton_Click">
@@ -226,7 +233,7 @@
</Button>
</StackPanel>
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="3" Background="Transparent" BorderThickness="0"
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="4" Background="Transparent" BorderThickness="0"
ItemsSource="{Binding ActiveScene.Elements}"
SelectedItem="{Binding SelectedElement, Mode=TwoWay}"
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
@@ -313,7 +320,7 @@
<Button Grid.Column="3" ToolTip="Remove Source"
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
Visibility="{Binding IsBackdrop, Converter={StaticResource InverseBoolToVis}}">
Visibility="{Binding IsBackdrop, Converter={StaticResource HiddenBoolToVis}}">
<Path Data="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
</Button>
@@ -322,7 +329,7 @@
</ListBox.ItemTemplate>
</Helpers:FocusPreservingListBox>
<TextBlock Grid.Row="3" Text="No sources yet" Foreground="#555" FontSize="12"
<TextBlock Grid.Row="4" Text="No sources yet" Foreground="#555" FontSize="12"
HorizontalAlignment="Center" VerticalAlignment="Center"
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
</Grid>
@@ -750,8 +757,9 @@
preview while a full-screen game is producing sound or
TRAX music is playing (the game audio detector). Meter
+ mute + volume control live here; desktop audio itself
is automatic WASAPI loopback, and since TASK 9 it rides
into the live mix (game volume + mute are stream-honest). -->
is automatic WASAPI loopback. Volume + mute are
stream-honest AND scale the TRAX music in the
headphones, so the creator hears the control work. -->
<Border HorizontalAlignment="Center" VerticalAlignment="Bottom"
Background="#D9000000" CornerRadius="6" Padding="12,8"
Margin="0,0,0,12"
@@ -855,12 +863,12 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Line 1: Social controls on the LEFT (under scenes/sources),
<!-- Line 1: Social controls + TRAX on the LEFT (under scenes/sources),
sound meter + volume control CENTERED beneath the preview panel.
Audio is KISS: desktop/game audio is automatic (WASAPI loopback
at unity); the mic meter is the creator's only footer audio
control (the game audio bar lives INSIDE the preview window,
overlaid at its bottom edge while a full-screen game is up). -->
Audio is KISS: desktop/game audio is automatic (WASAPI loopback);
the mic meter is the creator's only footer audio control (the
game audio bar lives INSIDE the preview window, overlaid at its
bottom edge while a full-screen game is up). -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220"/>
@@ -868,12 +876,14 @@
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<!-- Social controls: centered under the scenes/sources listboxes -->
<!-- Social controls + TRAX: centered under the scenes/sources
listboxes. TRAX rides the desktop/game audio channel, so
it lives in the left cluster, away from the mic meter. -->
<StackPanel Grid.Column="0" Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="{Binding OpenSocialDialogCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center"
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
ToolTip="Add or manage your social handles">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding SocialBarDotBrush}"
@@ -881,6 +891,21 @@
<TextBlock Text="Socials"/>
</StackPanel>
</Button>
<!-- TRAX (TASK 9): free background music. Dot: green = playing,
yellow = loaded/stopped, red = no track. Left-click toggles
play/pause (opens the picker when no track); right-click
always opens the picker. -->
<Button Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center"
ToolTip="{Binding TraxToolTip}"
Click="TraxButton_Click"
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
Margin="0,0,6,0" VerticalAlignment="Center"/>
<TextBlock Text="TRAX"/>
</StackPanel>
</Button>
</StackPanel>
<!-- Mic meter + volume: centered under the preview panel -->
@@ -896,21 +921,6 @@
<TextBlock Text="MIC"/>
</StackPanel>
</Button>
<!-- TRAX (TASK 9): free background music. Dot: green = playing,
yellow = loaded/stopped, red = no track. Left-click toggles
play/pause (opens the picker when no track); right-click
always opens the picker. -->
<Button Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
ToolTip="{Binding TraxToolTip}"
Click="TraxButton_Click"
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
Margin="0,0,6,0" VerticalAlignment="Center"/>
<TextBlock Text="TRAX"/>
</StackPanel>
</Button>
<!-- Sound meter: muted track with a ruler scale, zone tints at
the yellow (60%) and red (80%) starts. READ-ONLY realtime
level display (fill = live level scaled by volume). -->