From 30a193b2c9bc328adea46a1aef2dfc22df34fb15 Mon Sep 17 00:00:00 2001 From: gramps Date: Tue, 18 Aug 2026 08:44:21 -0700 Subject: [PATCH] TRAX: error display, retain track on failure, gray idle dot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Error text shows in red inside the desktop audio meter bar (scrollable) when TRAX load or playback fails — user gets feedback, not just a silent red dot. 2. LoadTrax no longer nulls _music on error — the track path is retained so the user sees what failed and can retry. 3. OnTraxPlaybackEnded now receives the NAudio exception and surfaces it. 4. TRAX dot is green (playing) or gray (idle/no track) — no more yellow or red states. 207 tests, 0 warnings. --- MainWindow.xaml | 6 ++++++ Services/Audio/MusicPlayer.cs | 4 ++-- ViewModels/MainViewModel.cs | 28 ++++++++++++++++++++-------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/MainWindow.xaml b/MainWindow.xaml index 249c282..e0e4173 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -924,6 +924,12 @@ VerticalAlignment="Stretch"/> + + + Raised when playback ends or is stopped (not on natural-end loop). - public event Action? PlaybackEnded; + public event Action? PlaybackEnded; public string? TrackPath { get; private set; } @@ -122,6 +122,6 @@ public sealed class MusicPlayer : IDisposable return; } - PlaybackEnded?.Invoke(); + PlaybackEnded?.Invoke(e.Exception); } } diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index c793651..53b6536 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -476,6 +476,7 @@ public class MainViewModel : ViewModelBase // ─── Mic status dot (the MIC button) ─── private static readonly SolidColorBrush MicProblemBrush = CreateBrush("#f1c40f"); + private static readonly SolidColorBrush TraxIdleBrush = CreateBrush("#555"); /// Mic connection state, driven by the mixer's MicConnected/MicFailed /// events and the startup device check (see StartMicCaptureAsync). @@ -525,12 +526,19 @@ public class MainViewModel : ViewModelBase } } - /// TRAX status dot: green = playing, yellow = loaded/stopped, red = no track. + private string? _traxError; + + /// Error message from the last TRAX load/playback failure; null = no error. + public string? TraxError + { + get => _traxError; + private set => SetProperty(ref _traxError, value); + } + + /// TRAX status dot: green = playing, gray = idle or no track. public SolidColorBrush TraxDotBrush => _musicPlayer.IsPlaying ? BarOnBrush - : _music != null && !string.IsNullOrWhiteSpace(_music.TrackPath) - ? MicProblemBrush - : BarOffBrush; + : TraxIdleBrush; public string TraxToolTip => _musicPlayer.TrackName != null ? $"TRAX: {_musicPlayer.TrackName} — left-click {(IsMusicPlaying ? "pauses" : "plays")}, right-click chooses a track" @@ -574,13 +582,14 @@ public class MainViewModel : ViewModelBase _musicPlayer.Load(_music.TrackPath); UpdateTraxLocalGain(); _music.IsEnabled = true; + TraxError = null; if (play) _musicPlayer.Play(); } catch (Exception ex) { AppLog.Write($"TRAX: failed to load '{_music.TrackPath}': {ex.Message}"); - _music = null; + TraxError = $"Failed to load: {Path.GetFileName(_music.TrackPath)} — {ex.Message}"; } RefreshTraxUi(); ScheduleSave(); @@ -590,10 +599,13 @@ public class MainViewModel : ViewModelBase private void PauseTrax() => _musicPlayer.Pause(); - private void OnTraxPlaybackEnded() + private void OnTraxPlaybackEnded(Exception? ex) { - // Natural end loops inside the player; here only an explicit stop or a - // device failure lands — flip the dot back to loaded/stopped. + if (ex != null) + { + AppLog.Write($"TRAX: playback error: {ex.Message}"); + TraxError = $"Playback error: {ex.Message}"; + } RefreshTraxUi(); }