Game audio bar is now ALWAYS visible (TASK 4 gating removed, 2026-08-15): the show/hide machinery kept hiding the desktop/game meter whenever no full-screen game with sound was up or no TRAX music played, which the creator read as the meter being lost. Deleted IGameAudioDetector/GameAudioDetector/GameAudioHysteresis (Services/), the VM's 250ms DispatcherTimer poll + OnGameAudioActiveChanged/OnGameAudioPollTick + IsGameAudioBarVisible + _gameAudioBarActive/_gameAudioTimer/_gameAudioDetector, and the two detector test files (GameAudioDetectorTests, GameAudioHysteresisTests); MainWindow.xaml drops the Visibility binding so the bar renders unconditionally. Desktop audio is just automatic WASAPI loopback now. Docs in the same commit: ai.md (audio section + Services inventory), Services/index.md, ViewModels/index.md, TASKS.md TASK 9 item 7 follow-up, HANDOFF.md shipped state + stale poll-timer landmine removed. Build 0 warnings, 189 tests passing (8 detector tests removed)

This commit is contained in:
2026-08-15 15:53:51 -07:00
parent cc71ed5066
commit 24f16b2cd9
12 changed files with 38 additions and 345 deletions
+2 -38
View File
@@ -28,7 +28,6 @@ public class MainViewModel : ViewModelBase
private readonly DispatcherTimer _liveTimer;
private readonly DispatcherTimer _volumeFlashTimer;
private readonly DispatcherTimer _gameVolumeFlashTimer;
private readonly DispatcherTimer _gameAudioTimer;
private Scene? _activeScene;
private SceneElement? _selectedElement;
@@ -50,7 +49,6 @@ public class MainViewModel : ViewModelBase
private double _gameVolume = 1.0;
private bool _gameMuted;
private double? _gameVolumeBeforeMute;
private bool _gameAudioBarActive;
// TRAX (TASK 9): free background music at a fixed quiet volume, playing to
// the default device so it rides the loopback into the stream. The dot is
@@ -122,7 +120,6 @@ public class MainViewModel : ViewModelBase
private readonly IFullScreenDetector _fullScreenDetector;
private readonly ScreenCaptureManager _screenCaptureManager;
private readonly ScreenCaptureSourceFactory _screenCaptureFactory;
private readonly IGameAudioDetector _gameAudioDetector;
private int? _lastForegroundFullScreenMonitor;
private CancellationTokenSource? _deactivateCts;
private ImageSource? _backdropImage;
@@ -491,14 +488,8 @@ public class MainViewModel : ViewModelBase
_ => "No mic connected — click to choose a microphone",
};
// ─── Game audio bar (desktop/game): visible only while a full-screen game
// ─── is producing sound (IGameAudioDetector). Meter + mute + volume mirror
// ─── the mic bar. ───
/// <summary>True while the game audio bar should be shown: a full-screen game
/// producing sound (IGameAudioDetector) OR background music playing (TASK 9)
/// — the meter bounces during scene setup even with no game up.</summary>
public bool IsGameAudioBarVisible => _gameAudioBarActive || IsMusicPlaying;
// ─── Game audio bar (desktop/game): always visible — a mirror of the mic bar
// ─── (meter + mute + volume). Desktop/game audio is automatic (WASAPI loopback). ───
/// <summary>True while the TRAX background track is playing.</summary>
public bool IsMusicPlaying
@@ -510,7 +501,6 @@ public class MainViewModel : ViewModelBase
{
OnPropertyChanged(nameof(TraxDotBrush));
OnPropertyChanged(nameof(TraxToolTip));
OnPropertyChanged(nameof(IsGameAudioBarVisible));
}
}
}
@@ -1202,13 +1192,6 @@ public class MainViewModel : ViewModelBase
foreach (var display in _fullScreenDetector.GetDisplays())
Displays.Add(display);
_gameAudioDetector = new GameAudioDetector(
() => _fullScreenDetector.GetForegroundFullScreenMonitorIndex(),
() => (float)GameAudioLevel);
_gameAudioDetector.IsGameAudioActiveChanged += OnGameAudioActiveChanged;
_gameAudioTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
_gameAudioTimer.Tick += OnGameAudioPollTick;
_gameAudioTimer.Start();
_screenCaptureFactory = new ScreenCaptureSourceFactory(
() => new WindowInteropHelper(System.Windows.Application.Current?.MainWindow).Handle);
_screenCaptureManager = new ScreenCaptureManager(
@@ -1546,7 +1529,6 @@ public class MainViewModel : ViewModelBase
{
_saveDebounce?.Stop();
SaveLayoutNow();
_gameAudioTimer.Stop();
_audioMixer.Dispose();
_framePump.Dispose();
_cameraManager.Dispose();
@@ -2261,24 +2243,6 @@ public class MainViewModel : ViewModelBase
}
}
private void OnGameAudioActiveChanged(bool active)
{
_gameAudioBarActive = active;
OnPropertyChanged(nameof(IsGameAudioBarVisible));
}
private void OnGameAudioPollTick(object? sender, EventArgs e)
{
try
{
_gameAudioDetector.Poll();
}
catch (Exception ex)
{
AppLog.Write($"Game audio detection failed: {ex.Message}");
}
}
// Scene-element → latest frame, for the live compositor. The map mirrors the
// preview: webcam by DeviceId, live captures (the backdrop) by CaptureKey,
// images/background by AssetId. A null frame leaves the element transparent.
+1 -1
View File
File diff suppressed because one or more lines are too long