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(); }