From be2c704654a043218fbeb7959abf101a0ab8f61a Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 4 Jul 2021 23:17:30 -0700 Subject: [PATCH 01/33] Make SoundOutput have events --- RedBookPlayer/GUI/PlayerView.xaml.cs | 22 +- RedBookPlayer/GUI/PlayerViewModel.cs | 119 +++++---- RedBookPlayer/Hardware/Player.cs | 361 ++++++++++++++++++++------ RedBookPlayer/Hardware/SoundOutput.cs | 84 ++++-- 4 files changed, 412 insertions(+), 174 deletions(-) diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs index 7dabeae..bdb1ecc 100644 --- a/RedBookPlayer/GUI/PlayerView.xaml.cs +++ b/RedBookPlayer/GUI/PlayerView.xaml.cs @@ -193,8 +193,6 @@ namespace RedBookPlayer.GUI if (_digits[i] != null) _digits[i].Source = GetBitmap(digitString[i]); } - - PlayerViewModel?.UpdateView(); }); } @@ -211,13 +209,19 @@ namespace RedBookPlayer.GUI await LoadImage(path); } - public void PlayButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = true; + public void PlayButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Play(); - public void PauseButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = false; + public void PauseButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Pause(); - public void PlayPauseButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = !(PlayerViewModel.Playing ?? false); + public void PlayPauseButton_Click(object sender, RoutedEventArgs e) + { + if(PlayerViewModel.Playing == true) + PlayerViewModel.Pause(); + else + PlayerViewModel.Play(); + } - public void StopButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = null; + public void StopButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Stop(); public void NextTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextTrack(); @@ -237,11 +241,11 @@ namespace RedBookPlayer.GUI public void MuteToggleButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ToggleMute(); - public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = true; + public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(true); - public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = false; + public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(false); - public void EnableDisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = !PlayerViewModel.ApplyDeEmphasis; + public void EnableDisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(!PlayerViewModel.ApplyDeEmphasis); #endregion } diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 4030ba5..4162012 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -1,7 +1,6 @@ +using System.ComponentModel; using System.Linq; -using Aaru.CommonTypes.Enums; using ReactiveUI; -using RedBookPlayer.Discs; using RedBookPlayer.Hardware; namespace RedBookPlayer.GUI @@ -18,7 +17,7 @@ namespace RedBookPlayer.GUI /// private int? _lastVolume = null; - #region Player Status + #region Player Passthrough /// /// Indicate if the model is ready to be used @@ -26,29 +25,12 @@ namespace RedBookPlayer.GUI public bool Initialized => _player?.Initialized ?? false; /// - /// Indicate the player state + /// Indicate if the output is playing /// public bool? Playing { - get => _player?.Playing ?? false; - set - { - if(_player != null) - _player.Playing = value; - } - } - - /// - /// Indicate the current playback volume - /// - public int Volume - { - get => _player?.Volume ?? 100; - set - { - if(_player != null) - _player.Volume = value; - } + get => _playing; + private set => this.RaiseAndSetIfChanged(ref _playing, value); } /// @@ -56,14 +38,23 @@ namespace RedBookPlayer.GUI /// public bool ApplyDeEmphasis { - get => _player?.ApplyDeEmphasis ?? false; - set - { - if(_player != null) - _player.ApplyDeEmphasis = value; - } + get => _applyDeEmphasis; + private set => this.RaiseAndSetIfChanged(ref _applyDeEmphasis, value); } + /// + /// Current playback volume + /// + public int Volume + { + get => _volume; + set => this.RaiseAndSetIfChanged(ref _volume, value); + } + + private bool? _playing; + private bool _applyDeEmphasis; + private int _volume; + #endregion #region Model-Provided Playback Information @@ -145,11 +136,29 @@ namespace RedBookPlayer.GUI // Create and attempt to initialize new Player _player = new Player(path, autoPlay, defaultVolume); if(Initialized) - UpdateView(); + { + _player.PropertyChanged += PlayerStateChanged; + PlayerStateChanged(this, null); + } } #region Playback + /// + /// Begin playback + /// + public void Play() => _player.Play(); + + /// + /// Pause current playback + /// + public void Pause() => _player.Pause(); + + /// + /// Stop current playback + /// + public void Stop() => _player.Stop(); + /// /// Move to the next playable track /// @@ -193,7 +202,7 @@ namespace RedBookPlayer.GUI public string GenerateDigitString() { // If the disc isn't initialized, return all '-' characters - if(_player?.OpticalDisc == null || !_player.OpticalDisc.Initialized) + if(_player?.Initialized != true) return string.Empty.PadLeft(20, '-'); // Otherwise, take the current time into account @@ -201,24 +210,30 @@ namespace RedBookPlayer.GUI int[] numbers = new int[] { - _player.OpticalDisc.CurrentTrackNumber + 1, - _player.OpticalDisc.CurrentTrackIndex, + _player.CurrentTrackNumber + 1, + _player.CurrentTrackIndex, (int)(sectorTime / (75 * 60)), (int)(sectorTime / 75 % 60), (int)(sectorTime % 75), - _player.OpticalDisc.TotalTracks, - _player.OpticalDisc.TotalIndexes, + _player.TotalTracks, + _player.TotalIndexes, - (int)(_player.OpticalDisc.TotalTime / (75 * 60)), - (int)(_player.OpticalDisc.TotalTime / 75 % 60), - (int)(_player.OpticalDisc.TotalTime % 75), + (int)(_player.TotalTime / (75 * 60)), + (int)(_player.TotalTime / 75 % 60), + (int)(_player.TotalTime % 75), }; return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); } + /// + /// Set de-emphasis status + /// + /// + public void SetDeEmphasis(bool apply) => _player?.SetDeEmphasis(apply); + /// /// Temporarily mute playback /// @@ -239,30 +254,24 @@ namespace RedBookPlayer.GUI /// /// Update the UI from the internal player /// - public void UpdateView() + private void PlayerStateChanged(object sender, PropertyChangedEventArgs e) { if(_player?.Initialized != true) return; + Playing = _player.Playing; + ApplyDeEmphasis = _player.ApplyDeEmphasis; + Volume = _player.Volume; + CurrentSector = _player.GetCurrentSectorTime(); - TotalSectors = _player.OpticalDisc.TotalTime; + TotalSectors = _player.TotalTime; - HiddenTrack = _player.OpticalDisc.TimeOffset > 150; + HiddenTrack = _player.HiddenTrack; - if(_player.OpticalDisc is CompactDisc compactDisc) - { - QuadChannel = compactDisc.QuadChannel; - IsDataTrack = compactDisc.IsDataTrack; - CopyAllowed = compactDisc.CopyAllowed; - TrackHasEmphasis = compactDisc.TrackHasEmphasis; - } - else - { - QuadChannel = false; - IsDataTrack = _player.OpticalDisc.TrackType != TrackType.Audio; - CopyAllowed = false; - TrackHasEmphasis = false; - } + QuadChannel = _player.QuadChannel; + IsDataTrack = _player.IsDataTrack; + CopyAllowed = _player.CopyAllowed; + TrackHasEmphasis = _player.TrackHasEmphasis; } #endregion diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 2850f89..3c72fa6 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -1,68 +1,141 @@ using System; +using System.ComponentModel; using System.IO; +using Aaru.CommonTypes.Enums; using Aaru.DiscImages; using Aaru.Filters; +using ReactiveUI; using RedBookPlayer.Discs; namespace RedBookPlayer.Hardware { - public class Player + public class Player : ReactiveObject { - #region Public Fields - /// /// Indicate if the player is ready to be used /// public bool Initialized { get; private set; } = false; - /// - /// OpticalDisc object - /// - public OpticalDisc OpticalDisc { get; private set; } + #region OpticalDisc Passthrough /// - /// Indicate if the disc is playing + /// Current track number /// - public bool? Playing + public int CurrentTrackNumber { - get => _soundOutput?.Playing; - set - { - if(OpticalDisc == null || !OpticalDisc.Initialized) - return; - - // If the playing state has not changed, do nothing - if(value == _soundOutput?.Playing) - return; - - if(value == true) - { - _soundOutput.Play(); - OpticalDisc.SetTotalIndexes(); - } - else if(value == false) - { - _soundOutput.Stop(); - } - else - { - _soundOutput.Stop(); - OpticalDisc.LoadFirstTrack(); - } - } + get => _currentTrackNumber; + private set => this.RaiseAndSetIfChanged(ref _currentTrackNumber, value); } /// - /// Indicate the current playback volume + /// Current track index /// - public int Volume + public ushort CurrentTrackIndex { - get => _soundOutput?.Volume ?? 100; - set - { - if(_soundOutput != null) - _soundOutput.Volume = value; - } + get => _currentTrackIndex; + private set => this.RaiseAndSetIfChanged(ref _currentTrackIndex, value); + } + + /// + /// Current sector number + /// + public ulong CurrentSector + { + get => _currentSector; + private set => this.RaiseAndSetIfChanged(ref _currentSector, value); + } + + /// + /// Represents if the disc has a hidden track + /// + public bool HiddenTrack + { + get => _hasHiddenTrack; + private set => this.RaiseAndSetIfChanged(ref _hasHiddenTrack, value); + } + + /// + /// Represents the 4CH flag [CompactDisc only] + /// + public bool QuadChannel + { + get => _quadChannel; + private set => this.RaiseAndSetIfChanged(ref _quadChannel, value); + } + + /// + /// Represents the DATA flag [CompactDisc only] + /// + public bool IsDataTrack + { + get => _isDataTrack; + private set => this.RaiseAndSetIfChanged(ref _isDataTrack, value); + } + + /// + /// Represents the DCP flag [CompactDisc only] + /// + public bool CopyAllowed + { + get => _copyAllowed; + private set => this.RaiseAndSetIfChanged(ref _copyAllowed, value); + } + + /// + /// Represents the PRE flag [CompactDisc only] + /// + public bool TrackHasEmphasis + { + get => _trackHasEmphasis; + private set => this.RaiseAndSetIfChanged(ref _trackHasEmphasis, value); + } + + /// + /// Represents the total tracks on the disc + /// + public int TotalTracks => _opticalDisc.TotalTracks; + + /// + /// Represents the total indices on the disc + /// + public int TotalIndexes => _opticalDisc.TotalIndexes; + + /// + /// Total sectors in the image + /// + public ulong TotalSectors => _opticalDisc.TotalSectors; + + /// + /// Represents the time adjustment offset for the disc + /// + public ulong TimeOffset => _opticalDisc.TimeOffset; + + /// + /// Represents the total playing time for the disc + /// + public ulong TotalTime => _opticalDisc.TotalTime; + + private int _currentTrackNumber; + private ushort _currentTrackIndex; + private ulong _currentSector; + + private bool _hasHiddenTrack; + private bool _quadChannel; + private bool _isDataTrack; + private bool _copyAllowed; + private bool _trackHasEmphasis; + + #endregion + + #region SoundOutput Passthrough + + /// + /// Indicate if the output is playing + /// + public bool? Playing + { + get => _playing; + private set => this.RaiseAndSetIfChanged(ref _playing, value); } /// @@ -70,10 +143,23 @@ namespace RedBookPlayer.Hardware /// public bool ApplyDeEmphasis { - get => _soundOutput?.ApplyDeEmphasis ?? false; - set => _soundOutput?.SetDeEmphasis(value); + get => _applyDeEmphasis; + private set => this.RaiseAndSetIfChanged(ref _applyDeEmphasis, value); } + /// + /// Current playback volume + /// + public int Volume + { + get => _volume; + set => this.RaiseAndSetIfChanged(ref _volume, value); + } + + private bool? _playing; + private bool _applyDeEmphasis; + private int _volume; + #endregion #region Private State Variables @@ -81,7 +167,12 @@ namespace RedBookPlayer.Hardware /// /// Sound output handling class /// - public SoundOutput _soundOutput; + private readonly SoundOutput _soundOutput; + + /// + /// OpticalDisc object + /// + private readonly OpticalDisc _opticalDisc; #endregion @@ -96,8 +187,8 @@ namespace RedBookPlayer.Hardware // Set the internal state for initialization Initialized = false; _soundOutput = new SoundOutput(); - _soundOutput.ApplyDeEmphasis = false; - OpticalDisc = null; + _soundOutput.SetDeEmphasis(false); + _opticalDisc = null; try { @@ -112,7 +203,7 @@ namespace RedBookPlayer.Hardware image.Open(filter); // Generate and instantiate the disc - OpticalDisc = OpticalDiscFactory.GenerateFromImage(image, autoPlay); + _opticalDisc = OpticalDiscFactory.GenerateFromImage(image, autoPlay); } catch { @@ -121,32 +212,88 @@ namespace RedBookPlayer.Hardware } // Initialize the sound output - _soundOutput.Init(OpticalDisc, autoPlay, defaultVolume); + _soundOutput.Init(_opticalDisc, autoPlay, defaultVolume); if(_soundOutput == null || !_soundOutput.Initialized) return; + // Add event handling for the sound output + _soundOutput.PropertyChanged += SoundOutputStateChanged; + // Mark the player as ready Initialized = true; + SetDiscInformation(); } #region Playback + /// + /// Begin playback + /// + public void Play() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + else if(_soundOutput == null) + return; + else if(_soundOutput.Playing) + return; + + _soundOutput.Play(); + _opticalDisc.SetTotalIndexes(); + Playing = true; + } + + /// + /// Pause current playback + /// + public void Pause() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + else if(_soundOutput == null) + return; + else if(!_soundOutput.Playing) + return; + + _soundOutput?.Stop(); + Playing = false; + } + + /// + /// Stop current playback + /// + public void Stop() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + else if(_soundOutput == null) + return; + else if(!_soundOutput.Playing) + return; + + _soundOutput?.Stop(); + _opticalDisc.LoadFirstTrack(); + Playing = null; + } + /// /// Move to the next playable track /// public void NextTrack() { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; bool? wasPlaying = Playing; - if(wasPlaying == true) Playing = false; + if(wasPlaying == true) Pause(); - OpticalDisc.NextTrack(); - if(OpticalDisc is CompactDisc compactDisc) - _soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + _opticalDisc.NextTrack(); + if(_opticalDisc is CompactDisc compactDisc) + _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - if(wasPlaying == true) Playing = true; + SetDiscInformation(); + + if(wasPlaying == true) Play(); } /// @@ -154,17 +301,19 @@ namespace RedBookPlayer.Hardware /// public void PreviousTrack() { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; bool? wasPlaying = Playing; - if(wasPlaying == true) Playing = false; + if(wasPlaying == true) Pause(); - OpticalDisc.PreviousTrack(); - if(OpticalDisc is CompactDisc compactDisc) - _soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + _opticalDisc.PreviousTrack(); + if(_opticalDisc is CompactDisc compactDisc) + _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - if(wasPlaying == true) Playing = true; + SetDiscInformation(); + + if(wasPlaying == true) Play(); } /// @@ -173,17 +322,19 @@ namespace RedBookPlayer.Hardware /// True if index changes can trigger a track change, false otherwise public void NextIndex(bool changeTrack) { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; bool? wasPlaying = Playing; - if(wasPlaying == true) Playing = false; + if(wasPlaying == true) Pause(); - OpticalDisc.NextIndex(changeTrack); - if(OpticalDisc is CompactDisc compactDisc) - _soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + _opticalDisc.NextIndex(changeTrack); + if(_opticalDisc is CompactDisc compactDisc) + _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - if(wasPlaying == true) Playing = true; + SetDiscInformation(); + + if(wasPlaying == true) Play(); } /// @@ -192,17 +343,19 @@ namespace RedBookPlayer.Hardware /// True if index changes can trigger a track change, false otherwise public void PreviousIndex(bool changeTrack) { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; bool? wasPlaying = Playing; - if(wasPlaying == true) Playing = false; + if(wasPlaying == true) Pause(); - OpticalDisc.PreviousIndex(changeTrack); - if(OpticalDisc is CompactDisc compactDisc) - _soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + _opticalDisc.PreviousIndex(changeTrack); + if(_opticalDisc is CompactDisc compactDisc) + _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - if(wasPlaying == true) Playing = true; + SetDiscInformation(); + + if(wasPlaying == true) Play(); } /// @@ -210,10 +363,11 @@ namespace RedBookPlayer.Hardware /// public void FastForward() { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; - OpticalDisc.CurrentSector = Math.Min(OpticalDisc.TotalSectors, OpticalDisc.CurrentSector + 75); + _opticalDisc.CurrentSector = Math.Min(_opticalDisc.TotalSectors, _opticalDisc.CurrentSector + 75); + SetDiscInformation(); } /// @@ -221,11 +375,13 @@ namespace RedBookPlayer.Hardware /// public void Rewind() { - if(OpticalDisc == null || !OpticalDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; - if(OpticalDisc.CurrentSector >= 75) - OpticalDisc.CurrentSector -= 75; + if(_opticalDisc.CurrentSector >= 75) + _opticalDisc.CurrentSector -= 75; + + SetDiscInformation(); } #endregion @@ -238,21 +394,58 @@ namespace RedBookPlayer.Hardware /// ulong representing the current sector time public ulong GetCurrentSectorTime() { - ulong sectorTime = OpticalDisc.CurrentSector; - if (OpticalDisc.SectionStartSector != 0) - sectorTime -= OpticalDisc.SectionStartSector; + ulong sectorTime = _opticalDisc.CurrentSector; + if (_opticalDisc.SectionStartSector != 0) + sectorTime -= _opticalDisc.SectionStartSector; else - sectorTime += OpticalDisc.TimeOffset; + sectorTime += _opticalDisc.TimeOffset; return sectorTime; } /// - /// Set if de-emphasis should be applied + /// Set de-emphasis status /// - /// True to enable, false to disable + /// public void SetDeEmphasis(bool apply) => _soundOutput?.SetDeEmphasis(apply); + /// + /// Update the player from the current SoundOutput + /// + private void SoundOutputStateChanged(object sender, PropertyChangedEventArgs e) + { + Playing = _soundOutput.Playing; + ApplyDeEmphasis = _soundOutput.ApplyDeEmphasis; + //Volume = _soundOutput.Volume; + } + + /// + /// Set all current disc information + /// + private void SetDiscInformation() + { + CurrentTrackNumber = _opticalDisc.CurrentTrackNumber; + CurrentTrackIndex = _opticalDisc.CurrentTrackIndex; + CurrentSector = _opticalDisc.CurrentSector; + + HiddenTrack = TimeOffset > 150; + + if(_opticalDisc is CompactDisc compactDisc) + { + QuadChannel = compactDisc.QuadChannel; + IsDataTrack = compactDisc.IsDataTrack; + CopyAllowed = compactDisc.CopyAllowed; + TrackHasEmphasis = compactDisc.TrackHasEmphasis; + } + else + { + QuadChannel = false; + IsDataTrack = _opticalDisc.TrackType != TrackType.Audio; + CopyAllowed = false; + TrackHasEmphasis = false; + } + } + #endregion } } \ No newline at end of file diff --git a/RedBookPlayer/Hardware/SoundOutput.cs b/RedBookPlayer/Hardware/SoundOutput.cs index d31b68c..d97466e 100644 --- a/RedBookPlayer/Hardware/SoundOutput.cs +++ b/RedBookPlayer/Hardware/SoundOutput.cs @@ -4,11 +4,12 @@ using System.Threading.Tasks; using CSCore.SoundOut; using NWaves.Audio; using NWaves.Filters.BiQuad; +using ReactiveUI; using RedBookPlayer.Discs; namespace RedBookPlayer.Hardware { - public class SoundOutput + public class SoundOutput : ReactiveObject { #region Public Fields @@ -17,15 +18,23 @@ namespace RedBookPlayer.Hardware /// public bool Initialized { get; private set; } = false; - /// - /// Indicates if de-emphasis should be applied - /// - public bool ApplyDeEmphasis { get; set; } = false; - /// /// Indicate if the output is playing /// - public bool Playing => _soundOut.PlaybackState == PlaybackState.Playing; + public bool Playing + { + get => _playing; + private set => this.RaiseAndSetIfChanged(ref _playing, value); + } + + /// + /// Indicates if de-emphasis should be applied + /// + public bool ApplyDeEmphasis + { + get => _applyDeEmphasis; + private set => this.RaiseAndSetIfChanged(ref _applyDeEmphasis, value); + } /// /// Current playback volume @@ -35,24 +44,24 @@ namespace RedBookPlayer.Hardware get => _volume; set { + int tempVolume = value; if(value > 100) - _volume = 100; + tempVolume = 100; else if(value < 0) - _volume = 0; - else - _volume = value; + tempVolume = 0; + + this.RaiseAndSetIfChanged(ref _volume, tempVolume); } } + private bool _playing; + private bool _applyDeEmphasis; + private int _volume; + #endregion #region Private State Variables - /// - /// Current position in the sector - /// - private int _currentSectorReadPosition = 0; - /// /// OpticalDisc from the parent player for easy access /// @@ -61,11 +70,6 @@ namespace RedBookPlayer.Hardware /// private OpticalDisc _opticalDisc; - /// - /// Internal value for the volume - /// - private int _volume; - /// /// Data provider for sound output /// @@ -86,6 +90,11 @@ namespace RedBookPlayer.Hardware /// private BiQuadFilter _deEmphasisFilterRight; + /// + /// Current position in the sector + /// + private int _currentSectorReadPosition = 0; + /// /// Lock object for reading track data /// @@ -254,22 +263,45 @@ namespace RedBookPlayer.Hardware /// /// Start audio playback /// - public void Play() => _soundOut.Play(); + public void Play() + { + if (_soundOut.PlaybackState != PlaybackState.Playing) + _soundOut.Play(); + + Playing = _soundOut.PlaybackState == PlaybackState.Playing; + } + + /// + /// Pause audio playback + /// + public void Pause() + { + if(_soundOut.PlaybackState != PlaybackState.Paused) + _soundOut.Pause(); + + Playing = _soundOut.PlaybackState == PlaybackState.Playing; + } /// /// Stop audio playback /// - public void Stop() => _soundOut.Stop(); + public void Stop() + { + if(_soundOut.PlaybackState != PlaybackState.Stopped) + _soundOut.Stop(); + + Playing = _soundOut.PlaybackState == PlaybackState.Playing; + } #endregion #region Helpers /// - /// Toggle de-emphasis processing + /// Set de-emphasis status /// - /// True to apply de-emphasis, false otherwise - public void SetDeEmphasis(bool enable) => ApplyDeEmphasis = enable; + /// + public void SetDeEmphasis(bool apply) => ApplyDeEmphasis = apply; /// /// Sets or resets the de-emphasis filters From cd3ccbc8ebdfee66777d759b6be8bb186bae30a8 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 4 Jul 2021 23:36:09 -0700 Subject: [PATCH 02/33] Make OpticalDisc have events --- RedBookPlayer/Discs/CompactDisc.cs | 62 ++++++--- RedBookPlayer/Discs/OpticalDisc.cs | 5 +- RedBookPlayer/GUI/PlayerViewModel.cs | 192 +++++++++++++++++---------- 3 files changed, 171 insertions(+), 88 deletions(-) diff --git a/RedBookPlayer/Discs/CompactDisc.cs b/RedBookPlayer/Discs/CompactDisc.cs index 21e8fc8..62d8728 100644 --- a/RedBookPlayer/Discs/CompactDisc.cs +++ b/RedBookPlayer/Discs/CompactDisc.cs @@ -6,11 +6,12 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Decoders.CD; using Aaru.Helpers; +using ReactiveUI; using static Aaru.Decoders.CD.FullTOC; namespace RedBookPlayer.Discs { - public class CompactDisc : OpticalDisc + public class CompactDisc : OpticalDisc, IReactiveObject { #region Public Fields @@ -26,7 +27,7 @@ namespace RedBookPlayer.Discs // Cache the value and the current track number int cachedValue = value; - int cachedTrackNumber = _currentTrackNumber; + int cachedTrackNumber; // Check if we're incrementing or decrementing the track bool increment = cachedValue >= _currentTrackNumber; @@ -39,10 +40,10 @@ namespace RedBookPlayer.Discs else if(cachedValue < 0) cachedValue = _image.Tracks.Count - 1; - _currentTrackNumber = cachedValue; + cachedTrackNumber = cachedValue; // Cache the current track for easy access - Track track = _image.Tracks[_currentTrackNumber]; + Track track = _image.Tracks[cachedTrackNumber]; // Set track flags from subchannel data, if possible SetTrackFlags(track); @@ -52,7 +53,7 @@ namespace RedBookPlayer.Discs // If the track is playable, just return if(TrackType == TrackType.Audio || App.Settings.PlayDataTracks) - return; + break; // If we're not playing the track, skip if(increment) @@ -60,7 +61,9 @@ namespace RedBookPlayer.Discs else cachedValue--; } - while(cachedValue != cachedTrackNumber); + while(cachedValue != _currentTrackNumber); + + this.RaiseAndSetIfChanged(ref _currentTrackNumber, cachedTrackNumber); } } @@ -78,12 +81,13 @@ namespace RedBookPlayer.Discs Track track = _image.Tracks[CurrentTrackNumber]; // Ensure that the value is valid, wrapping around if necessary + ushort fixedValue = value; if(value > track.Indexes.Keys.Max()) - _currentTrackIndex = track.Indexes.Keys.Min(); + fixedValue = track.Indexes.Keys.Min(); else if(value < track.Indexes.Keys.Min()) - _currentTrackIndex = track.Indexes.Keys.Max(); - else - _currentTrackIndex = value; + fixedValue = track.Indexes.Keys.Max(); + + this.RaiseAndSetIfChanged(ref _currentTrackIndex, fixedValue); // Set new index-specific data SectionStartSector = (ulong)track.Indexes[CurrentTrackIndex]; @@ -104,7 +108,7 @@ namespace RedBookPlayer.Discs // Cache the current track for easy access Track track = _image.Tracks[CurrentTrackNumber]; - _currentSector = value; + this.RaiseAndSetIfChanged(ref _currentSector, value); if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= _image.Tracks[CurrentTrackNumber + 1].TrackStartSector) || (CurrentTrackNumber > 0 && CurrentSector < track.TrackStartSector)) @@ -135,22 +139,43 @@ namespace RedBookPlayer.Discs /// /// Represents the 4CH flag /// - public bool QuadChannel { get; private set; } = false; + public bool QuadChannel + { + get => _quadChannel; + private set => this.RaiseAndSetIfChanged(ref _quadChannel, value); + } /// /// Represents the DATA flag /// - public bool IsDataTrack => TrackType != TrackType.Audio; + public bool IsDataTrack + { + get => _isDataTrack; + private set => this.RaiseAndSetIfChanged(ref _isDataTrack, value); + } /// /// Represents the DCP flag /// - public bool CopyAllowed { get; private set; } = false; + public bool CopyAllowed + { + get => _copyAllowed; + private set => this.RaiseAndSetIfChanged(ref _copyAllowed, value); + } /// /// Represents the PRE flag /// - public bool TrackHasEmphasis { get; private set; } = false; + public bool TrackHasEmphasis + { + get => _trackHasEmphasis; + private set => this.RaiseAndSetIfChanged(ref _trackHasEmphasis, value); + } + + private bool _quadChannel; + private bool _isDataTrack; + private bool _copyAllowed; + private bool _trackHasEmphasis; #endregion @@ -369,8 +394,9 @@ namespace RedBookPlayer.Discs /// Track object to read from private void SetDefaultTrackFlags(Track track) { - QuadChannel = false; TrackType = track.TrackType; + QuadChannel = false; + IsDataTrack = track.TrackType != TrackType.Audio; CopyAllowed = false; TrackHasEmphasis = false; } @@ -390,9 +416,11 @@ namespace RedBookPlayer.Discs byte flags = (byte)(descriptor.CONTROL & 0x0D); TrackHasEmphasis = (flags & (byte)TocControl.TwoChanPreEmph) == (byte)TocControl.TwoChanPreEmph; CopyAllowed = (flags & (byte)TocControl.CopyPermissionMask) == (byte)TocControl.CopyPermissionMask; - TrackType = (flags & (byte)TocControl.DataTrack) == (byte)TocControl.DataTrack ? TrackType.Data : TrackType.Audio; + IsDataTrack = (flags & (byte)TocControl.DataTrack) == (byte)TocControl.DataTrack; QuadChannel = (flags & (byte)TocControl.FourChanNoPreEmph) == (byte)TocControl.FourChanNoPreEmph; + TrackType = IsDataTrack ? TrackType.Data : TrackType.Audio; + return; } catch(Exception) diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer/Discs/OpticalDisc.cs index 817c626..831deed 100644 --- a/RedBookPlayer/Discs/OpticalDisc.cs +++ b/RedBookPlayer/Discs/OpticalDisc.cs @@ -1,9 +1,10 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; +using ReactiveUI; namespace RedBookPlayer.Discs { - public abstract class OpticalDisc + public abstract class OpticalDisc : ReactiveObject { #region Public Fields @@ -55,7 +56,7 @@ namespace RedBookPlayer.Discs /// /// Total sectors in the image /// - public ulong TotalSectors => _image.Info.Sectors; + public ulong TotalSectors => _image?.Info.Sectors ?? 0; /// /// Represents the time adjustment offset for the disc diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 4162012..8714d48 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -19,6 +19,119 @@ namespace RedBookPlayer.GUI #region Player Passthrough + #region OpticalDisc Passthrough + + /// + /// Current track number + /// + public int CurrentTrackNumber + { + get => _currentTrackNumber; + private set => this.RaiseAndSetIfChanged(ref _currentTrackNumber, value); + } + + /// + /// Current track index + /// + public ushort CurrentTrackIndex + { + get => _currentTrackIndex; + private set => this.RaiseAndSetIfChanged(ref _currentTrackIndex, value); + } + + /// + /// Current sector number + /// + public ulong CurrentSector + { + get => _currentSector; + private set => this.RaiseAndSetIfChanged(ref _currentSector, value); + } + + /// + /// Represents if the disc has a hidden track + /// + public bool HiddenTrack + { + get => _hasHiddenTrack; + private set => this.RaiseAndSetIfChanged(ref _hasHiddenTrack, value); + } + + /// + /// Represents the 4CH flag [CompactDisc only] + /// + public bool QuadChannel + { + get => _quadChannel; + private set => this.RaiseAndSetIfChanged(ref _quadChannel, value); + } + + /// + /// Represents the DATA flag [CompactDisc only] + /// + public bool IsDataTrack + { + get => _isDataTrack; + private set => this.RaiseAndSetIfChanged(ref _isDataTrack, value); + } + + /// + /// Represents the DCP flag [CompactDisc only] + /// + public bool CopyAllowed + { + get => _copyAllowed; + private set => this.RaiseAndSetIfChanged(ref _copyAllowed, value); + } + + /// + /// Represents the PRE flag [CompactDisc only] + /// + public bool TrackHasEmphasis + { + get => _trackHasEmphasis; + private set => this.RaiseAndSetIfChanged(ref _trackHasEmphasis, value); + } + + /// + /// Represents the total tracks on the disc + /// + public int TotalTracks => _player.TotalTracks; + + /// + /// Represents the total indices on the disc + /// + public int TotalIndexes => _player.TotalIndexes; + + /// + /// Total sectors in the image + /// + public ulong TotalSectors => _player.TotalSectors; + + /// + /// Represents the time adjustment offset for the disc + /// + public ulong TimeOffset => _player.TimeOffset; + + /// + /// Represents the total playing time for the disc + /// + public ulong TotalTime => _player.TotalTime; + + private int _currentTrackNumber; + private ushort _currentTrackIndex; + private ulong _currentSector; + + private bool _hasHiddenTrack; + private bool _quadChannel; + private bool _isDataTrack; + private bool _copyAllowed; + private bool _trackHasEmphasis; + + #endregion + + #region SoundOutput Passthrough + /// /// Indicate if the model is ready to be used /// @@ -57,69 +170,6 @@ namespace RedBookPlayer.GUI #endregion - #region Model-Provided Playback Information - - private ulong _currentSector; - public ulong CurrentSector - { - get => _currentSector; - set => this.RaiseAndSetIfChanged(ref _currentSector, value); - } - - public int CurrentFrame => (int)(_currentSector / (75 * 60)); - public int CurrentSecond => (int)(_currentSector / 75 % 60); - public int CurrentMinute => (int)(_currentSector % 75); - - private ulong _totalSectors; - public ulong TotalSectors - { - get => _totalSectors; - set => this.RaiseAndSetIfChanged(ref _totalSectors, value); - } - - public int TotalFrames => (int)(_totalSectors / (75 * 60)); - public int TotalSeconds => (int)(_totalSectors / 75 % 60); - public int TotalMinutes => (int)(_totalSectors % 75); - - #endregion - - #region Disc Flags - - private bool _quadChannel; - public bool QuadChannel - { - get => _quadChannel; - set => this.RaiseAndSetIfChanged(ref _quadChannel, value); - } - - private bool _isDataTrack; - public bool IsDataTrack - { - get => _isDataTrack; - set => this.RaiseAndSetIfChanged(ref _isDataTrack, value); - } - - private bool _copyAllowed; - public bool CopyAllowed - { - get => _copyAllowed; - set => this.RaiseAndSetIfChanged(ref _copyAllowed, value); - } - - private bool _trackHasEmphasis; - public bool TrackHasEmphasis - { - get => _trackHasEmphasis; - set => this.RaiseAndSetIfChanged(ref _trackHasEmphasis, value); - } - - private bool _hiddenTrack; - public bool HiddenTrack - { - get => _hiddenTrack; - set => this.RaiseAndSetIfChanged(ref _hiddenTrack, value); - } - #endregion /// @@ -199,6 +249,9 @@ namespace RedBookPlayer.GUI /// Generate the digit string to be interpreted by the frontend /// /// String representing the digits for the frontend + /// + /// TODO: The model shouldn't care about this + /// public string GenerateDigitString() { // If the disc isn't initialized, return all '-' characters @@ -259,12 +312,9 @@ namespace RedBookPlayer.GUI if(_player?.Initialized != true) return; - Playing = _player.Playing; - ApplyDeEmphasis = _player.ApplyDeEmphasis; - Volume = _player.Volume; - - CurrentSector = _player.GetCurrentSectorTime(); - TotalSectors = _player.TotalTime; + CurrentTrackNumber = _player.CurrentTrackNumber; + CurrentTrackIndex = _player.CurrentTrackIndex; + CurrentSector = _player.CurrentSector; HiddenTrack = _player.HiddenTrack; @@ -272,6 +322,10 @@ namespace RedBookPlayer.GUI IsDataTrack = _player.IsDataTrack; CopyAllowed = _player.CopyAllowed; TrackHasEmphasis = _player.TrackHasEmphasis; + + Playing = _player.Playing; + ApplyDeEmphasis = _player.ApplyDeEmphasis; + Volume = _player.Volume; } #endregion From 65ab29f29f64ec5280cf8d237f99a427857f878b Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 4 Jul 2021 23:43:55 -0700 Subject: [PATCH 03/33] Remove need for UI timer for updates --- RedBookPlayer/GUI/PlayerView.xaml.cs | 32 ++++++---------------------- RedBookPlayer/GUI/PlayerViewModel.cs | 2 +- RedBookPlayer/Hardware/Player.cs | 9 ++++++++ 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs index bdb1ecc..ec749cb 100644 --- a/RedBookPlayer/GUI/PlayerView.xaml.cs +++ b/RedBookPlayer/GUI/PlayerView.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -29,11 +30,6 @@ namespace RedBookPlayer.GUI /// private Image[] _digits; - /// - /// Timer for performing UI updates - /// - private Timer _updateTimer; - public PlayerView() => InitializeComponent(null); public PlayerView(string xaml) => InitializeComponent(xaml); @@ -111,6 +107,7 @@ namespace RedBookPlayer.GUI private void InitializeComponent(string xaml) { DataContext = new PlayerViewModel(); + PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; // Load the theme try @@ -126,23 +123,6 @@ namespace RedBookPlayer.GUI } InitializeDigits(); - - _updateTimer = new Timer(1000 / 60); - - _updateTimer.Elapsed += (sender, e) => - { - try - { - UpdateView(sender, e); - } - catch(Exception ex) - { - Console.WriteLine(ex); - } - }; - - _updateTimer.AutoReset = true; - _updateTimer.Start(); } /// @@ -181,16 +161,16 @@ namespace RedBookPlayer.GUI } /// - /// Update the UI with the most recent information from the Player + /// Update the UI from the view-model /// - private void UpdateView(object sender, ElapsedEventArgs e) + private void PlayerViewModelStateChanged(object sender, PropertyChangedEventArgs e) { Dispatcher.UIThread.InvokeAsync(() => { string digitString = PlayerViewModel.GenerateDigitString(); - for (int i = 0; i < _digits.Length; i++) + for(int i = 0; i < _digits.Length; i++) { - if (_digits[i] != null) + if(_digits[i] != null) _digits[i].Source = GetBitmap(digitString[i]); } }); diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 8714d48..cee36af 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -305,7 +305,7 @@ namespace RedBookPlayer.GUI } /// - /// Update the UI from the internal player + /// Update the view-model from the Player /// private void PlayerStateChanged(object sender, PropertyChangedEventArgs e) { diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 3c72fa6..0733e5a 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -211,6 +211,10 @@ namespace RedBookPlayer.Hardware return; } + // Add event handling for the optical disc + if(_opticalDisc != null) + _opticalDisc.PropertyChanged += OpticalDiscStateChanged; + // Initialize the sound output _soundOutput.Init(_opticalDisc, autoPlay, defaultVolume); if(_soundOutput == null || !_soundOutput.Initialized) @@ -409,6 +413,11 @@ namespace RedBookPlayer.Hardware /// public void SetDeEmphasis(bool apply) => _soundOutput?.SetDeEmphasis(apply); + /// + /// Update the player from the current OpticalDisc + /// + private void OpticalDiscStateChanged(object sender, PropertyChangedEventArgs e) => SetDiscInformation(); + /// /// Update the player from the current SoundOutput /// From 59bfd405a606072f65f33eb5fc727f945b5e58af Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 00:31:19 -0700 Subject: [PATCH 04/33] Move all image logic to factory --- RedBookPlayer/Discs/OpticalDiscFactory.cs | 34 +++++++++++++++++++++++ RedBookPlayer/Hardware/Player.cs | 29 +++---------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/RedBookPlayer/Discs/OpticalDiscFactory.cs b/RedBookPlayer/Discs/OpticalDiscFactory.cs index 99d9257..178a87a 100644 --- a/RedBookPlayer/Discs/OpticalDiscFactory.cs +++ b/RedBookPlayer/Discs/OpticalDiscFactory.cs @@ -1,10 +1,44 @@ +using System.IO; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Metadata; +using Aaru.DiscImages; +using Aaru.Filters; namespace RedBookPlayer.Discs { public static class OpticalDiscFactory { + /// + /// Generate an OpticalDisc from an input path + /// + /// Path to load the image from + /// True if the image should be playable immediately, false otherwise + /// Instantiated OpticalDisc, if possible + public static OpticalDisc GenerateFromPath(string path, bool autoPlay) + { + try + { + // Validate the image exists + if(string.IsNullOrWhiteSpace(path) || !File.Exists(path)) + return null; + + // Load the disc image to memory + // TODO: Assumes Aaruformat right now for all + var image = new AaruFormat(); + var filter = new ZZZNoFilter(); + filter.Open(path); + image.Open(filter); + + // Generate and instantiate the disc + return GenerateFromImage(image, autoPlay); + } + catch + { + // All errors mean an invalid image in some way + return null; + } + } + /// /// Generate an OpticalDisc from an input IOpticalMediaImage /// diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 0733e5a..746729c 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -1,9 +1,6 @@ using System; using System.ComponentModel; -using System.IO; using Aaru.CommonTypes.Enums; -using Aaru.DiscImages; -using Aaru.Filters; using ReactiveUI; using RedBookPlayer.Discs; @@ -188,32 +185,14 @@ namespace RedBookPlayer.Hardware Initialized = false; _soundOutput = new SoundOutput(); _soundOutput.SetDeEmphasis(false); - _opticalDisc = null; - try - { - // Validate the image exists - if(string.IsNullOrWhiteSpace(path) || !File.Exists(path)) - return; - - // Load the disc image to memory - var image = new AaruFormat(); - var filter = new ZZZNoFilter(); - filter.Open(path); - image.Open(filter); - - // Generate and instantiate the disc - _opticalDisc = OpticalDiscFactory.GenerateFromImage(image, autoPlay); - } - catch - { - // All errors mean an invalid image in some way + // Initalize the disc + _opticalDisc = OpticalDiscFactory.GenerateFromPath(path, autoPlay); + if(_opticalDisc == null || !_opticalDisc.Initialized) return; - } // Add event handling for the optical disc - if(_opticalDisc != null) - _opticalDisc.PropertyChanged += OpticalDiscStateChanged; + _opticalDisc.PropertyChanged += OpticalDiscStateChanged; // Initialize the sound output _soundOutput.Init(_opticalDisc, autoPlay, defaultVolume); From a5bffa83beb747f61c65292e9764267f03ba64dc Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 00:31:49 -0700 Subject: [PATCH 05/33] Make view-model safer --- RedBookPlayer/GUI/PlayerViewModel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index cee36af..467895b 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -197,17 +197,17 @@ namespace RedBookPlayer.GUI /// /// Begin playback /// - public void Play() => _player.Play(); + public void Play() => _player?.Play(); /// /// Pause current playback /// - public void Pause() => _player.Pause(); + public void Pause() => _player?.Pause(); /// /// Stop current playback /// - public void Stop() => _player.Stop(); + public void Stop() => _player?.Stop(); /// /// Move to the next playable track From 2476d07619b3dc32a1f051a79f0a84241ab072ef Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 00:48:14 -0700 Subject: [PATCH 06/33] Move methods to better locations --- RedBookPlayer/Discs/OpticalDisc.cs | 8 ++++- RedBookPlayer/GUI/PlayerView.xaml.cs | 54 +++++++++++++++++++++++++--- RedBookPlayer/GUI/PlayerViewModel.cs | 47 ++++++------------------ RedBookPlayer/Hardware/Player.cs | 26 ++++++-------- 4 files changed, 77 insertions(+), 58 deletions(-) diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer/Discs/OpticalDisc.cs index 831deed..701660a 100644 --- a/RedBookPlayer/Discs/OpticalDisc.cs +++ b/RedBookPlayer/Discs/OpticalDisc.cs @@ -31,7 +31,11 @@ namespace RedBookPlayer.Discs /// /// Represents the sector starting the section /// - public ulong SectionStartSector { get; protected set; } + public ulong SectionStartSector + { + get => _sectionStartSector; + protected set => this.RaiseAndSetIfChanged(ref _sectionStartSector, value); + } /// /// Number of bytes per sector for the current track @@ -68,6 +72,8 @@ namespace RedBookPlayer.Discs /// public ulong TotalTime { get; protected set; } = 0; + private ulong _sectionStartSector; + #endregion #region Protected State Variables diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs index ec749cb..ac975c3 100644 --- a/RedBookPlayer/GUI/PlayerView.xaml.cs +++ b/RedBookPlayer/GUI/PlayerView.xaml.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Timers; using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; @@ -25,9 +24,6 @@ namespace RedBookPlayer.GUI /// /// Set of images representing the digits for the UI /// - /// - /// TODO: Does it make sense to have this as an array? - /// private Image[] _digits; public PlayerView() => InitializeComponent(null); @@ -167,7 +163,7 @@ namespace RedBookPlayer.GUI { Dispatcher.UIThread.InvokeAsync(() => { - string digitString = PlayerViewModel.GenerateDigitString(); + string digitString = GenerateDigitString(); for(int i = 0; i < _digits.Length; i++) { if(_digits[i] != null) @@ -176,6 +172,54 @@ namespace RedBookPlayer.GUI }); } + /// + /// Generate the digit string to be interpreted by the frontend + /// + /// String representing the digits for the frontend + private string GenerateDigitString() + { + // If the disc isn't initialized, return all '-' characters + if(PlayerViewModel?.Initialized != true) + return string.Empty.PadLeft(20, '-'); + + // Otherwise, take the current time into account + ulong sectorTime = GetCurrentSectorTime(); + + int[] numbers = new int[] + { + PlayerViewModel.CurrentTrackNumber + 1, + PlayerViewModel.CurrentTrackIndex, + + (int)(sectorTime / (75 * 60)), + (int)(sectorTime / 75 % 60), + (int)(sectorTime % 75), + + PlayerViewModel.TotalTracks, + PlayerViewModel.TotalIndexes, + + (int)(PlayerViewModel.TotalTime / (75 * 60)), + (int)(PlayerViewModel.TotalTime / 75 % 60), + (int)(PlayerViewModel.TotalTime % 75), + }; + + return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); + } + + /// + /// Get current sector time, accounting for offsets + /// + /// ulong representing the current sector time + private ulong GetCurrentSectorTime() + { + ulong sectorTime = PlayerViewModel.CurrentSector; + if(PlayerViewModel.SectionStartSector != 0) + sectorTime -= PlayerViewModel.SectionStartSector; + else + sectorTime += PlayerViewModel.TimeOffset; + + return sectorTime; + } + #endregion #region Event Handlers diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 467895b..654d3cd 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -1,5 +1,4 @@ using System.ComponentModel; -using System.Linq; using ReactiveUI; using RedBookPlayer.Hardware; @@ -48,6 +47,15 @@ namespace RedBookPlayer.GUI private set => this.RaiseAndSetIfChanged(ref _currentSector, value); } + /// + /// Represents the sector starting the section + /// + public ulong SectionStartSector + { + get => _sectionStartSector; + protected set => this.RaiseAndSetIfChanged(ref _sectionStartSector, value); + } + /// /// Represents if the disc has a hidden track /// @@ -121,6 +129,7 @@ namespace RedBookPlayer.GUI private int _currentTrackNumber; private ushort _currentTrackIndex; private ulong _currentSector; + private ulong _sectionStartSector; private bool _hasHiddenTrack; private bool _quadChannel; @@ -245,42 +254,6 @@ namespace RedBookPlayer.GUI #region Helpers - /// - /// Generate the digit string to be interpreted by the frontend - /// - /// String representing the digits for the frontend - /// - /// TODO: The model shouldn't care about this - /// - public string GenerateDigitString() - { - // If the disc isn't initialized, return all '-' characters - if(_player?.Initialized != true) - return string.Empty.PadLeft(20, '-'); - - // Otherwise, take the current time into account - ulong sectorTime = _player.GetCurrentSectorTime(); - - int[] numbers = new int[] - { - _player.CurrentTrackNumber + 1, - _player.CurrentTrackIndex, - - (int)(sectorTime / (75 * 60)), - (int)(sectorTime / 75 % 60), - (int)(sectorTime % 75), - - _player.TotalTracks, - _player.TotalIndexes, - - (int)(_player.TotalTime / (75 * 60)), - (int)(_player.TotalTime / 75 % 60), - (int)(_player.TotalTime % 75), - }; - - return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); - } - /// /// Set de-emphasis status /// diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 746729c..870200e 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -42,6 +42,15 @@ namespace RedBookPlayer.Hardware private set => this.RaiseAndSetIfChanged(ref _currentSector, value); } + /// + /// Represents the sector starting the section + /// + public ulong SectionStartSector + { + get => _sectionStartSector; + protected set => this.RaiseAndSetIfChanged(ref _sectionStartSector, value); + } + /// /// Represents if the disc has a hidden track /// @@ -115,6 +124,7 @@ namespace RedBookPlayer.Hardware private int _currentTrackNumber; private ushort _currentTrackIndex; private ulong _currentSector; + private ulong _sectionStartSector; private bool _hasHiddenTrack; private bool _quadChannel; @@ -371,21 +381,6 @@ namespace RedBookPlayer.Hardware #region Helpers - /// - /// Get current sector time, accounting for offsets - /// - /// ulong representing the current sector time - public ulong GetCurrentSectorTime() - { - ulong sectorTime = _opticalDisc.CurrentSector; - if (_opticalDisc.SectionStartSector != 0) - sectorTime -= _opticalDisc.SectionStartSector; - else - sectorTime += _opticalDisc.TimeOffset; - - return sectorTime; - } - /// /// Set de-emphasis status /// @@ -415,6 +410,7 @@ namespace RedBookPlayer.Hardware CurrentTrackNumber = _opticalDisc.CurrentTrackNumber; CurrentTrackIndex = _opticalDisc.CurrentTrackIndex; CurrentSector = _opticalDisc.CurrentSector; + SectionStartSector = _opticalDisc.SectionStartSector; HiddenTrack = TimeOffset > 150; From 8c9f10dd8ee622e0ad19a5f747b3101f0cef7f75 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 10:42:28 -0700 Subject: [PATCH 07/33] Consolidate view init code --- RedBookPlayer/GUI/PlayerView.xaml.cs | 225 ++++++++++++++------------- 1 file changed, 113 insertions(+), 112 deletions(-) diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs index ac975c3..2319c97 100644 --- a/RedBookPlayer/GUI/PlayerView.xaml.cs +++ b/RedBookPlayer/GUI/PlayerView.xaml.cs @@ -26,9 +26,18 @@ namespace RedBookPlayer.GUI /// private Image[] _digits; - public PlayerView() => InitializeComponent(null); + /// + /// Initialize the UI based on the currently selected theme + /// + /// XAML data representing the theme, null for default + public PlayerView(string xaml = null) + { + DataContext = new PlayerViewModel(); + PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; - public PlayerView(string xaml) => InitializeComponent(xaml); + LoadTheme(xaml); + InitializeDigits(); + } #region Helpers @@ -55,120 +64,13 @@ namespace RedBookPlayer.GUI /// Path to the image to load public async Task LoadImage(string path) { - bool result = await Dispatcher.UIThread.InvokeAsync(() => + return await Dispatcher.UIThread.InvokeAsync(() => { PlayerViewModel.Init(path, App.Settings.AutoPlay, App.Settings.Volume); - return PlayerViewModel.Initialized; - }); - - if(result) - { - await Dispatcher.UIThread.InvokeAsync(() => - { + if (PlayerViewModel.Initialized) MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); - }); - } - return result; - } - - /// - /// Load the png image for a given character based on the theme - /// - /// Character to load the image for - /// Bitmap representing the loaded image - /// - /// TODO: Currently assumes that an image must always exist - /// - private Bitmap GetBitmap(char character) - { - if(App.Settings.SelectedTheme == "default") - { - IAssetLoader assets = AvaloniaLocator.Current.GetService(); - - return new Bitmap(assets.Open(new Uri($"avares://RedBookPlayer/Assets/{character}.png"))); - } - else - { - string themeDirectory = $"{Directory.GetCurrentDirectory()}/themes/{App.Settings.SelectedTheme}"; - using FileStream stream = File.Open($"{themeDirectory}/{character}.png", FileMode.Open); - return new Bitmap(stream); - } - } - - /// - /// Initialize the UI based on the currently selected theme - /// - /// XAML data representing the theme, null for default - private void InitializeComponent(string xaml) - { - DataContext = new PlayerViewModel(); - PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; - - // Load the theme - try - { - if(xaml != null) - new AvaloniaXamlLoader().Load(xaml, null, this); - else - AvaloniaXamlLoader.Load(this); - } - catch - { - AvaloniaXamlLoader.Load(this); - } - - InitializeDigits(); - } - - /// - /// Initialize the displayed digits array - /// - private void InitializeDigits() - { - _digits = new Image[] - { - this.FindControl("TrackDigit1"), - this.FindControl("TrackDigit2"), - - this.FindControl("IndexDigit1"), - this.FindControl("IndexDigit2"), - - this.FindControl("TimeDigit1"), - this.FindControl("TimeDigit2"), - this.FindControl("TimeDigit3"), - this.FindControl("TimeDigit4"), - this.FindControl("TimeDigit5"), - this.FindControl("TimeDigit6"), - - this.FindControl("TotalTracksDigit1"), - this.FindControl("TotalTracksDigit2"), - - this.FindControl("TotalIndexesDigit1"), - this.FindControl("TotalIndexesDigit2"), - - this.FindControl("TotalTimeDigit1"), - this.FindControl("TotalTimeDigit2"), - this.FindControl("TotalTimeDigit3"), - this.FindControl("TotalTimeDigit4"), - this.FindControl("TotalTimeDigit5"), - this.FindControl("TotalTimeDigit6"), - }; - } - - /// - /// Update the UI from the view-model - /// - private void PlayerViewModelStateChanged(object sender, PropertyChangedEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - string digitString = GenerateDigitString(); - for(int i = 0; i < _digits.Length; i++) - { - if(_digits[i] != null) - _digits[i].Source = GetBitmap(digitString[i]); - } + return PlayerViewModel.Initialized; }); } @@ -220,6 +122,105 @@ namespace RedBookPlayer.GUI return sectorTime; } + /// + /// Load the png image for a given character based on the theme + /// + /// Character to load the image for + /// Bitmap representing the loaded image + private Bitmap GetBitmap(char character) + { + try + { + if(App.Settings.SelectedTheme == "default") + { + IAssetLoader assets = AvaloniaLocator.Current.GetService(); + + return new Bitmap(assets.Open(new Uri($"avares://RedBookPlayer/Assets/{character}.png"))); + } + else + { + string themeDirectory = $"{Directory.GetCurrentDirectory()}/themes/{App.Settings.SelectedTheme}"; + using FileStream stream = File.Open($"{themeDirectory}/{character}.png", FileMode.Open); + return new Bitmap(stream); + } + } + catch + { + return null; + } + } + + /// + /// Initialize the displayed digits array + /// + private void InitializeDigits() + { + _digits = new Image[] + { + this.FindControl("TrackDigit1"), + this.FindControl("TrackDigit2"), + + this.FindControl("IndexDigit1"), + this.FindControl("IndexDigit2"), + + this.FindControl("TimeDigit1"), + this.FindControl("TimeDigit2"), + this.FindControl("TimeDigit3"), + this.FindControl("TimeDigit4"), + this.FindControl("TimeDigit5"), + this.FindControl("TimeDigit6"), + + this.FindControl("TotalTracksDigit1"), + this.FindControl("TotalTracksDigit2"), + + this.FindControl("TotalIndexesDigit1"), + this.FindControl("TotalIndexesDigit2"), + + this.FindControl("TotalTimeDigit1"), + this.FindControl("TotalTimeDigit2"), + this.FindControl("TotalTimeDigit3"), + this.FindControl("TotalTimeDigit4"), + this.FindControl("TotalTimeDigit5"), + this.FindControl("TotalTimeDigit6"), + }; + } + + /// + /// Load the theme from a XAML, if possible + /// + /// XAML data representing the theme, null for default + private void LoadTheme(string xaml) + { + try + { + if(xaml != null) + new AvaloniaXamlLoader().Load(xaml, null, this); + else + AvaloniaXamlLoader.Load(this); + } + catch + { + AvaloniaXamlLoader.Load(this); + } + } + + /// + /// Update the UI from the view-model + /// + private void PlayerViewModelStateChanged(object sender, PropertyChangedEventArgs e) + { + Dispatcher.UIThread.InvokeAsync(() => + { + string digitString = GenerateDigitString(); + for(int i = 0; i < _digits.Length; i++) + { + Bitmap digitImage = GetBitmap(digitString[i]); + if(_digits[i] != null && digitImage != null) + _digits[i].Source = digitImage; + } + }); + } + #endregion #region Event Handlers From d8635594ec8b8b0a427f32757a3150a40816b1c7 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 11:55:36 -0700 Subject: [PATCH 08/33] View-model and model shouldn't know about settings --- RedBookPlayer/Discs/CompactDisc.cs | 37 ++++++++-- RedBookPlayer/Discs/OpticalDisc.cs | 10 +-- RedBookPlayer/Discs/OpticalDiscFactory.cs | 12 ++-- RedBookPlayer/GUI/MainWindow.xaml.cs | 7 ++ RedBookPlayer/GUI/PlayerView.xaml.cs | 82 +++++++++++++---------- RedBookPlayer/GUI/PlayerViewModel.cs | 18 +++-- RedBookPlayer/Hardware/Player.cs | 22 ++++-- 7 files changed, 129 insertions(+), 59 deletions(-) diff --git a/RedBookPlayer/Discs/CompactDisc.cs b/RedBookPlayer/Discs/CompactDisc.cs index 62d8728..f25a7ec 100644 --- a/RedBookPlayer/Discs/CompactDisc.cs +++ b/RedBookPlayer/Discs/CompactDisc.cs @@ -52,7 +52,7 @@ namespace RedBookPlayer.Discs CurrentTrackIndex = track.Indexes.Keys.Min(); // If the track is playable, just return - if(TrackType == TrackType.Audio || App.Settings.PlayDataTracks) + if(TrackType == TrackType.Audio || _loadDataTracks) break; // If we're not playing the track, skip @@ -196,6 +196,16 @@ namespace RedBookPlayer.Discs /// private ulong _currentSector = 0; + /// + /// Indicate if a TOC should be generated if missing + /// + private readonly bool _generateMissingToc = false; + + /// + /// Indicate if data tracks should be loaded + /// + private bool _loadDataTracks = false; + /// /// Current disc table of contents /// @@ -203,8 +213,19 @@ namespace RedBookPlayer.Discs #endregion + /// + /// Constructor + /// + /// Generate a TOC if the disc is missing one + /// Load data tracks for playback + public CompactDisc(bool generateMissingToc, bool loadDataTracks) + { + _generateMissingToc = generateMissingToc; + _loadDataTracks = loadDataTracks; + } + /// - public override void Init(IOpticalMediaImage image, bool autoPlay = false) + public override void Init(IOpticalMediaImage image, bool autoPlay) { // If the image is null, we can't do anything if(image == null) @@ -260,7 +281,7 @@ namespace RedBookPlayer.Discs } /// - public override bool PreviousIndex(bool changeTrack) + public override bool PreviousIndex(bool changeTrack, bool playHiddenTrack) { if(_image == null) return false; @@ -269,7 +290,7 @@ namespace RedBookPlayer.Discs { if(changeTrack) { - PreviousTrack(); + PreviousTrack(playHiddenTrack); CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes.Values.Max(); return true; } @@ -293,6 +314,12 @@ namespace RedBookPlayer.Discs LoadTrack(CurrentTrackNumber); } + /// + /// Set the value for loading data tracks + /// + /// True to enable loading data tracks, false otherwise + public void SetLoadDataTracks(bool load) => _loadDataTracks = load; + /// public override void SetTotalIndexes() { @@ -329,7 +356,7 @@ namespace RedBookPlayer.Discs if(_image.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC) != true) { // Only generate the TOC if we have it set - if(!App.Settings.GenerateMissingTOC) + if(!_generateMissingToc) { Console.WriteLine("Full TOC not found"); return false; diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer/Discs/OpticalDisc.cs index 701660a..3bc2443 100644 --- a/RedBookPlayer/Discs/OpticalDisc.cs +++ b/RedBookPlayer/Discs/OpticalDisc.cs @@ -90,7 +90,7 @@ namespace RedBookPlayer.Discs /// /// Aaruformat image to load /// True if playback should begin immediately, false otherwise - public abstract void Init(IOpticalMediaImage image, bool autoPlay = false); + public abstract void Init(IOpticalMediaImage image, bool autoPlay); #region Seeking @@ -109,14 +109,15 @@ namespace RedBookPlayer.Discs /// /// Try to move to the previous track, wrapping around if necessary /// - public void PreviousTrack() + /// True to play the hidden track, if it exists + public void PreviousTrack(bool playHiddenTrack) { if(_image == null) return; if(CurrentSector < (ulong)_image.Tracks[CurrentTrackNumber].Indexes[1] + 75) { - if(App.Settings.AllowSkipHiddenTrack && CurrentTrackNumber == 0 && CurrentSector >= 75) + if(playHiddenTrack && CurrentTrackNumber == 0 && CurrentSector >= 75) CurrentSector = 0; else CurrentTrackNumber--; @@ -138,8 +139,9 @@ namespace RedBookPlayer.Discs /// Try to move to the previous track index /// /// True if index changes can trigger a track change, false otherwise + /// True to play the hidden track, if it exists /// True if the track was changed, false otherwise - public abstract bool PreviousIndex(bool changeTrack); + public abstract bool PreviousIndex(bool changeTrack, bool playHiddenTrack); #endregion diff --git a/RedBookPlayer/Discs/OpticalDiscFactory.cs b/RedBookPlayer/Discs/OpticalDiscFactory.cs index 178a87a..76ab981 100644 --- a/RedBookPlayer/Discs/OpticalDiscFactory.cs +++ b/RedBookPlayer/Discs/OpticalDiscFactory.cs @@ -12,9 +12,11 @@ namespace RedBookPlayer.Discs /// Generate an OpticalDisc from an input path /// /// Path to load the image from + /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromPath(string path, bool autoPlay) + public static OpticalDisc GenerateFromPath(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay) { try { @@ -30,7 +32,7 @@ namespace RedBookPlayer.Discs image.Open(filter); // Generate and instantiate the disc - return GenerateFromImage(image, autoPlay); + return GenerateFromImage(image, generateMissingToc, loadDataTracks, autoPlay); } catch { @@ -43,9 +45,11 @@ namespace RedBookPlayer.Discs /// Generate an OpticalDisc from an input IOpticalMediaImage /// /// IOpticalMediaImage to create from + /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool autoPlay) + public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool generateMissingToc, bool loadDataTracks, bool autoPlay) { // If the image is not usable, we don't do anything if(!IsUsableImage(image)) @@ -59,7 +63,7 @@ namespace RedBookPlayer.Discs { case "Compact Disc": case "GD": - opticalDisc = new CompactDisc(); + opticalDisc = new CompactDisc(generateMissingToc, loadDataTracks); break; default: opticalDisc = null; diff --git a/RedBookPlayer/GUI/MainWindow.xaml.cs b/RedBookPlayer/GUI/MainWindow.xaml.cs index 9c46297..d95bb98 100644 --- a/RedBookPlayer/GUI/MainWindow.xaml.cs +++ b/RedBookPlayer/GUI/MainWindow.xaml.cs @@ -121,6 +121,7 @@ namespace RedBookPlayer.GUI if(e.Key == App.Settings.OpenSettingsKey) { settingsWindow = new SettingsWindow(App.Settings); + settingsWindow.Closed += OnSettingsClosed; settingsWindow.Show(); } @@ -217,6 +218,12 @@ namespace RedBookPlayer.GUI } } + public void OnSettingsClosed(object sender, EventArgs e) + { + PlayerView playerView = ContentControl.Content as PlayerView; + playerView?.UpdateViewModel(); + } + #endregion } } \ No newline at end of file diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs index 2319c97..1a31981 100644 --- a/RedBookPlayer/GUI/PlayerView.xaml.cs +++ b/RedBookPlayer/GUI/PlayerView.xaml.cs @@ -26,11 +26,16 @@ namespace RedBookPlayer.GUI /// private Image[] _digits; + /// + /// Initialize the UI based on the default theme + /// + public PlayerView() : this(null) { } + /// /// Initialize the UI based on the currently selected theme /// /// XAML data representing the theme, null for default - public PlayerView(string xaml = null) + public PlayerView(string xaml) { DataContext = new PlayerViewModel(); PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; @@ -41,23 +46,6 @@ namespace RedBookPlayer.GUI #region Helpers - /// - /// Generate a path selection dialog box - /// - /// User-selected path, if possible - public async Task GetPath() - { - var dialog = new OpenFileDialog { AllowMultiple = false }; - List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); - dialog.Filters.Add(new FileDialogFilter() - { - Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", - Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) - }); - - return (await dialog.ShowAsync((Window)Parent.Parent))?.FirstOrDefault(); - } - /// /// Load an image from the path /// @@ -66,7 +54,7 @@ namespace RedBookPlayer.GUI { return await Dispatcher.UIThread.InvokeAsync(() => { - PlayerViewModel.Init(path, App.Settings.AutoPlay, App.Settings.Volume); + PlayerViewModel.Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); if (PlayerViewModel.Initialized) MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); @@ -74,6 +62,11 @@ namespace RedBookPlayer.GUI }); } + /// + /// Update the view model with new settings + /// + public void UpdateViewModel() => PlayerViewModel.SetLoadDataTracks(App.Settings.PlayDataTracks); + /// /// Generate the digit string to be interpreted by the frontend /// @@ -107,21 +100,6 @@ namespace RedBookPlayer.GUI return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); } - /// - /// Get current sector time, accounting for offsets - /// - /// ulong representing the current sector time - private ulong GetCurrentSectorTime() - { - ulong sectorTime = PlayerViewModel.CurrentSector; - if(PlayerViewModel.SectionStartSector != 0) - sectorTime -= PlayerViewModel.SectionStartSector; - else - sectorTime += PlayerViewModel.TimeOffset; - - return sectorTime; - } - /// /// Load the png image for a given character based on the theme /// @@ -150,6 +128,38 @@ namespace RedBookPlayer.GUI } } + /// + /// Get current sector time, accounting for offsets + /// + /// ulong representing the current sector time + private ulong GetCurrentSectorTime() + { + ulong sectorTime = PlayerViewModel.CurrentSector; + if(PlayerViewModel.SectionStartSector != 0) + sectorTime -= PlayerViewModel.SectionStartSector; + else + sectorTime += PlayerViewModel.TimeOffset; + + return sectorTime; + } + + /// + /// Generate a path selection dialog box + /// + /// User-selected path, if possible + private async Task GetPath() + { + var dialog = new OpenFileDialog { AllowMultiple = false }; + List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); + dialog.Filters.Add(new FileDialogFilter() + { + Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", + Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) + }); + + return (await dialog.ShowAsync((Window)Parent.Parent))?.FirstOrDefault(); + } + /// /// Initialize the displayed digits array /// @@ -250,11 +260,11 @@ namespace RedBookPlayer.GUI public void NextTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextTrack(); - public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack(); + public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack(App.Settings.AllowSkipHiddenTrack); public void NextIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextIndex(App.Settings.IndexButtonChangeTrack); - public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack); + public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack, App.Settings.AllowSkipHiddenTrack); public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.FastForward(); diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 654d3cd..87ebbb4 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -185,15 +185,17 @@ namespace RedBookPlayer.GUI /// Initialize the view model with a given image path /// /// Path to the disc image + /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load data tracks for playback [CompactDisc only] /// True if playback should begin immediately, false otherwise /// Default volume between 0 and 100 to use when starting playback - public void Init(string path, bool autoPlay, int defaultVolume) + public void Init(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Stop current playback, if necessary if(Playing != null) Playing = null; // Create and attempt to initialize new Player - _player = new Player(path, autoPlay, defaultVolume); + _player = new Player(path, generateMissingToc, loadDataTracks, autoPlay, defaultVolume); if(Initialized) { _player.PropertyChanged += PlayerStateChanged; @@ -226,7 +228,8 @@ namespace RedBookPlayer.GUI /// /// Move to the previous playable track /// - public void PreviousTrack() => _player?.PreviousTrack(); + /// True to play the hidden track, if it exists + public void PreviousTrack(bool playHiddenTrack) => _player?.PreviousTrack(playHiddenTrack); /// /// Move to the next index @@ -238,7 +241,8 @@ namespace RedBookPlayer.GUI /// Move to the previous index /// /// True if index changes can trigger a track change, false otherwise - public void PreviousIndex(bool changeTrack) => _player?.PreviousIndex(changeTrack); + /// True to play the hidden track, if it exists + public void PreviousIndex(bool changeTrack, bool playHiddenTrack) => _player?.PreviousIndex(changeTrack, playHiddenTrack); /// /// Fast-forward playback by 75 sectors, if possible @@ -260,6 +264,12 @@ namespace RedBookPlayer.GUI /// public void SetDeEmphasis(bool apply) => _player?.SetDeEmphasis(apply); + /// + /// Set the value for loading data tracks [CompactDisc only] + /// + /// True to enable loading data tracks, false otherwise + public void SetLoadDataTracks(bool load) => _player?.SetLoadDataTracks(load); + /// /// Temporarily mute playback /// diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 870200e..85c2263 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -187,9 +187,11 @@ namespace RedBookPlayer.Hardware /// Create a new Player from a given image path /// /// Path to the disc image + /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load data tracks for playback [CompactDisc only] /// True if playback should begin immediately, false otherwise /// Default volume between 0 and 100 to use when starting playback - public Player(string path, bool autoPlay = false, int defaultVolume = 100) + public Player(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Set the internal state for initialization Initialized = false; @@ -197,7 +199,7 @@ namespace RedBookPlayer.Hardware _soundOutput.SetDeEmphasis(false); // Initalize the disc - _opticalDisc = OpticalDiscFactory.GenerateFromPath(path, autoPlay); + _opticalDisc = OpticalDiscFactory.GenerateFromPath(path, generateMissingToc, loadDataTracks, autoPlay); if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -292,7 +294,8 @@ namespace RedBookPlayer.Hardware /// /// Move to the previous playable track /// - public void PreviousTrack() + /// True to play the hidden track, if it exists + public void PreviousTrack(bool playHiddenTrack) { if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -300,7 +303,7 @@ namespace RedBookPlayer.Hardware bool? wasPlaying = Playing; if(wasPlaying == true) Pause(); - _opticalDisc.PreviousTrack(); + _opticalDisc.PreviousTrack(playHiddenTrack); if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); @@ -334,7 +337,8 @@ namespace RedBookPlayer.Hardware /// Move to the previous index /// /// True if index changes can trigger a track change, false otherwise - public void PreviousIndex(bool changeTrack) + /// True to play the hidden track, if it exists + public void PreviousIndex(bool changeTrack, bool playHiddenTrack) { if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -342,7 +346,7 @@ namespace RedBookPlayer.Hardware bool? wasPlaying = Playing; if(wasPlaying == true) Pause(); - _opticalDisc.PreviousIndex(changeTrack); + _opticalDisc.PreviousIndex(changeTrack, playHiddenTrack); if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); @@ -387,6 +391,12 @@ namespace RedBookPlayer.Hardware /// public void SetDeEmphasis(bool apply) => _soundOutput?.SetDeEmphasis(apply); + /// + /// Set the value for loading data tracks [CompactDisc only] + /// + /// True to enable loading data tracks, false otherwise + public void SetLoadDataTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadDataTracks(load); + /// /// Update the player from the current OpticalDisc /// From c43317f0017d96e6da860df84489ff99f87d871f Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 12:06:34 -0700 Subject: [PATCH 09/33] Fix minor playback issues --- RedBookPlayer/Discs/OpticalDisc.cs | 2 +- RedBookPlayer/GUI/PlayerViewModel.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer/Discs/OpticalDisc.cs index 3bc2443..e641bdd 100644 --- a/RedBookPlayer/Discs/OpticalDisc.cs +++ b/RedBookPlayer/Discs/OpticalDisc.cs @@ -40,7 +40,7 @@ namespace RedBookPlayer.Discs /// /// Number of bytes per sector for the current track /// - public int BytesPerSector => _image.Tracks[CurrentTrackNumber].TrackBytesPerSector; + public int BytesPerSector => _image.Tracks[CurrentTrackNumber].TrackRawBytesPerSector; /// /// Represents the track type diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs index 87ebbb4..0712a80 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer/GUI/PlayerViewModel.cs @@ -298,6 +298,7 @@ namespace RedBookPlayer.GUI CurrentTrackNumber = _player.CurrentTrackNumber; CurrentTrackIndex = _player.CurrentTrackIndex; CurrentSector = _player.CurrentSector; + SectionStartSector = _player.SectionStartSector; HiddenTrack = _player.HiddenTrack; From 761afb9344590c6209ac44c7e7ad5258975c4214 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 13:00:20 -0700 Subject: [PATCH 10/33] Remove needless method calls --- RedBookPlayer/Hardware/Player.cs | 42 +++++++++++--------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index 85c2263..b592834 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -216,7 +216,9 @@ namespace RedBookPlayer.Hardware // Mark the player as ready Initialized = true; - SetDiscInformation(); + + // Force a refresh of the disc information + OpticalDiscStateChanged(this, null); } #region Playback @@ -286,8 +288,6 @@ namespace RedBookPlayer.Hardware if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - SetDiscInformation(); - if(wasPlaying == true) Play(); } @@ -307,8 +307,6 @@ namespace RedBookPlayer.Hardware if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - SetDiscInformation(); - if(wasPlaying == true) Play(); } @@ -328,8 +326,6 @@ namespace RedBookPlayer.Hardware if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - SetDiscInformation(); - if(wasPlaying == true) Play(); } @@ -350,8 +346,6 @@ namespace RedBookPlayer.Hardware if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); - SetDiscInformation(); - if(wasPlaying == true) Play(); } @@ -364,7 +358,6 @@ namespace RedBookPlayer.Hardware return; _opticalDisc.CurrentSector = Math.Min(_opticalDisc.TotalSectors, _opticalDisc.CurrentSector + 75); - SetDiscInformation(); } /// @@ -377,8 +370,6 @@ namespace RedBookPlayer.Hardware if(_opticalDisc.CurrentSector >= 75) _opticalDisc.CurrentSector -= 75; - - SetDiscInformation(); } #endregion @@ -400,22 +391,7 @@ namespace RedBookPlayer.Hardware /// /// Update the player from the current OpticalDisc /// - private void OpticalDiscStateChanged(object sender, PropertyChangedEventArgs e) => SetDiscInformation(); - - /// - /// Update the player from the current SoundOutput - /// - private void SoundOutputStateChanged(object sender, PropertyChangedEventArgs e) - { - Playing = _soundOutput.Playing; - ApplyDeEmphasis = _soundOutput.ApplyDeEmphasis; - //Volume = _soundOutput.Volume; - } - - /// - /// Set all current disc information - /// - private void SetDiscInformation() + private void OpticalDiscStateChanged(object sender, PropertyChangedEventArgs e) { CurrentTrackNumber = _opticalDisc.CurrentTrackNumber; CurrentTrackIndex = _opticalDisc.CurrentTrackIndex; @@ -440,6 +416,16 @@ namespace RedBookPlayer.Hardware } } + /// + /// Update the player from the current SoundOutput + /// + private void SoundOutputStateChanged(object sender, PropertyChangedEventArgs e) + { + Playing = _soundOutput.Playing; + ApplyDeEmphasis = _soundOutput.ApplyDeEmphasis; + Volume = _soundOutput.Volume; + } + #endregion } } \ No newline at end of file From 30e4573be6d5ac50ab8f796f9fead0e29c745e08 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 13:04:48 -0700 Subject: [PATCH 11/33] Include SoundOutput in force refresh --- RedBookPlayer/Hardware/Player.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs index b592834..635abcb 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer/Hardware/Player.cs @@ -217,8 +217,9 @@ namespace RedBookPlayer.Hardware // Mark the player as ready Initialized = true; - // Force a refresh of the disc information + // Force a refresh of the state information OpticalDiscStateChanged(this, null); + SoundOutputStateChanged(this, null); } #region Playback From 4efc58d0ef1f4dbe68c21ecaaf5d0f23b626d8ec Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 13:20:06 -0700 Subject: [PATCH 12/33] Split common and GUI code into separate projects --- .../Discs/CompactDisc.cs | 2 +- .../Discs/OpticalDisc.cs | 2 +- .../Discs/OpticalDiscFactory.cs | 2 +- .../Hardware/DeEmphasisFilter.cs | 2 +- .../Hardware/Player.cs | 4 ++-- .../Hardware/PlayerSource.cs | 2 +- .../Hardware/SoundOutput.cs | 4 ++-- .../RedBookPlayer.Common.csproj | 19 ++++++++++++++++++ .../nuget.config | 0 {RedBookPlayer => RedBookPlayer.GUI}/App.xaml | 0 .../App.xaml.cs | 0 .../Assets/-.png | Bin .../Assets/0.png | Bin .../Assets/1.png | Bin .../Assets/2.png | Bin .../Assets/3.png | Bin .../Assets/4.png | Bin .../Assets/5.png | Bin .../Assets/6.png | Bin .../Assets/7.png | Bin .../Assets/8.png | Bin .../Assets/9.png | Bin .../Assets/colon.png | Bin .../GUI => RedBookPlayer.GUI}/MainWindow.xaml | 0 .../MainWindow.xaml.cs | 0 .../GUI => RedBookPlayer.GUI}/PlayerView.xaml | 0 .../PlayerView.xaml.cs | 0 .../PlayerViewModel.cs | 2 +- .../Program.cs | 2 +- .../RedBookPlayer.GUI.csproj | 12 ++--------- .../Settings.cs | 2 +- .../SettingsWindow.xaml | 0 .../SettingsWindow.xaml.cs | 0 RedBookPlayer.GUI/nuget.config | 6 ++++++ RedBookPlayer.sln | 16 ++++++++++++++- 35 files changed, 54 insertions(+), 23 deletions(-) rename {RedBookPlayer => RedBookPlayer.Common}/Discs/CompactDisc.cs (99%) rename {RedBookPlayer => RedBookPlayer.Common}/Discs/OpticalDisc.cs (99%) rename {RedBookPlayer => RedBookPlayer.Common}/Discs/OpticalDiscFactory.cs (99%) rename {RedBookPlayer => RedBookPlayer.Common}/Hardware/DeEmphasisFilter.cs (97%) rename {RedBookPlayer => RedBookPlayer.Common}/Hardware/Player.cs (99%) rename {RedBookPlayer => RedBookPlayer.Common}/Hardware/PlayerSource.cs (96%) rename {RedBookPlayer => RedBookPlayer.Common}/Hardware/SoundOutput.cs (99%) create mode 100644 RedBookPlayer.Common/RedBookPlayer.Common.csproj rename {RedBookPlayer => RedBookPlayer.Common}/nuget.config (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/App.xaml (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/App.xaml.cs (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/-.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/0.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/1.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/2.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/3.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/4.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/5.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/6.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/7.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/8.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/9.png (100%) rename {RedBookPlayer => RedBookPlayer.GUI}/Assets/colon.png (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/MainWindow.xaml (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/MainWindow.xaml.cs (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/PlayerView.xaml (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/PlayerView.xaml.cs (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/PlayerViewModel.cs (99%) rename {RedBookPlayer => RedBookPlayer.GUI}/Program.cs (95%) rename RedBookPlayer/RedBookPlayer.csproj => RedBookPlayer.GUI/RedBookPlayer.GUI.csproj (58%) rename {RedBookPlayer => RedBookPlayer.GUI}/Settings.cs (99%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/SettingsWindow.xaml (100%) rename {RedBookPlayer/GUI => RedBookPlayer.GUI}/SettingsWindow.xaml.cs (100%) create mode 100644 RedBookPlayer.GUI/nuget.config diff --git a/RedBookPlayer/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs similarity index 99% rename from RedBookPlayer/Discs/CompactDisc.cs rename to RedBookPlayer.Common/Discs/CompactDisc.cs index f25a7ec..9078929 100644 --- a/RedBookPlayer/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -9,7 +9,7 @@ using Aaru.Helpers; using ReactiveUI; using static Aaru.Decoders.CD.FullTOC; -namespace RedBookPlayer.Discs +namespace RedBookPlayer.Common.Discs { public class CompactDisc : OpticalDisc, IReactiveObject { diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer.Common/Discs/OpticalDisc.cs similarity index 99% rename from RedBookPlayer/Discs/OpticalDisc.cs rename to RedBookPlayer.Common/Discs/OpticalDisc.cs index e641bdd..0cb5fb3 100644 --- a/RedBookPlayer/Discs/OpticalDisc.cs +++ b/RedBookPlayer.Common/Discs/OpticalDisc.cs @@ -2,7 +2,7 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using ReactiveUI; -namespace RedBookPlayer.Discs +namespace RedBookPlayer.Common.Discs { public abstract class OpticalDisc : ReactiveObject { diff --git a/RedBookPlayer/Discs/OpticalDiscFactory.cs b/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs similarity index 99% rename from RedBookPlayer/Discs/OpticalDiscFactory.cs rename to RedBookPlayer.Common/Discs/OpticalDiscFactory.cs index 76ab981..416cbe4 100644 --- a/RedBookPlayer/Discs/OpticalDiscFactory.cs +++ b/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs @@ -4,7 +4,7 @@ using Aaru.CommonTypes.Metadata; using Aaru.DiscImages; using Aaru.Filters; -namespace RedBookPlayer.Discs +namespace RedBookPlayer.Common.Discs { public static class OpticalDiscFactory { diff --git a/RedBookPlayer/Hardware/DeEmphasisFilter.cs b/RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs similarity index 97% rename from RedBookPlayer/Hardware/DeEmphasisFilter.cs rename to RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs index 546cbb3..d33117e 100644 --- a/RedBookPlayer/Hardware/DeEmphasisFilter.cs +++ b/RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs @@ -1,7 +1,7 @@ using System; using NWaves.Filters.BiQuad; -namespace RedBookPlayer.Hardware +namespace RedBookPlayer.Common.Hardware { /// /// Filter for applying de-emphasis to audio diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs similarity index 99% rename from RedBookPlayer/Hardware/Player.cs rename to RedBookPlayer.Common/Hardware/Player.cs index 635abcb..585f5f4 100644 --- a/RedBookPlayer/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -2,9 +2,9 @@ using System; using System.ComponentModel; using Aaru.CommonTypes.Enums; using ReactiveUI; -using RedBookPlayer.Discs; +using RedBookPlayer.Common.Discs; -namespace RedBookPlayer.Hardware +namespace RedBookPlayer.Common.Hardware { public class Player : ReactiveObject { diff --git a/RedBookPlayer/Hardware/PlayerSource.cs b/RedBookPlayer.Common/Hardware/PlayerSource.cs similarity index 96% rename from RedBookPlayer/Hardware/PlayerSource.cs rename to RedBookPlayer.Common/Hardware/PlayerSource.cs index 17b4a5c..630a8d1 100644 --- a/RedBookPlayer/Hardware/PlayerSource.cs +++ b/RedBookPlayer.Common/Hardware/PlayerSource.cs @@ -2,7 +2,7 @@ using System; using CSCore; using WaveFormat = CSCore.WaveFormat; -namespace RedBookPlayer.Hardware +namespace RedBookPlayer.Common.Hardware { public class PlayerSource : IWaveSource { diff --git a/RedBookPlayer/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs similarity index 99% rename from RedBookPlayer/Hardware/SoundOutput.cs rename to RedBookPlayer.Common/Hardware/SoundOutput.cs index d97466e..2058a26 100644 --- a/RedBookPlayer/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -5,9 +5,9 @@ using CSCore.SoundOut; using NWaves.Audio; using NWaves.Filters.BiQuad; using ReactiveUI; -using RedBookPlayer.Discs; +using RedBookPlayer.Common.Discs; -namespace RedBookPlayer.Hardware +namespace RedBookPlayer.Common.Hardware { public class SoundOutput : ReactiveObject { diff --git a/RedBookPlayer.Common/RedBookPlayer.Common.csproj b/RedBookPlayer.Common/RedBookPlayer.Common.csproj new file mode 100644 index 0000000..bfbe9f2 --- /dev/null +++ b/RedBookPlayer.Common/RedBookPlayer.Common.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + true + + + + + + + + + + + + + + diff --git a/RedBookPlayer/nuget.config b/RedBookPlayer.Common/nuget.config similarity index 100% rename from RedBookPlayer/nuget.config rename to RedBookPlayer.Common/nuget.config diff --git a/RedBookPlayer/App.xaml b/RedBookPlayer.GUI/App.xaml similarity index 100% rename from RedBookPlayer/App.xaml rename to RedBookPlayer.GUI/App.xaml diff --git a/RedBookPlayer/App.xaml.cs b/RedBookPlayer.GUI/App.xaml.cs similarity index 100% rename from RedBookPlayer/App.xaml.cs rename to RedBookPlayer.GUI/App.xaml.cs diff --git a/RedBookPlayer/Assets/-.png b/RedBookPlayer.GUI/Assets/-.png similarity index 100% rename from RedBookPlayer/Assets/-.png rename to RedBookPlayer.GUI/Assets/-.png diff --git a/RedBookPlayer/Assets/0.png b/RedBookPlayer.GUI/Assets/0.png similarity index 100% rename from RedBookPlayer/Assets/0.png rename to RedBookPlayer.GUI/Assets/0.png diff --git a/RedBookPlayer/Assets/1.png b/RedBookPlayer.GUI/Assets/1.png similarity index 100% rename from RedBookPlayer/Assets/1.png rename to RedBookPlayer.GUI/Assets/1.png diff --git a/RedBookPlayer/Assets/2.png b/RedBookPlayer.GUI/Assets/2.png similarity index 100% rename from RedBookPlayer/Assets/2.png rename to RedBookPlayer.GUI/Assets/2.png diff --git a/RedBookPlayer/Assets/3.png b/RedBookPlayer.GUI/Assets/3.png similarity index 100% rename from RedBookPlayer/Assets/3.png rename to RedBookPlayer.GUI/Assets/3.png diff --git a/RedBookPlayer/Assets/4.png b/RedBookPlayer.GUI/Assets/4.png similarity index 100% rename from RedBookPlayer/Assets/4.png rename to RedBookPlayer.GUI/Assets/4.png diff --git a/RedBookPlayer/Assets/5.png b/RedBookPlayer.GUI/Assets/5.png similarity index 100% rename from RedBookPlayer/Assets/5.png rename to RedBookPlayer.GUI/Assets/5.png diff --git a/RedBookPlayer/Assets/6.png b/RedBookPlayer.GUI/Assets/6.png similarity index 100% rename from RedBookPlayer/Assets/6.png rename to RedBookPlayer.GUI/Assets/6.png diff --git a/RedBookPlayer/Assets/7.png b/RedBookPlayer.GUI/Assets/7.png similarity index 100% rename from RedBookPlayer/Assets/7.png rename to RedBookPlayer.GUI/Assets/7.png diff --git a/RedBookPlayer/Assets/8.png b/RedBookPlayer.GUI/Assets/8.png similarity index 100% rename from RedBookPlayer/Assets/8.png rename to RedBookPlayer.GUI/Assets/8.png diff --git a/RedBookPlayer/Assets/9.png b/RedBookPlayer.GUI/Assets/9.png similarity index 100% rename from RedBookPlayer/Assets/9.png rename to RedBookPlayer.GUI/Assets/9.png diff --git a/RedBookPlayer/Assets/colon.png b/RedBookPlayer.GUI/Assets/colon.png similarity index 100% rename from RedBookPlayer/Assets/colon.png rename to RedBookPlayer.GUI/Assets/colon.png diff --git a/RedBookPlayer/GUI/MainWindow.xaml b/RedBookPlayer.GUI/MainWindow.xaml similarity index 100% rename from RedBookPlayer/GUI/MainWindow.xaml rename to RedBookPlayer.GUI/MainWindow.xaml diff --git a/RedBookPlayer/GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs similarity index 100% rename from RedBookPlayer/GUI/MainWindow.xaml.cs rename to RedBookPlayer.GUI/MainWindow.xaml.cs diff --git a/RedBookPlayer/GUI/PlayerView.xaml b/RedBookPlayer.GUI/PlayerView.xaml similarity index 100% rename from RedBookPlayer/GUI/PlayerView.xaml rename to RedBookPlayer.GUI/PlayerView.xaml diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs similarity index 100% rename from RedBookPlayer/GUI/PlayerView.xaml.cs rename to RedBookPlayer.GUI/PlayerView.xaml.cs diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer.GUI/PlayerViewModel.cs similarity index 99% rename from RedBookPlayer/GUI/PlayerViewModel.cs rename to RedBookPlayer.GUI/PlayerViewModel.cs index 0712a80..b3984d7 100644 --- a/RedBookPlayer/GUI/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/PlayerViewModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel; using ReactiveUI; -using RedBookPlayer.Hardware; +using RedBookPlayer.Common.Hardware; namespace RedBookPlayer.GUI { diff --git a/RedBookPlayer/Program.cs b/RedBookPlayer.GUI/Program.cs similarity index 95% rename from RedBookPlayer/Program.cs rename to RedBookPlayer.GUI/Program.cs index 731908b..b1919b6 100644 --- a/RedBookPlayer/Program.cs +++ b/RedBookPlayer.GUI/Program.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using Avalonia; using Avalonia.Logging.Serilog; -namespace RedBookPlayer +namespace RedBookPlayer.GUI { internal class Program { diff --git a/RedBookPlayer/RedBookPlayer.csproj b/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj similarity index 58% rename from RedBookPlayer/RedBookPlayer.csproj rename to RedBookPlayer.GUI/RedBookPlayer.GUI.csproj index bc7ee1e..d2a03b6 100644 --- a/RedBookPlayer/RedBookPlayer.csproj +++ b/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj @@ -2,8 +2,8 @@ WinExe netcoreapp3.1 - true win-x64;linux-x64 + linux-x64 embedded @@ -21,17 +21,9 @@ - - - - - - - - - + diff --git a/RedBookPlayer/Settings.cs b/RedBookPlayer.GUI/Settings.cs similarity index 99% rename from RedBookPlayer/Settings.cs rename to RedBookPlayer.GUI/Settings.cs index 36e95fb..2329e7c 100644 --- a/RedBookPlayer/Settings.cs +++ b/RedBookPlayer.GUI/Settings.cs @@ -4,7 +4,7 @@ using System.Text.Json; using Avalonia.Input; using RedBookPlayer.GUI; -namespace RedBookPlayer +namespace RedBookPlayer.GUI { public class Settings { diff --git a/RedBookPlayer/GUI/SettingsWindow.xaml b/RedBookPlayer.GUI/SettingsWindow.xaml similarity index 100% rename from RedBookPlayer/GUI/SettingsWindow.xaml rename to RedBookPlayer.GUI/SettingsWindow.xaml diff --git a/RedBookPlayer/GUI/SettingsWindow.xaml.cs b/RedBookPlayer.GUI/SettingsWindow.xaml.cs similarity index 100% rename from RedBookPlayer/GUI/SettingsWindow.xaml.cs rename to RedBookPlayer.GUI/SettingsWindow.xaml.cs diff --git a/RedBookPlayer.GUI/nuget.config b/RedBookPlayer.GUI/nuget.config new file mode 100644 index 0000000..7c07e22 --- /dev/null +++ b/RedBookPlayer.GUI/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RedBookPlayer.sln b/RedBookPlayer.sln index a622fe1..a36debc 100644 --- a/RedBookPlayer.sln +++ b/RedBookPlayer.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31321.278 MinimumVisualStudioVersion = 15.0.26124.0 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedBookPlayer", "RedBookPlayer\RedBookPlayer.csproj", "{94944959-0352-4ABF-9C5C-19FF33747ECE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedBookPlayer.GUI", "RedBookPlayer.GUI\RedBookPlayer.GUI.csproj", "{94944959-0352-4ABF-9C5C-19FF33747ECE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cscore", "cscore", "{9A371299-4C59-4E46-9C3B-4FE024017491}" EndProject @@ -40,6 +40,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedBookPlayer.Common", "RedBookPlayer.Common\RedBookPlayer.Common.csproj", "{462A3B8E-A5D4-4539-8469-1647B47AB2A8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -194,6 +196,18 @@ Global {ED8E11B7-786F-4EFF-9E4C-B937B7A2DE89}.Release|x64.Build.0 = Release|Any CPU {ED8E11B7-786F-4EFF-9E4C-B937B7A2DE89}.Release|x86.ActiveCfg = Release|Any CPU {ED8E11B7-786F-4EFF-9E4C-B937B7A2DE89}.Release|x86.Build.0 = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|x64.ActiveCfg = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|x64.Build.0 = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|x86.ActiveCfg = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Debug|x86.Build.0 = Debug|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|Any CPU.Build.0 = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|x64.ActiveCfg = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|x64.Build.0 = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|x86.ActiveCfg = Release|Any CPU + {462A3B8E-A5D4-4539-8469-1647B47AB2A8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 49d8d8c23a35f6cc7c5abd6b544edfcdf27d776e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 16:20:34 -0700 Subject: [PATCH 13/33] Fix volume setting; move view model to common --- RedBookPlayer.Common/Hardware/Player.cs | 6 ++++++ RedBookPlayer.Common/Hardware/SoundOutput.cs | 6 ++++++ .../PlayerViewModel.cs | 14 ++++++++++---- RedBookPlayer.GUI/MainWindow.xaml.cs | 4 ++-- RedBookPlayer.GUI/PlayerView.xaml | 1 + RedBookPlayer.GUI/PlayerView.xaml.cs | 5 +++-- 6 files changed, 28 insertions(+), 8 deletions(-) rename {RedBookPlayer.GUI => RedBookPlayer.Common}/PlayerViewModel.cs (95%) diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 585f5f4..0ae2563 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -389,6 +389,12 @@ namespace RedBookPlayer.Common.Hardware /// True to enable loading data tracks, false otherwise public void SetLoadDataTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadDataTracks(load); + /// + /// Set the value for the volume + /// + /// New volume value + public void SetVolume(int volume) => _soundOutput?.SetVolume(volume); + /// /// Update the player from the current OpticalDisc /// diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index 2058a26..77104b3 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -303,6 +303,12 @@ namespace RedBookPlayer.Common.Hardware /// public void SetDeEmphasis(bool apply) => ApplyDeEmphasis = apply; + /// + /// Set the value for the volume + /// + /// New volume value + public void SetVolume(int volume) => Volume = volume; + /// /// Sets or resets the de-emphasis filters /// diff --git a/RedBookPlayer.GUI/PlayerViewModel.cs b/RedBookPlayer.Common/PlayerViewModel.cs similarity index 95% rename from RedBookPlayer.GUI/PlayerViewModel.cs rename to RedBookPlayer.Common/PlayerViewModel.cs index b3984d7..8e4cf5b 100644 --- a/RedBookPlayer.GUI/PlayerViewModel.cs +++ b/RedBookPlayer.Common/PlayerViewModel.cs @@ -2,7 +2,7 @@ using System.ComponentModel; using ReactiveUI; using RedBookPlayer.Common.Hardware; -namespace RedBookPlayer.GUI +namespace RedBookPlayer.Common { public class PlayerViewModel : ReactiveObject { @@ -170,7 +170,7 @@ namespace RedBookPlayer.GUI public int Volume { get => _volume; - set => this.RaiseAndSetIfChanged(ref _volume, value); + private set => this.RaiseAndSetIfChanged(ref _volume, value); } private bool? _playing; @@ -270,6 +270,12 @@ namespace RedBookPlayer.GUI /// True to enable loading data tracks, false otherwise public void SetLoadDataTracks(bool load) => _player?.SetLoadDataTracks(load); + /// + /// Set the value for the volume + /// + /// New volume value + public void SetVolume(int volume) => _player?.SetVolume(volume); + /// /// Temporarily mute playback /// @@ -278,11 +284,11 @@ namespace RedBookPlayer.GUI if(_lastVolume == null) { _lastVolume = Volume; - Volume = 0; + _player?.SetVolume(0); } else { - Volume = _lastVolume.Value; + _player?.SetVolume(_lastVolume.Value); _lastVolume = null; } } diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs index d95bb98..a04d83f 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/MainWindow.xaml.cs @@ -189,7 +189,7 @@ namespace RedBookPlayer.GUI increment *= 5; if(playerView?.PlayerViewModel?.Volume != null) - playerView.PlayerViewModel.Volume += increment; + playerView.PlayerViewModel.SetVolume(playerView.PlayerViewModel.Volume + increment); } // Volume Down @@ -202,7 +202,7 @@ namespace RedBookPlayer.GUI decrement *= 5; if (playerView?.PlayerViewModel?.Volume != null) - playerView.PlayerViewModel.Volume -= decrement; + playerView.PlayerViewModel.SetVolume(playerView.PlayerViewModel.Volume - decrement); } // Mute Toggle diff --git a/RedBookPlayer.GUI/PlayerView.xaml b/RedBookPlayer.GUI/PlayerView.xaml index 9221ecd..ab9fcec 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml +++ b/RedBookPlayer.GUI/PlayerView.xaml @@ -98,6 +98,7 @@ 4CH HIDDEN HIDDEN + \ No newline at end of file diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index 1a31981..ef7d3b9 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -11,6 +11,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Threading; +using RedBookPlayer.Common; namespace RedBookPlayer.GUI { @@ -270,9 +271,9 @@ namespace RedBookPlayer.GUI public void RewindButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Rewind(); - public void VolumeUpButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Volume++; + public void VolumeUpButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetVolume(PlayerViewModel.Volume + 1); - public void VolumeDownButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Volume--; + public void VolumeDownButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetVolume(PlayerViewModel.Volume - 1); public void MuteToggleButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ToggleMute(); From 32a686d4d1b4675b974c29f9fcb46ef0642009eb Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 16:23:50 -0700 Subject: [PATCH 14/33] Ensure access modifiers set properly --- RedBookPlayer.Common/Discs/OpticalDisc.cs | 1 + RedBookPlayer.Common/Hardware/Player.cs | 2 +- RedBookPlayer.Common/Hardware/SoundOutput.cs | 2 +- RedBookPlayer.Common/PlayerViewModel.cs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RedBookPlayer.Common/Discs/OpticalDisc.cs b/RedBookPlayer.Common/Discs/OpticalDisc.cs index 0cb5fb3..555c630 100644 --- a/RedBookPlayer.Common/Discs/OpticalDisc.cs +++ b/RedBookPlayer.Common/Discs/OpticalDisc.cs @@ -26,6 +26,7 @@ namespace RedBookPlayer.Common.Discs /// /// Current sector number /// + /// TODO: Maske this `protected set` public abstract ulong CurrentSector { get; set; } /// diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 0ae2563..03acf26 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -160,7 +160,7 @@ namespace RedBookPlayer.Common.Hardware public int Volume { get => _volume; - set => this.RaiseAndSetIfChanged(ref _volume, value); + private set => this.RaiseAndSetIfChanged(ref _volume, value); } private bool? _playing; diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index 77104b3..cdb0def 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -42,7 +42,7 @@ namespace RedBookPlayer.Common.Hardware public int Volume { get => _volume; - set + private set { int tempVolume = value; if(value > 100) diff --git a/RedBookPlayer.Common/PlayerViewModel.cs b/RedBookPlayer.Common/PlayerViewModel.cs index 8e4cf5b..635b4a4 100644 --- a/RedBookPlayer.Common/PlayerViewModel.cs +++ b/RedBookPlayer.Common/PlayerViewModel.cs @@ -53,7 +53,7 @@ namespace RedBookPlayer.Common public ulong SectionStartSector { get => _sectionStartSector; - protected set => this.RaiseAndSetIfChanged(ref _sectionStartSector, value); + private set => this.RaiseAndSetIfChanged(ref _sectionStartSector, value); } /// From bd159b95a5561d2c050342cd540e21cba4e6222c Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 16:30:38 -0700 Subject: [PATCH 15/33] Fix last access modifier --- RedBookPlayer.Common/Discs/CompactDisc.cs | 2 +- RedBookPlayer.Common/Discs/OpticalDisc.cs | 9 +++++++-- RedBookPlayer.Common/Hardware/Player.cs | 4 ++-- RedBookPlayer.Common/Hardware/SoundOutput.cs | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 9078929..eb51c0c 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -99,7 +99,7 @@ namespace RedBookPlayer.Common.Discs public override ulong CurrentSector { get => _currentSector; - set + protected set { // Unset image means we can't do anything if(_image == null) diff --git a/RedBookPlayer.Common/Discs/OpticalDisc.cs b/RedBookPlayer.Common/Discs/OpticalDisc.cs index 555c630..985b741 100644 --- a/RedBookPlayer.Common/Discs/OpticalDisc.cs +++ b/RedBookPlayer.Common/Discs/OpticalDisc.cs @@ -26,8 +26,7 @@ namespace RedBookPlayer.Common.Discs /// /// Current sector number /// - /// TODO: Maske this `protected set` - public abstract ulong CurrentSector { get; set; } + public abstract ulong CurrentSector { get; protected set; } /// /// Represents the sector starting the section @@ -165,6 +164,12 @@ namespace RedBookPlayer.Common.Discs /// public abstract void SetTotalIndexes(); + /// + /// Set the current sector + /// + /// New sector number to use + public void SetCurrentSector(ulong sector) => CurrentSector = sector; + /// /// Load the desired track, if possible /// diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 03acf26..6a4d20e 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -358,7 +358,7 @@ namespace RedBookPlayer.Common.Hardware if(_opticalDisc == null || !_opticalDisc.Initialized) return; - _opticalDisc.CurrentSector = Math.Min(_opticalDisc.TotalSectors, _opticalDisc.CurrentSector + 75); + _opticalDisc.SetCurrentSector(Math.Min(_opticalDisc.TotalSectors, _opticalDisc.CurrentSector + 75)); } /// @@ -370,7 +370,7 @@ namespace RedBookPlayer.Common.Hardware return; if(_opticalDisc.CurrentSector >= 75) - _opticalDisc.CurrentSector -= 75; + _opticalDisc.SetCurrentSector(_opticalDisc.CurrentSector - 75); } #endregion diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index cdb0def..4207801 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -251,7 +251,7 @@ namespace RedBookPlayer.Common.Hardware _currentSectorReadPosition += count; if(_currentSectorReadPosition >= _opticalDisc.BytesPerSector) { - _opticalDisc.CurrentSector += (ulong)(_currentSectorReadPosition / _opticalDisc.BytesPerSector); + _opticalDisc.SetCurrentSector(_opticalDisc.CurrentSector + (ulong)(_currentSectorReadPosition / _opticalDisc.BytesPerSector)); _currentSectorReadPosition %= _opticalDisc.BytesPerSector; } From 4c712677e0586f10f1552def1bbeb5ff4408a2a5 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 22:13:00 -0700 Subject: [PATCH 16/33] Fix rendering of hidden tracks --- RedBookPlayer.Common/Discs/CompactDisc.cs | 154 +++++++++++++++--- RedBookPlayer.Common/Discs/OpticalDisc.cs | 33 +--- .../Discs/OpticalDiscFactory.cs | 10 +- RedBookPlayer.Common/Hardware/Player.cs | 21 ++- RedBookPlayer.Common/PlayerViewModel.cs | 17 +- RedBookPlayer.GUI/PlayerView.xaml.cs | 16 +- RedBookPlayer.GUI/Settings.cs | 9 +- RedBookPlayer.GUI/SettingsWindow.xaml | 4 +- 8 files changed, 182 insertions(+), 82 deletions(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index eb51c0c..8f123ef 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -34,16 +34,30 @@ namespace RedBookPlayer.Common.Discs do { - // Ensure that the value is valid, wrapping around if necessary - if(cachedValue >= _image.Tracks.Count) - cachedValue = 0; - else if(cachedValue < 0) - cachedValue = _image.Tracks.Count - 1; + // If we're over the last track, wrap around + if(cachedValue > _image.Tracks.Max(t => t.TrackSequence)) + { + cachedValue = (int)_image.Tracks.Min(t => t.TrackSequence); + if(cachedValue == 0 && !_loadHiddenTracks) + cachedValue++; + } + + // If we're under the first track and we're not loading hidden tracks, wrap around + else if(cachedValue < 1 && !_loadHiddenTracks) + { + cachedValue = (int)_image.Tracks.Max(t => t.TrackSequence); + } + + // If we're under the first valid track, wrap around + else if(cachedValue < _image.Tracks.Min(t => t.TrackSequence)) + { + cachedValue = (int)_image.Tracks.Max(t => t.TrackSequence); + } cachedTrackNumber = cachedValue; // Cache the current track for easy access - Track track = _image.Tracks[cachedTrackNumber]; + Track track = GetTrack(cachedTrackNumber); // Set track flags from subchannel data, if possible SetTrackFlags(track); @@ -78,7 +92,7 @@ namespace RedBookPlayer.Common.Discs return; // Cache the current track for easy access - Track track = _image.Tracks[CurrentTrackNumber]; + Track track = GetTrack(CurrentTrackNumber); // Ensure that the value is valid, wrapping around if necessary ushort fixedValue = value; @@ -106,18 +120,18 @@ namespace RedBookPlayer.Common.Discs return; // Cache the current track for easy access - Track track = _image.Tracks[CurrentTrackNumber]; + Track track = GetTrack(CurrentTrackNumber); this.RaiseAndSetIfChanged(ref _currentSector, value); - if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= _image.Tracks[CurrentTrackNumber + 1].TrackStartSector) + if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= GetTrack(CurrentTrackNumber + 1).TrackStartSector) || (CurrentTrackNumber > 0 && CurrentSector < track.TrackStartSector)) { foreach(Track trackData in _image.Tracks.ToArray().Reverse()) { if(CurrentSector >= trackData.TrackStartSector) { - CurrentTrackNumber = (int)trackData.TrackSequence - 1; + CurrentTrackNumber = (int)trackData.TrackSequence; break; } } @@ -136,6 +150,9 @@ namespace RedBookPlayer.Common.Discs } } + /// + public override int BytesPerSector => GetTrack(CurrentTrackNumber).TrackRawBytesPerSector; + /// /// Represents the 4CH flag /// @@ -206,6 +223,11 @@ namespace RedBookPlayer.Common.Discs /// private bool _loadDataTracks = false; + /// + /// Indicate if hidden tracks should be loaded + /// + private bool _loadHiddenTracks = false; + /// /// Current disc table of contents /// @@ -217,10 +239,12 @@ namespace RedBookPlayer.Common.Discs /// Constructor /// /// Generate a TOC if the disc is missing one + /// Load hidden tracks for playback /// Load data tracks for playback - public CompactDisc(bool generateMissingToc, bool loadDataTracks) + public CompactDisc(bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks) { _generateMissingToc = generateMissingToc; + _loadHiddenTracks = loadHiddenTracks; _loadDataTracks = loadDataTracks; } @@ -257,47 +281,101 @@ namespace RedBookPlayer.Common.Discs #region Seeking + /// + public override void NextTrack() + { + if(_image == null) + return; + + CurrentTrackNumber++; + LoadTrack(CurrentTrackNumber); + } + + /// + public override void PreviousTrack() + { + if(_image == null) + return; + + CurrentTrackNumber--; + LoadTrack(CurrentTrackNumber); + } + /// public override bool NextIndex(bool changeTrack) { if(_image == null) return false; - if(CurrentTrackIndex + 1 > _image.Tracks[CurrentTrackNumber].Indexes.Keys.Max()) + // Cache the current track for easy access + Track track = GetTrack(CurrentTrackNumber); + + // If the index is greater than the highest index, change tracks if needed + if(CurrentTrackIndex + 1 > track.Indexes.Keys.Max()) { if(changeTrack) { NextTrack(); - CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes.Values.Min(); + CurrentSector = (ulong)GetTrack(CurrentTrackNumber).Indexes.Values.Min(); return true; } } + + // If the next index has an invalid offset, change tracks if needed + else if(track.Indexes[(ushort)(CurrentTrackIndex + 1)] < 0) + { + if(changeTrack) + { + NextTrack(); + CurrentSector = (ulong)GetTrack(CurrentTrackNumber).Indexes.Values.Min(); + return true; + } + } + + // Otherwise, just move to the next index else { - CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes[++CurrentTrackIndex]; + CurrentSector = (ulong)track.Indexes[++CurrentTrackIndex]; } return false; } /// - public override bool PreviousIndex(bool changeTrack, bool playHiddenTrack) + public override bool PreviousIndex(bool changeTrack) { if(_image == null) return false; - if(CurrentTrackIndex - 1 < _image.Tracks[CurrentTrackNumber].Indexes.Keys.Min()) + // Cache the current track for easy access + Track track = GetTrack(CurrentTrackNumber); + + // If the index is less than the lowest index, change tracks if needed + if(CurrentTrackIndex - 1 < track.Indexes.Keys.Min()) { if(changeTrack) { - PreviousTrack(playHiddenTrack); - CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes.Values.Max(); + PreviousTrack(); + CurrentSector = (ulong)GetTrack(CurrentTrackNumber).Indexes.Values.Max(); return true; } } + + // If the previous index has an invalid offset, change tracks if needed + else if (track.Indexes[(ushort)(CurrentTrackIndex - 1)] < 0) + { + if(changeTrack) + { + PreviousTrack(); + CurrentSector = (ulong)GetTrack(CurrentTrackNumber).Indexes.Values.Max(); + return true; + } + } + + // Otherwise, just move to the previous index else { - CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes[--CurrentTrackIndex]; + CurrentSector = (ulong)track.Indexes[--CurrentTrackIndex]; } return false; @@ -310,7 +388,7 @@ namespace RedBookPlayer.Common.Discs /// public override void LoadFirstTrack() { - CurrentTrackNumber = 0; + CurrentTrackNumber = 1; LoadTrack(CurrentTrackNumber); } @@ -320,6 +398,12 @@ namespace RedBookPlayer.Common.Discs /// True to enable loading data tracks, false otherwise public void SetLoadDataTracks(bool load) => _loadDataTracks = load; + /// + /// Set the value for loading hidden tracks + /// + /// True to enable loading hidden tracks, false otherwise + public void SetLoadHiddenTracks(bool load) => _loadHiddenTracks = load; + /// public override void SetTotalIndexes() { @@ -330,17 +414,37 @@ namespace RedBookPlayer.Common.Discs } /// - protected override void LoadTrack(int track) + protected override void LoadTrack(int trackNumber) { if(_image == null) return; - if(track < 0 || track >= _image.Tracks.Count) + // If the track number is invalid, just return + if(trackNumber < _image.Tracks.Min(t => t.TrackSequence) || trackNumber > _image.Tracks.Max(t => t.TrackSequence)) return; - ushort firstIndex = _image.Tracks[track].Indexes.Keys.Min(); - int firstSector = _image.Tracks[track].Indexes[firstIndex]; - CurrentSector = (ulong)(firstSector >= 0 ? firstSector : _image.Tracks[track].Indexes[1]); + // Cache the current track for easy access + Track track = GetTrack(trackNumber); + + // Select the first index that has a sector offset greater than or equal to 0 + CurrentSector = (ulong)(track?.Indexes.OrderBy(kvp => kvp.Key).First(kvp => kvp.Value >= 0).Value ?? 0); + } + + /// + /// Get the track with the given sequence value, if possible + /// + /// Track number to retrieve + /// Track object for the requested sequence, null on error + private Track GetTrack(int trackNumber) + { + try + { + return _image.Tracks.FirstOrDefault(t => t.TrackSequence == trackNumber); + } + catch + { + return null; + } } /// diff --git a/RedBookPlayer.Common/Discs/OpticalDisc.cs b/RedBookPlayer.Common/Discs/OpticalDisc.cs index 985b741..b522574 100644 --- a/RedBookPlayer.Common/Discs/OpticalDisc.cs +++ b/RedBookPlayer.Common/Discs/OpticalDisc.cs @@ -40,7 +40,7 @@ namespace RedBookPlayer.Common.Discs /// /// Number of bytes per sector for the current track /// - public int BytesPerSector => _image.Tracks[CurrentTrackNumber].TrackRawBytesPerSector; + public abstract int BytesPerSector { get; } /// /// Represents the track type @@ -97,36 +97,12 @@ namespace RedBookPlayer.Common.Discs /// /// Try to move to the next track, wrapping around if necessary /// - public void NextTrack() - { - if(_image == null) - return; - - CurrentTrackNumber++; - LoadTrack(CurrentTrackNumber); - } + public abstract void NextTrack(); /// /// Try to move to the previous track, wrapping around if necessary /// - /// True to play the hidden track, if it exists - public void PreviousTrack(bool playHiddenTrack) - { - if(_image == null) - return; - - if(CurrentSector < (ulong)_image.Tracks[CurrentTrackNumber].Indexes[1] + 75) - { - if(playHiddenTrack && CurrentTrackNumber == 0 && CurrentSector >= 75) - CurrentSector = 0; - else - CurrentTrackNumber--; - } - else - CurrentTrackNumber--; - - LoadTrack(CurrentTrackNumber); - } + public abstract void PreviousTrack(); /// /// Try to move to the next track index @@ -139,9 +115,8 @@ namespace RedBookPlayer.Common.Discs /// Try to move to the previous track index /// /// True if index changes can trigger a track change, false otherwise - /// True to play the hidden track, if it exists /// True if the track was changed, false otherwise - public abstract bool PreviousIndex(bool changeTrack, bool playHiddenTrack); + public abstract bool PreviousIndex(bool changeTrack); #endregion diff --git a/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs b/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs index 416cbe4..52ad9d8 100644 --- a/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs +++ b/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs @@ -13,10 +13,11 @@ namespace RedBookPlayer.Common.Discs /// /// Path to load the image from /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load hidden tracks for playback [CompactDisc only] /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromPath(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay) + public static OpticalDisc GenerateFromPath(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) { try { @@ -32,7 +33,7 @@ namespace RedBookPlayer.Common.Discs image.Open(filter); // Generate and instantiate the disc - return GenerateFromImage(image, generateMissingToc, loadDataTracks, autoPlay); + return GenerateFromImage(image, generateMissingToc, loadHiddenTracks, loadDataTracks, autoPlay); } catch { @@ -46,10 +47,11 @@ namespace RedBookPlayer.Common.Discs /// /// IOpticalMediaImage to create from /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load hidden tracks for playback [CompactDisc only] /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool generateMissingToc, bool loadDataTracks, bool autoPlay) + public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) { // If the image is not usable, we don't do anything if(!IsUsableImage(image)) @@ -63,7 +65,7 @@ namespace RedBookPlayer.Common.Discs { case "Compact Disc": case "GD": - opticalDisc = new CompactDisc(generateMissingToc, loadDataTracks); + opticalDisc = new CompactDisc(generateMissingToc, loadHiddenTracks, loadDataTracks); break; default: opticalDisc = null; diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 6a4d20e..0fdc4d4 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -188,10 +188,11 @@ namespace RedBookPlayer.Common.Hardware /// /// Path to the disc image /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load hidden tracks for playback [CompactDisc only] /// Load data tracks for playback [CompactDisc only] /// True if playback should begin immediately, false otherwise /// Default volume between 0 and 100 to use when starting playback - public Player(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay, int defaultVolume) + public Player(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Set the internal state for initialization Initialized = false; @@ -199,7 +200,7 @@ namespace RedBookPlayer.Common.Hardware _soundOutput.SetDeEmphasis(false); // Initalize the disc - _opticalDisc = OpticalDiscFactory.GenerateFromPath(path, generateMissingToc, loadDataTracks, autoPlay); + _opticalDisc = OpticalDiscFactory.GenerateFromPath(path, generateMissingToc, loadHiddenTracks, loadDataTracks, autoPlay); if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -295,8 +296,7 @@ namespace RedBookPlayer.Common.Hardware /// /// Move to the previous playable track /// - /// True to play the hidden track, if it exists - public void PreviousTrack(bool playHiddenTrack) + public void PreviousTrack() { if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -304,7 +304,7 @@ namespace RedBookPlayer.Common.Hardware bool? wasPlaying = Playing; if(wasPlaying == true) Pause(); - _opticalDisc.PreviousTrack(playHiddenTrack); + _opticalDisc.PreviousTrack(); if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); @@ -334,8 +334,7 @@ namespace RedBookPlayer.Common.Hardware /// Move to the previous index /// /// True if index changes can trigger a track change, false otherwise - /// True to play the hidden track, if it exists - public void PreviousIndex(bool changeTrack, bool playHiddenTrack) + public void PreviousIndex(bool changeTrack) { if(_opticalDisc == null || !_opticalDisc.Initialized) return; @@ -343,7 +342,7 @@ namespace RedBookPlayer.Common.Hardware bool? wasPlaying = Playing; if(wasPlaying == true) Pause(); - _opticalDisc.PreviousIndex(changeTrack, playHiddenTrack); + _opticalDisc.PreviousIndex(changeTrack); if(_opticalDisc is CompactDisc compactDisc) _soundOutput.SetDeEmphasis(compactDisc.TrackHasEmphasis); @@ -389,6 +388,12 @@ namespace RedBookPlayer.Common.Hardware /// True to enable loading data tracks, false otherwise public void SetLoadDataTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadDataTracks(load); + /// + /// Set the value for loading hidden tracks [CompactDisc only] + /// + /// True to enable loading hidden tracks, false otherwise + public void SetLoadHiddenTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadHiddenTracks(load); + /// /// Set the value for the volume /// diff --git a/RedBookPlayer.Common/PlayerViewModel.cs b/RedBookPlayer.Common/PlayerViewModel.cs index 635b4a4..04c62a2 100644 --- a/RedBookPlayer.Common/PlayerViewModel.cs +++ b/RedBookPlayer.Common/PlayerViewModel.cs @@ -186,16 +186,17 @@ namespace RedBookPlayer.Common /// /// Path to the disc image /// Generate a TOC if the disc is missing one [CompactDisc only] + /// Load hidden tracks for playback [CompactDisc only] /// Load data tracks for playback [CompactDisc only] /// True if playback should begin immediately, false otherwise /// Default volume between 0 and 100 to use when starting playback - public void Init(string path, bool generateMissingToc, bool loadDataTracks, bool autoPlay, int defaultVolume) + public void Init(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Stop current playback, if necessary if(Playing != null) Playing = null; // Create and attempt to initialize new Player - _player = new Player(path, generateMissingToc, loadDataTracks, autoPlay, defaultVolume); + _player = new Player(path, generateMissingToc, loadHiddenTracks, loadDataTracks, autoPlay, defaultVolume); if(Initialized) { _player.PropertyChanged += PlayerStateChanged; @@ -228,8 +229,7 @@ namespace RedBookPlayer.Common /// /// Move to the previous playable track /// - /// True to play the hidden track, if it exists - public void PreviousTrack(bool playHiddenTrack) => _player?.PreviousTrack(playHiddenTrack); + public void PreviousTrack() => _player?.PreviousTrack(); /// /// Move to the next index @@ -241,8 +241,7 @@ namespace RedBookPlayer.Common /// Move to the previous index /// /// True if index changes can trigger a track change, false otherwise - /// True to play the hidden track, if it exists - public void PreviousIndex(bool changeTrack, bool playHiddenTrack) => _player?.PreviousIndex(changeTrack, playHiddenTrack); + public void PreviousIndex(bool changeTrack) => _player?.PreviousIndex(changeTrack); /// /// Fast-forward playback by 75 sectors, if possible @@ -270,6 +269,12 @@ namespace RedBookPlayer.Common /// True to enable loading data tracks, false otherwise public void SetLoadDataTracks(bool load) => _player?.SetLoadDataTracks(load); + /// + /// Set the value for loading hidden tracks [CompactDisc only] + /// + /// True to enable loading hidden tracks, false otherwise + public void SetLoadHiddenTracks(bool load) => _player?.SetLoadHiddenTracks(load); + /// /// Set the value for the volume /// diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index ef7d3b9..16f43cc 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -55,7 +55,7 @@ namespace RedBookPlayer.GUI { return await Dispatcher.UIThread.InvokeAsync(() => { - PlayerViewModel.Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); + PlayerViewModel.Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayHiddenTracks, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); if (PlayerViewModel.Initialized) MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); @@ -66,7 +66,11 @@ namespace RedBookPlayer.GUI /// /// Update the view model with new settings /// - public void UpdateViewModel() => PlayerViewModel.SetLoadDataTracks(App.Settings.PlayDataTracks); + public void UpdateViewModel() + { + PlayerViewModel.SetLoadDataTracks(App.Settings.PlayDataTracks); + PlayerViewModel.SetLoadHiddenTracks(App.Settings.PlayHiddenTracks); + } /// /// Generate the digit string to be interpreted by the frontend @@ -83,7 +87,7 @@ namespace RedBookPlayer.GUI int[] numbers = new int[] { - PlayerViewModel.CurrentTrackNumber + 1, + PlayerViewModel.CurrentTrackNumber, PlayerViewModel.CurrentTrackIndex, (int)(sectorTime / (75 * 60)), @@ -138,7 +142,7 @@ namespace RedBookPlayer.GUI ulong sectorTime = PlayerViewModel.CurrentSector; if(PlayerViewModel.SectionStartSector != 0) sectorTime -= PlayerViewModel.SectionStartSector; - else + else if (PlayerViewModel.CurrentTrackNumber > 0) sectorTime += PlayerViewModel.TimeOffset; return sectorTime; @@ -261,11 +265,11 @@ namespace RedBookPlayer.GUI public void NextTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextTrack(); - public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack(App.Settings.AllowSkipHiddenTrack); + public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack(); public void NextIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextIndex(App.Settings.IndexButtonChangeTrack); - public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack, App.Settings.AllowSkipHiddenTrack); + public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack); public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.FastForward(); diff --git a/RedBookPlayer.GUI/Settings.cs b/RedBookPlayer.GUI/Settings.cs index 2329e7c..855b2c8 100644 --- a/RedBookPlayer.GUI/Settings.cs +++ b/RedBookPlayer.GUI/Settings.cs @@ -21,9 +21,14 @@ namespace RedBookPlayer.GUI public bool IndexButtonChangeTrack { get; set; } = false; /// - /// Indicates if the index 0 of track 1 is treated like a hidden track + /// Indicates if hidden tracks should be played /// - public bool AllowSkipHiddenTrack { get; set; } = false; + /// + /// Hidden tracks can be one of the following: + /// - TrackSequence == 0 + /// - Larget pregap of track 1 (> 150 sectors) + /// + public bool PlayHiddenTracks { get; set; } = false; /// /// Indicates if data tracks should be played like old, non-compliant players diff --git a/RedBookPlayer.GUI/SettingsWindow.xaml b/RedBookPlayer.GUI/SettingsWindow.xaml index ba416ba..158ba9e 100644 --- a/RedBookPlayer.GUI/SettingsWindow.xaml +++ b/RedBookPlayer.GUI/SettingsWindow.xaml @@ -17,8 +17,8 @@ Index navigation can change track - - Treat index 0 of track 1 as track 0 (hidden track) + + Play hidden tracks From 8cca9203da308fe95ee9ae897fc330cfb002e903 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 22:39:08 -0700 Subject: [PATCH 17/33] Safer track switching --- RedBookPlayer.Common/Discs/CompactDisc.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 8f123ef..2dc5b99 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -62,9 +62,6 @@ namespace RedBookPlayer.Common.Discs // Set track flags from subchannel data, if possible SetTrackFlags(track); - TotalIndexes = track.Indexes.Keys.Max(); - CurrentTrackIndex = track.Indexes.Keys.Min(); - // If the track is playable, just return if(TrackType == TrackType.Audio || _loadDataTracks) break; @@ -78,6 +75,9 @@ namespace RedBookPlayer.Common.Discs while(cachedValue != _currentTrackNumber); this.RaiseAndSetIfChanged(ref _currentTrackNumber, cachedTrackNumber); + + TotalIndexes = GetTrack(_currentTrackNumber).Indexes.Keys.Max(); + CurrentTrackIndex = GetTrack(_currentTrackNumber).Indexes.Keys.Min(); } } From 52ede7799fe9077ff9e50d41a8e8be22a1c170f8 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 22:46:24 -0700 Subject: [PATCH 18/33] Fix off-by-one display total tracks if hidden track exists --- RedBookPlayer.Common/Discs/CompactDisc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 2dc5b99..b90281d 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -270,7 +270,7 @@ namespace RedBookPlayer.Common.Discs TotalIndexes = 0; // Set the internal disc state - TotalTracks = _image.Tracks.Count; + TotalTracks = (int)_image.Tracks.Max(t => t.TrackSequence); TrackDataDescriptor firstTrack = _toc.TrackDescriptors.First(d => d.ADR == 1 && d.POINT == 1); TimeOffset = (ulong)((firstTrack.PMIN * 60 * 75) + (firstTrack.PSEC * 75) + firstTrack.PFRAME); TotalTime = TimeOffset + _image.Tracks.Last().TrackEndSector; From 0ae58ab21c7e41512cc77e01e8fc0cd3f592aef7 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 22:51:34 -0700 Subject: [PATCH 19/33] Fix improper use of field in PlayerViewModel --- RedBookPlayer.Common/PlayerViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RedBookPlayer.Common/PlayerViewModel.cs b/RedBookPlayer.Common/PlayerViewModel.cs index 04c62a2..e82ed29 100644 --- a/RedBookPlayer.Common/PlayerViewModel.cs +++ b/RedBookPlayer.Common/PlayerViewModel.cs @@ -193,7 +193,7 @@ namespace RedBookPlayer.Common public void Init(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Stop current playback, if necessary - if(Playing != null) Playing = null; + if(Playing != null) Stop(); // Create and attempt to initialize new Player _player = new Player(path, generateMissingToc, loadHiddenTracks, loadDataTracks, autoPlay, defaultVolume); From 7f4992d2d01b1f3edb19f9117e7a5878f3b187ec Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 23:09:22 -0700 Subject: [PATCH 20/33] Persist view model between theme swaps --- RedBookPlayer.GUI/MainWindow.xaml.cs | 10 +++++++--- RedBookPlayer.GUI/PlayerView.xaml.cs | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs index a04d83f..094c53e 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using System.Xml; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; +using RedBookPlayer.Common; namespace RedBookPlayer.GUI { @@ -30,10 +31,13 @@ namespace RedBookPlayer.GUI if(string.IsNullOrWhiteSpace(theme)) return; + // If we already have a view, cache the view model + PlayerViewModel pvm = ((PlayerView)Instance.ContentControl.Content).PlayerViewModel; + // If the theme name is "default", we assume the internal theme is used if(theme.Equals("default", StringComparison.CurrentCultureIgnoreCase)) { - Instance.ContentControl.Content = new PlayerView(); + Instance.ContentControl.Content = new PlayerView(pvm); } else { @@ -50,12 +54,12 @@ namespace RedBookPlayer.GUI { string xaml = File.ReadAllText(xamlPath); xaml = xaml.Replace("Source=\"", $"Source=\"file://{themeDirectory}/"); - Instance.ContentControl.Content = new PlayerView(xaml); + Instance.ContentControl.Content = new PlayerView(xaml, pvm); } catch(XmlException ex) { Console.WriteLine($"Error: invalid theme XAML ({ex.Message}), reverting to default"); - Instance.ContentControl.Content = new PlayerView(); + Instance.ContentControl.Content = new PlayerView(pvm); } } diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index 16f43cc..6696fd0 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -30,15 +30,27 @@ namespace RedBookPlayer.GUI /// /// Initialize the UI based on the default theme /// - public PlayerView() : this(null) { } + public PlayerView() : this(null, null) { } + + /// + /// Initialize the UI based on the default theme with an existing view model + /// + /// XAML data representing the theme, null for default + /// Existing PlayerViewModel to load in instead of creating a new one + public PlayerView(PlayerViewModel playerViewModel) : this(null, playerViewModel) { } /// /// Initialize the UI based on the currently selected theme /// /// XAML data representing the theme, null for default - public PlayerView(string xaml) + /// Existing PlayerViewModel to load in instead of creating a new one + public PlayerView(string xaml, PlayerViewModel playerViewModel) { - DataContext = new PlayerViewModel(); + if(playerViewModel != null) + DataContext = playerViewModel; + else + DataContext = new PlayerViewModel(); + PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; LoadTheme(xaml); From 80a31f1fd41632093f4d9a83953c08e80b25ba32 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 5 Jul 2021 23:16:46 -0700 Subject: [PATCH 21/33] Fix SetTotalIndexes for corner case --- RedBookPlayer.Common/Discs/CompactDisc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index b90281d..4a4591f 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -410,7 +410,7 @@ namespace RedBookPlayer.Common.Discs if(_image == null) return; - TotalIndexes = _image.Tracks[CurrentTrackNumber].Indexes.Keys.Max(); + TotalIndexes = GetTrack(CurrentTrackNumber).Indexes.Keys.Max(); } /// From 04b56d97a18f9d6e5d7163796d3b6a3260822e99 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 6 Jul 2021 09:54:43 -0700 Subject: [PATCH 22/33] Handle data track corner cases --- RedBookPlayer.Common/Discs/CompactDisc.cs | 42 +++++++++++++++++--- RedBookPlayer.Common/Hardware/SoundOutput.cs | 7 ++++ RedBookPlayer.GUI/PlayerView.xaml.cs | 8 +++- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 4a4591f..3338839 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -25,6 +25,10 @@ namespace RedBookPlayer.Common.Discs if(_image == null) return; + // Data tracks only and flag disabled means we can't do anything + if(_image.Tracks.All(t => t.TrackType != TrackType.Audio) && !_loadDataTracks) + return; + // Cache the value and the current track number int cachedValue = value; int cachedTrackNumber; @@ -58,6 +62,8 @@ namespace RedBookPlayer.Common.Discs // Cache the current track for easy access Track track = GetTrack(cachedTrackNumber); + if(track == null) + return; // Set track flags from subchannel data, if possible SetTrackFlags(track); @@ -74,10 +80,26 @@ namespace RedBookPlayer.Common.Discs } while(cachedValue != _currentTrackNumber); - this.RaiseAndSetIfChanged(ref _currentTrackNumber, cachedTrackNumber); + // If we looped around, ensure it reloads + if(cachedValue == _currentTrackNumber) + { + this.RaiseAndSetIfChanged(ref _currentTrackNumber, -1); - TotalIndexes = GetTrack(_currentTrackNumber).Indexes.Keys.Max(); - CurrentTrackIndex = GetTrack(_currentTrackNumber).Indexes.Keys.Min(); + Track track = GetTrack(cachedValue); + if(track == null) + return; + + SetTrackFlags(track); + } + + this.RaiseAndSetIfChanged(ref _currentTrackNumber, cachedValue); + + Track cachedTrack = GetTrack(cachedValue); + if(cachedTrack == null) + return; + + TotalIndexes = cachedTrack.Indexes.Keys.Max(); + CurrentTrackIndex = cachedTrack.Indexes.Keys.Min(); } } @@ -93,6 +115,8 @@ namespace RedBookPlayer.Common.Discs // Cache the current track for easy access Track track = GetTrack(CurrentTrackNumber); + if(track == null) + return; // Ensure that the value is valid, wrapping around if necessary ushort fixedValue = value; @@ -121,10 +145,12 @@ namespace RedBookPlayer.Common.Discs // Cache the current track for easy access Track track = GetTrack(CurrentTrackNumber); + if(track == null) + return; this.RaiseAndSetIfChanged(ref _currentSector, value); - if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= GetTrack(CurrentTrackNumber + 1).TrackStartSector) + if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= (GetTrack(CurrentTrackNumber + 1)?.TrackStartSector ?? 0)) || (CurrentTrackNumber > 0 && CurrentSector < track.TrackStartSector)) { foreach(Track trackData in _image.Tracks.ToArray().Reverse()) @@ -151,7 +177,7 @@ namespace RedBookPlayer.Common.Discs } /// - public override int BytesPerSector => GetTrack(CurrentTrackNumber).TrackRawBytesPerSector; + public override int BytesPerSector => GetTrack(CurrentTrackNumber)?.TrackRawBytesPerSector ?? 0; /// /// Represents the 4CH flag @@ -309,6 +335,8 @@ namespace RedBookPlayer.Common.Discs // Cache the current track for easy access Track track = GetTrack(CurrentTrackNumber); + if(track == null) + return false; // If the index is greater than the highest index, change tracks if needed if(CurrentTrackIndex + 1 > track.Indexes.Keys.Max()) @@ -349,6 +377,8 @@ namespace RedBookPlayer.Common.Discs // Cache the current track for easy access Track track = GetTrack(CurrentTrackNumber); + if(track == null) + return false; // If the index is less than the lowest index, change tracks if needed if(CurrentTrackIndex - 1 < track.Indexes.Keys.Min()) @@ -410,7 +440,7 @@ namespace RedBookPlayer.Common.Discs if(_image == null) return; - TotalIndexes = GetTrack(CurrentTrackNumber).Indexes.Keys.Max(); + TotalIndexes = GetTrack(CurrentTrackNumber)?.Indexes.Keys.Max() ?? 0; } /// diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index 4207801..5d78791 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -153,6 +153,13 @@ namespace RedBookPlayer.Common.Hardware // Set the current volume _soundOut.Volume = (float)Volume / 100; + // If we have an unreadable track, just return + if (_opticalDisc.BytesPerSector <= 0) + { + Array.Clear(buffer, offset, count); + return count; + } + // Determine how many sectors we can read ulong sectorsToRead; ulong zeroSectorsAmount; diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index 6696fd0..b6019f7 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -94,12 +94,18 @@ namespace RedBookPlayer.GUI if(PlayerViewModel?.Initialized != true) return string.Empty.PadLeft(20, '-'); + int usableTrackNumber = PlayerViewModel.CurrentTrackNumber; + if(usableTrackNumber < 0) + usableTrackNumber = 0; + else if(usableTrackNumber > 99) + usableTrackNumber = 99; + // Otherwise, take the current time into account ulong sectorTime = GetCurrentSectorTime(); int[] numbers = new int[] { - PlayerViewModel.CurrentTrackNumber, + usableTrackNumber, PlayerViewModel.CurrentTrackIndex, (int)(sectorTime / (75 * 60)), From 902e2d73b394aef6175f5a8c8f81c18867798d96 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 7 Jul 2021 10:21:23 -0700 Subject: [PATCH 23/33] Make de-emphasis a separate method --- RedBookPlayer.Common/Hardware/SoundOutput.cs | 35 ++++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index 5d78791..8531775 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -236,20 +236,7 @@ namespace RedBookPlayer.Common.Hardware // Apply de-emphasis filtering, only if enabled if(ApplyDeEmphasis) - { - float[][] floatAudioData = new float[2][]; - floatAudioData[0] = new float[audioDataSegment.Length / 4]; - floatAudioData[1] = new float[audioDataSegment.Length / 4]; - ByteConverter.ToFloats16Bit(audioDataSegment, floatAudioData); - - for(int i = 0; i < floatAudioData[0].Length; i++) - { - floatAudioData[0][i] = _deEmphasisFilterLeft.Process(floatAudioData[0][i]); - floatAudioData[1][i] = _deEmphasisFilterRight.Process(floatAudioData[1][i]); - } - - ByteConverter.FromFloats16Bit(floatAudioData, audioDataSegment); - } + ProcessDeEmphasis(audioDataSegment); // Write out the audio data to the buffer Array.Copy(audioDataSegment, 0, buffer, offset, count); @@ -316,6 +303,26 @@ namespace RedBookPlayer.Common.Hardware /// New volume value public void SetVolume(int volume) => Volume = volume; + /// + /// Process de-emphasis of audio data + /// + /// Audio data to process + private void ProcessDeEmphasis(byte[] audioData) + { + float[][] floatAudioData = new float[2][]; + floatAudioData[0] = new float[audioData.Length / 4]; + floatAudioData[1] = new float[audioData.Length / 4]; + ByteConverter.ToFloats16Bit(audioData, floatAudioData); + + for(int i = 0; i < floatAudioData[0].Length; i++) + { + floatAudioData[0][i] = _deEmphasisFilterLeft.Process(floatAudioData[0][i]); + floatAudioData[1][i] = _deEmphasisFilterRight.Process(floatAudioData[1][i]); + } + + ByteConverter.FromFloats16Bit(floatAudioData, audioData); + } + /// /// Sets or resets the de-emphasis filters /// From 6f9c39f5c15203a3fd5ad7f8fe9d3f079f710fc1 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 7 Jul 2021 10:40:44 -0700 Subject: [PATCH 24/33] Extract out another helper method --- RedBookPlayer.Common/Hardware/SoundOutput.cs | 62 ++++++++++++-------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index 8531775..b460cee 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -154,38 +154,14 @@ namespace RedBookPlayer.Common.Hardware _soundOut.Volume = (float)Volume / 100; // If we have an unreadable track, just return - if (_opticalDisc.BytesPerSector <= 0) + if(_opticalDisc.BytesPerSector <= 0) { Array.Clear(buffer, offset, count); return count; } // Determine how many sectors we can read - ulong sectorsToRead; - ulong zeroSectorsAmount; - do - { - // Attempt to read 2 more sectors than requested - sectorsToRead = ((ulong)count / (ulong)_opticalDisc.BytesPerSector) + 2; - zeroSectorsAmount = 0; - - // Avoid overreads by padding with 0-byte data at the end - if(_opticalDisc.CurrentSector + sectorsToRead > _opticalDisc.TotalSectors) - { - ulong oldSectorsToRead = sectorsToRead; - sectorsToRead = _opticalDisc.TotalSectors - _opticalDisc.CurrentSector; - - int tempZeroSectorCount = (int)(oldSectorsToRead - sectorsToRead); - zeroSectorsAmount = (ulong)(tempZeroSectorCount < 0 ? 0 : tempZeroSectorCount); - } - - // TODO: Figure out when this value could be negative - if(sectorsToRead <= 0) - { - _opticalDisc.LoadFirstTrack(); - _currentSectorReadPosition = 0; - } - } while(sectorsToRead <= 0); + DetermineReadAmount(count, out ulong sectorsToRead, out ulong zeroSectorsAmount); // Create padding data for overreads byte[] zeroSectors = new byte[(int)zeroSectorsAmount * _opticalDisc.BytesPerSector]; @@ -303,6 +279,40 @@ namespace RedBookPlayer.Common.Hardware /// New volume value public void SetVolume(int volume) => Volume = volume; + /// + /// Determine the number of real and zero sectors to read + /// + /// Number of requested bytes to read + /// Number of sectors to read + /// Number of zeroed sectors to concatenate + private void DetermineReadAmount(int count, out ulong sectorsToRead, out ulong zeroSectorsAmount) + { + do + { + // Attempt to read 2 more sectors than requested + sectorsToRead = ((ulong)count / (ulong)_opticalDisc.BytesPerSector) + 2; + zeroSectorsAmount = 0; + + // Avoid overreads by padding with 0-byte data at the end + if(_opticalDisc.CurrentSector + sectorsToRead > _opticalDisc.TotalSectors) + { + ulong oldSectorsToRead = sectorsToRead; + sectorsToRead = _opticalDisc.TotalSectors - _opticalDisc.CurrentSector; + + int tempZeroSectorCount = (int)(oldSectorsToRead - sectorsToRead); + zeroSectorsAmount = (ulong)(tempZeroSectorCount < 0 ? 0 : tempZeroSectorCount); + } + + // If we're reading past the last sector of the disc, wrap around + // TODO: Have past-end reads looping back controlled by a flag instead (Repeat? Repeat All?) + if(sectorsToRead <= 0) + { + _opticalDisc.LoadFirstTrack(); + _currentSectorReadPosition = 0; + } + } while(sectorsToRead <= 0); + } + /// /// Process de-emphasis of audio data /// From 788d10ecd8f15b4186a32b7bb49a81ebb4d7eebf Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 10:41:11 -0700 Subject: [PATCH 25/33] Move more logic down the chain --- RedBookPlayer.Common/Hardware/Player.cs | 82 ++++++- RedBookPlayer.GUI/MainWindow.xaml.cs | 28 +-- RedBookPlayer.GUI/PlayerView.xaml | 22 +- RedBookPlayer.GUI/PlayerView.xaml.cs | 40 +-- .../ViewModels}/PlayerViewModel.cs | 229 ++++++++++++++---- 5 files changed, 282 insertions(+), 119 deletions(-) rename {RedBookPlayer.Common => RedBookPlayer.GUI/ViewModels}/PlayerViewModel.cs (60%) diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 0fdc4d4..8c4c256 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -181,6 +181,11 @@ namespace RedBookPlayer.Common.Hardware /// private readonly OpticalDisc _opticalDisc; + /// + /// Last volume for mute toggling + /// + private int? _lastVolume = null; + #endregion /// @@ -258,6 +263,17 @@ namespace RedBookPlayer.Common.Hardware Playing = false; } + /// + /// Toggle current playback + /// + public void TogglePlayback() + { + if(Playing == true) + Pause(); + else + Play(); + } + /// /// Stop current playback /// @@ -374,13 +390,69 @@ namespace RedBookPlayer.Common.Hardware #endregion - #region Helpers + #region Volume + + /// + /// Increment the volume value + /// + public void VolumeUp() => SetVolume(Volume + 1); + + /// + /// Decrement the volume value + /// + public void VolumeDown() => SetVolume(Volume + 1); + + /// + /// Set the value for the volume + /// + /// New volume value + public void SetVolume(int volume) => _soundOutput?.SetVolume(volume); + + /// + /// Temporarily mute playback + /// + public void ToggleMute() + { + if(_lastVolume == null) + { + _lastVolume = Volume; + SetVolume(0); + } + else + { + SetVolume(_lastVolume.Value); + _lastVolume = null; + } + } + + #endregion + + #region Emphasis + + /// + /// Enable de-emphasis + /// + public void EnableDeEmphasis() => SetDeEmphasis(true); + + /// + /// Disable de-emphasis + /// + public void DisableDeEmphasis() => SetDeEmphasis(false); + + /// + /// Toggle de-emphasis + /// + public void ToggleDeEmphasis() => SetDeEmphasis(!ApplyDeEmphasis); /// /// Set de-emphasis status /// /// - public void SetDeEmphasis(bool apply) => _soundOutput?.SetDeEmphasis(apply); + private void SetDeEmphasis(bool apply) => _soundOutput?.SetDeEmphasis(apply); + + #endregion + + #region Helpers /// /// Set the value for loading data tracks [CompactDisc only] @@ -394,12 +466,6 @@ namespace RedBookPlayer.Common.Hardware /// True to enable loading hidden tracks, false otherwise public void SetLoadHiddenTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadHiddenTracks(load); - /// - /// Set the value for the volume - /// - /// New volume value - public void SetVolume(int volume) => _soundOutput?.SetVolume(volume); - /// /// Update the player from the current OpticalDisc /// diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs index 094c53e..2172c1a 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/MainWindow.xaml.cs @@ -5,7 +5,7 @@ using System.Xml; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; -using RedBookPlayer.Common; +using RedBookPlayer.GUI.ViewModels; namespace RedBookPlayer.GUI { @@ -94,7 +94,7 @@ namespace RedBookPlayer.GUI Closing += (e, f) => { - ((PlayerView)ContentControl.Content).StopButton_Click(this, null); + ((PlayerView)ContentControl.Content).PlayerViewModel.ExecuteStop(); }; AddHandler(DragDrop.DropEvent, MainWindow_Drop); @@ -138,49 +138,49 @@ namespace RedBookPlayer.GUI // Toggle playback else if(e.Key == App.Settings.TogglePlaybackKey || e.Key == Key.MediaPlayPause) { - playerView?.PlayPauseButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteTogglePlayPause(); } // Stop playback else if(e.Key == App.Settings.StopPlaybackKey || e.Key == Key.MediaStop) { - playerView?.StopButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteStop(); } // Next Track else if(e.Key == App.Settings.NextTrackKey || e.Key == Key.MediaNextTrack) { - playerView?.NextTrackButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteNextTrack(); } // Previous Track else if(e.Key == App.Settings.PreviousTrackKey || e.Key == Key.MediaPreviousTrack) { - playerView?.PreviousTrackButton_Click(this, null); + playerView?.PlayerViewModel?.ExecutePreviousTrack(); } // Next Index else if(e.Key == App.Settings.NextIndexKey) { - playerView?.NextIndexButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteNextIndex(); } // Previous Index else if(e.Key == App.Settings.PreviousIndexKey) { - playerView?.PreviousIndexButton_Click(this, null); + playerView?.PlayerViewModel?.ExecutePreviousIndex(); } // Fast Foward else if(e.Key == App.Settings.FastForwardPlaybackKey) { - playerView?.FastForwardButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteFastForward(); } // Rewind else if(e.Key == App.Settings.RewindPlaybackKey) { - playerView?.RewindButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteRewind(); } // Volume Up @@ -193,7 +193,7 @@ namespace RedBookPlayer.GUI increment *= 5; if(playerView?.PlayerViewModel?.Volume != null) - playerView.PlayerViewModel.SetVolume(playerView.PlayerViewModel.Volume + increment); + playerView.PlayerViewModel.ExecuteSetVolume(playerView.PlayerViewModel.Volume + increment); } // Volume Down @@ -206,19 +206,19 @@ namespace RedBookPlayer.GUI decrement *= 5; if (playerView?.PlayerViewModel?.Volume != null) - playerView.PlayerViewModel.SetVolume(playerView.PlayerViewModel.Volume - decrement); + playerView.PlayerViewModel.ExecuteSetVolume(playerView.PlayerViewModel.Volume - decrement); } // Mute Toggle else if(e.Key == App.Settings.ToggleMuteKey || e.Key == Key.VolumeMute) { - playerView?.MuteToggleButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteToggleMute(); } // Emphasis Toggle else if(e.Key == App.Settings.ToggleDeEmphasisKey) { - playerView?.EnableDisableDeEmphasisButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteToggleDeEmphasis(); } } diff --git a/RedBookPlayer.GUI/PlayerView.xaml b/RedBookPlayer.GUI/PlayerView.xaml index ab9fcec..e531106 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml +++ b/RedBookPlayer.GUI/PlayerView.xaml @@ -5,17 +5,17 @@ - - - + + + - - - - - Rewind - Fast Forward + + + + + Rewind + Fast Forward @@ -76,11 +76,11 @@ - - diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index b6019f7..d2af50e 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -11,7 +11,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Threading; -using RedBookPlayer.Common; +using RedBookPlayer.GUI.ViewModels; namespace RedBookPlayer.GUI { @@ -267,44 +267,6 @@ namespace RedBookPlayer.GUI await LoadImage(path); } - public void PlayButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Play(); - - public void PauseButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Pause(); - - public void PlayPauseButton_Click(object sender, RoutedEventArgs e) - { - if(PlayerViewModel.Playing == true) - PlayerViewModel.Pause(); - else - PlayerViewModel.Play(); - } - - public void StopButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Stop(); - - public void NextTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextTrack(); - - public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack(); - - public void NextIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextIndex(App.Settings.IndexButtonChangeTrack); - - public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack); - - public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.FastForward(); - - public void RewindButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Rewind(); - - public void VolumeUpButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetVolume(PlayerViewModel.Volume + 1); - - public void VolumeDownButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetVolume(PlayerViewModel.Volume - 1); - - public void MuteToggleButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ToggleMute(); - - public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(true); - - public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(false); - - public void EnableDisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.SetDeEmphasis(!PlayerViewModel.ApplyDeEmphasis); - #endregion } } \ No newline at end of file diff --git a/RedBookPlayer.Common/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs similarity index 60% rename from RedBookPlayer.Common/PlayerViewModel.cs rename to RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index e82ed29..2c9d466 100644 --- a/RedBookPlayer.Common/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -1,8 +1,9 @@ using System.ComponentModel; +using System.Reactive; using ReactiveUI; using RedBookPlayer.Common.Hardware; -namespace RedBookPlayer.Common +namespace RedBookPlayer.GUI.ViewModels { public class PlayerViewModel : ReactiveObject { @@ -11,11 +12,6 @@ namespace RedBookPlayer.Common /// private Player _player; - /// - /// Last volume for mute toggling - /// - private int? _lastVolume = null; - #region Player Passthrough #region OpticalDisc Passthrough @@ -181,6 +177,127 @@ namespace RedBookPlayer.Common #endregion + #region Commands + + #region Playback + + /// + /// Command for beginning playback + /// + public ReactiveCommand PlayCommand { get; } + + /// + /// Command for pausing playback + /// + public ReactiveCommand PauseCommand { get; } + + /// + /// Command for pausing playback + /// + public ReactiveCommand TogglePlayPauseCommand { get; } + + /// + /// Command for stopping playback + /// + public ReactiveCommand StopCommand { get; } + + /// + /// Command for moving to the next track + /// + public ReactiveCommand NextTrackCommand { get; } + + /// + /// Command for moving to the previous track + /// + public ReactiveCommand PreviousTrackCommand { get; } + + /// + /// Command for moving to the next index + /// + public ReactiveCommand NextIndexCommand { get; } + + /// + /// Command for moving to the previous index + /// + public ReactiveCommand PreviousIndexCommand { get; } + + /// + /// Command for fast forwarding + /// + public ReactiveCommand FastForwardCommand { get; } + + /// + /// Command for rewinding + /// + public ReactiveCommand RewindCommand { get; } + + #endregion + + #region Volume + + /// + /// Command for incrementing volume + /// + public ReactiveCommand VolumeUpCommand { get; } + + /// + /// Command for decrementing volume + /// + public ReactiveCommand VolumeDownCommand { get; } + + /// + /// Command for toggling mute + /// + public ReactiveCommand ToggleMuteCommand { get; } + + #endregion + + #region Emphasis + + /// + /// Command for enabling de-emphasis + /// + public ReactiveCommand EnableDeEmphasisCommand { get; } + + /// + /// Command for disabling de-emphasis + /// + public ReactiveCommand DisableDeEmphasisCommand { get; } + + /// + /// Command for toggling de-emphasis + /// + public ReactiveCommand ToggleDeEmphasisCommand { get; } + + #endregion + + #endregion + + /// + /// Constructor + /// + public PlayerViewModel() + { + PlayCommand = ReactiveCommand.Create(ExecutePlay); + PauseCommand = ReactiveCommand.Create(ExecutePause); + TogglePlayPauseCommand = ReactiveCommand.Create(ExecuteTogglePlayPause); + StopCommand = ReactiveCommand.Create(ExecuteStop); + NextTrackCommand = ReactiveCommand.Create(ExecuteNextTrack); + PreviousTrackCommand = ReactiveCommand.Create(ExecutePreviousTrack); + NextIndexCommand = ReactiveCommand.Create(ExecuteNextIndex); + PreviousIndexCommand = ReactiveCommand.Create(ExecutePreviousIndex); + FastForwardCommand = ReactiveCommand.Create(ExecuteFastForward); + RewindCommand = ReactiveCommand.Create(ExecuteRewind); + + VolumeUpCommand = ReactiveCommand.Create(ExecuteVolumeUp); + VolumeDownCommand = ReactiveCommand.Create(ExecuteVolumeDown); + ToggleMuteCommand = ReactiveCommand.Create(ExecuteToggleMute); + + EnableDeEmphasisCommand = ReactiveCommand.Create(ExecuteEnableDeEmphasis); + DisableDeEmphasisCommand = ReactiveCommand.Create(ExecuteDisableDeEmphasis); + ToggleDeEmphasisCommand = ReactiveCommand.Create(ExecuteToggleDeEmphasis); + } + /// /// Initialize the view model with a given image path /// @@ -193,7 +310,7 @@ namespace RedBookPlayer.Common public void Init(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay, int defaultVolume) { // Stop current playback, if necessary - if(Playing != null) Stop(); + if(Playing != null) ExecuteStop(); // Create and attempt to initialize new Player _player = new Player(path, generateMissingToc, loadHiddenTracks, loadDataTracks, autoPlay, defaultVolume); @@ -209,60 +326,101 @@ namespace RedBookPlayer.Common /// /// Begin playback /// - public void Play() => _player?.Play(); + public void ExecutePlay() => _player?.Play(); /// /// Pause current playback /// - public void Pause() => _player?.Pause(); + public void ExecutePause() => _player?.Pause(); + + /// + /// Toggle playback + /// + public void ExecuteTogglePlayPause() => _player?.TogglePlayback(); /// /// Stop current playback /// - public void Stop() => _player?.Stop(); + public void ExecuteStop() => _player?.Stop(); /// /// Move to the next playable track /// - public void NextTrack() => _player?.NextTrack(); + public void ExecuteNextTrack() => _player?.NextTrack(); /// /// Move to the previous playable track /// - public void PreviousTrack() => _player?.PreviousTrack(); + public void ExecutePreviousTrack() => _player?.PreviousTrack(); /// /// Move to the next index /// - /// True if index changes can trigger a track change, false otherwise - public void NextIndex(bool changeTrack) => _player?.NextIndex(changeTrack); + public void ExecuteNextIndex() => _player?.NextIndex(App.Settings.IndexButtonChangeTrack); /// /// Move to the previous index /// - /// True if index changes can trigger a track change, false otherwise - public void PreviousIndex(bool changeTrack) => _player?.PreviousIndex(changeTrack); + public void ExecutePreviousIndex() => _player?.PreviousIndex(App.Settings.IndexButtonChangeTrack); /// /// Fast-forward playback by 75 sectors, if possible /// - public void FastForward() => _player?.FastForward(); + public void ExecuteFastForward() => _player?.FastForward(); /// /// Rewind playback by 75 sectors, if possible /// - public void Rewind() => _player?.Rewind(); + public void ExecuteRewind() => _player?.Rewind(); + + #endregion + + #region Volume + + /// + /// Increment the volume value + /// + public void ExecuteVolumeUp() => _player?.VolumeUp(); + + /// + /// Decrement the volume value + /// + public void ExecuteVolumeDown() => _player?.VolumeDown(); + + /// + /// Set the value for the volume + /// + /// New volume value + public void ExecuteSetVolume(int volume) => _player?.SetVolume(volume); + + /// + /// Temporarily mute playback + /// + public void ExecuteToggleMute() => _player?.ToggleMute(); + + #endregion + + #region Emphasis + + /// + /// Enable de-emphasis + /// + public void ExecuteEnableDeEmphasis() => _player?.EnableDeEmphasis(); + + /// + /// Disable de-emphasis + /// + public void ExecuteDisableDeEmphasis() => _player?.DisableDeEmphasis(); + + /// + /// Toggle de-emphasis + /// + public void ExecuteToggleDeEmphasis() => _player?.ToggleDeEmphasis(); #endregion #region Helpers - /// - /// Set de-emphasis status - /// - /// - public void SetDeEmphasis(bool apply) => _player?.SetDeEmphasis(apply); - /// /// Set the value for loading data tracks [CompactDisc only] /// @@ -275,29 +433,6 @@ namespace RedBookPlayer.Common /// True to enable loading hidden tracks, false otherwise public void SetLoadHiddenTracks(bool load) => _player?.SetLoadHiddenTracks(load); - /// - /// Set the value for the volume - /// - /// New volume value - public void SetVolume(int volume) => _player?.SetVolume(volume); - - /// - /// Temporarily mute playback - /// - public void ToggleMute() - { - if(_lastVolume == null) - { - _lastVolume = Volume; - _player?.SetVolume(0); - } - else - { - _player?.SetVolume(_lastVolume.Value); - _lastVolume = null; - } - } - /// /// Update the view-model from the Player /// From 29205c953b9f11311831ef3dcc53c819d110b1b0 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 10:52:50 -0700 Subject: [PATCH 26/33] Factories and base class renames --- RedBookPlayer.Common/Discs/CompactDisc.cs | 2 +- .../{OpticalDisc.cs => OpticalDiscBase.cs} | 2 +- .../OpticalDiscFactory.cs | 9 +-- RedBookPlayer.Common/Hardware/Player.cs | 3 +- RedBookPlayer.Common/Hardware/SoundOutput.cs | 4 +- RedBookPlayer.GUI/PlayerView.xaml.cs | 56 +------------------ .../ViewModels/PlayerViewModel.cs | 55 ++++++++++++++++++ 7 files changed, 67 insertions(+), 64 deletions(-) rename RedBookPlayer.Common/Discs/{OpticalDisc.cs => OpticalDiscBase.cs} (98%) rename RedBookPlayer.Common/{Discs => Factories}/OpticalDiscFactory.cs (91%) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 3338839..0fe5311 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -11,7 +11,7 @@ using static Aaru.Decoders.CD.FullTOC; namespace RedBookPlayer.Common.Discs { - public class CompactDisc : OpticalDisc, IReactiveObject + public class CompactDisc : OpticalDiscBase, IReactiveObject { #region Public Fields diff --git a/RedBookPlayer.Common/Discs/OpticalDisc.cs b/RedBookPlayer.Common/Discs/OpticalDiscBase.cs similarity index 98% rename from RedBookPlayer.Common/Discs/OpticalDisc.cs rename to RedBookPlayer.Common/Discs/OpticalDiscBase.cs index b522574..0a0ab60 100644 --- a/RedBookPlayer.Common/Discs/OpticalDisc.cs +++ b/RedBookPlayer.Common/Discs/OpticalDiscBase.cs @@ -4,7 +4,7 @@ using ReactiveUI; namespace RedBookPlayer.Common.Discs { - public abstract class OpticalDisc : ReactiveObject + public abstract class OpticalDiscBase : ReactiveObject { #region Public Fields diff --git a/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs b/RedBookPlayer.Common/Factories/OpticalDiscFactory.cs similarity index 91% rename from RedBookPlayer.Common/Discs/OpticalDiscFactory.cs rename to RedBookPlayer.Common/Factories/OpticalDiscFactory.cs index 52ad9d8..b4f290c 100644 --- a/RedBookPlayer.Common/Discs/OpticalDiscFactory.cs +++ b/RedBookPlayer.Common/Factories/OpticalDiscFactory.cs @@ -3,8 +3,9 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Metadata; using Aaru.DiscImages; using Aaru.Filters; +using RedBookPlayer.Common.Discs; -namespace RedBookPlayer.Common.Discs +namespace RedBookPlayer.Common.Factories { public static class OpticalDiscFactory { @@ -17,7 +18,7 @@ namespace RedBookPlayer.Common.Discs /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromPath(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) + public static OpticalDiscBase GenerateFromPath(string path, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) { try { @@ -51,14 +52,14 @@ namespace RedBookPlayer.Common.Discs /// Load data tracks for playback [CompactDisc only] /// True if the image should be playable immediately, false otherwise /// Instantiated OpticalDisc, if possible - public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) + public static OpticalDiscBase GenerateFromImage(IOpticalMediaImage image, bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks, bool autoPlay) { // If the image is not usable, we don't do anything if(!IsUsableImage(image)) return null; // Create the output object - OpticalDisc opticalDisc; + OpticalDiscBase opticalDisc; // Create the proper disc type switch(GetMediaType(image)) diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 8c4c256..4cf76d2 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using Aaru.CommonTypes.Enums; using ReactiveUI; using RedBookPlayer.Common.Discs; +using RedBookPlayer.Common.Factories; namespace RedBookPlayer.Common.Hardware { @@ -179,7 +180,7 @@ namespace RedBookPlayer.Common.Hardware /// /// OpticalDisc object /// - private readonly OpticalDisc _opticalDisc; + private readonly OpticalDiscBase _opticalDisc; /// /// Last volume for mute toggling diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Common/Hardware/SoundOutput.cs index b460cee..89f2b8c 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Common/Hardware/SoundOutput.cs @@ -68,7 +68,7 @@ namespace RedBookPlayer.Common.Hardware /// /// TODO: Can we remove the need for a local reference to OpticalDisc? /// - private OpticalDisc _opticalDisc; + private OpticalDiscBase _opticalDisc; /// /// Data provider for sound output @@ -108,7 +108,7 @@ namespace RedBookPlayer.Common.Hardware /// OpticalDisc to load from /// True if playback should begin immediately, false otherwise /// Default volume between 0 and 100 to use when starting playback - public void Init(OpticalDisc opticalDisc, bool autoPlay = false, int defaultVolume = 100) + public void Init(OpticalDiscBase opticalDisc, bool autoPlay = false, int defaultVolume = 100) { // If we have an unusable disc, just return if(opticalDisc == null || !opticalDisc.Initialized) diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index d2af50e..a1b763b 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -84,45 +84,6 @@ namespace RedBookPlayer.GUI PlayerViewModel.SetLoadHiddenTracks(App.Settings.PlayHiddenTracks); } - /// - /// Generate the digit string to be interpreted by the frontend - /// - /// String representing the digits for the frontend - private string GenerateDigitString() - { - // If the disc isn't initialized, return all '-' characters - if(PlayerViewModel?.Initialized != true) - return string.Empty.PadLeft(20, '-'); - - int usableTrackNumber = PlayerViewModel.CurrentTrackNumber; - if(usableTrackNumber < 0) - usableTrackNumber = 0; - else if(usableTrackNumber > 99) - usableTrackNumber = 99; - - // Otherwise, take the current time into account - ulong sectorTime = GetCurrentSectorTime(); - - int[] numbers = new int[] - { - usableTrackNumber, - PlayerViewModel.CurrentTrackIndex, - - (int)(sectorTime / (75 * 60)), - (int)(sectorTime / 75 % 60), - (int)(sectorTime % 75), - - PlayerViewModel.TotalTracks, - PlayerViewModel.TotalIndexes, - - (int)(PlayerViewModel.TotalTime / (75 * 60)), - (int)(PlayerViewModel.TotalTime / 75 % 60), - (int)(PlayerViewModel.TotalTime % 75), - }; - - return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); - } - /// /// Load the png image for a given character based on the theme /// @@ -151,21 +112,6 @@ namespace RedBookPlayer.GUI } } - /// - /// Get current sector time, accounting for offsets - /// - /// ulong representing the current sector time - private ulong GetCurrentSectorTime() - { - ulong sectorTime = PlayerViewModel.CurrentSector; - if(PlayerViewModel.SectionStartSector != 0) - sectorTime -= PlayerViewModel.SectionStartSector; - else if (PlayerViewModel.CurrentTrackNumber > 0) - sectorTime += PlayerViewModel.TimeOffset; - - return sectorTime; - } - /// /// Generate a path selection dialog box /// @@ -244,7 +190,7 @@ namespace RedBookPlayer.GUI { Dispatcher.UIThread.InvokeAsync(() => { - string digitString = GenerateDigitString(); + string digitString = PlayerViewModel?.GenerateDigitString() ?? string.Empty.PadLeft(20, '-'); for(int i = 0; i < _digits.Length; i++) { Bitmap digitImage = GetBitmap(digitString[i]); diff --git a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index 2c9d466..a5cad4a 100644 --- a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Linq; using System.Reactive; using ReactiveUI; using RedBookPlayer.Common.Hardware; @@ -421,6 +422,45 @@ namespace RedBookPlayer.GUI.ViewModels #region Helpers + /// + /// Generate the digit string to be interpreted by the frontend + /// + /// String representing the digits for the frontend + public string GenerateDigitString() + { + // If the disc isn't initialized, return all '-' characters + if(Initialized != true) + return string.Empty.PadLeft(20, '-'); + + int usableTrackNumber = CurrentTrackNumber; + if(usableTrackNumber < 0) + usableTrackNumber = 0; + else if(usableTrackNumber > 99) + usableTrackNumber = 99; + + // Otherwise, take the current time into account + ulong sectorTime = GetCurrentSectorTime(); + + int[] numbers = new int[] + { + usableTrackNumber, + CurrentTrackIndex, + + (int)(sectorTime / (75 * 60)), + (int)(sectorTime / 75 % 60), + (int)(sectorTime % 75), + + TotalTracks, + TotalIndexes, + + (int)(TotalTime / (75 * 60)), + (int)(TotalTime / 75 % 60), + (int)(TotalTime % 75), + }; + + return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); + } + /// /// Set the value for loading data tracks [CompactDisc only] /// @@ -433,6 +473,21 @@ namespace RedBookPlayer.GUI.ViewModels /// True to enable loading hidden tracks, false otherwise public void SetLoadHiddenTracks(bool load) => _player?.SetLoadHiddenTracks(load); + /// + /// Get current sector time, accounting for offsets + /// + /// ulong representing the current sector time + private ulong GetCurrentSectorTime() + { + ulong sectorTime = CurrentSector; + if(SectionStartSector != 0) + sectorTime -= SectionStartSector; + else if(CurrentTrackNumber > 0) + sectorTime += TimeOffset; + + return sectorTime; + } + /// /// Update the view-model from the Player /// From bc810034d03fdab4fcecf215fd35beabd2cf4ad1 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 10:57:52 -0700 Subject: [PATCH 27/33] Use properties in CompactDisc --- RedBookPlayer.Common/Discs/CompactDisc.cs | 44 +++++++++-------------- RedBookPlayer.Common/Hardware/Player.cs | 12 +++++-- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Common/Discs/CompactDisc.cs index 0fe5311..45c634e 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Common/Discs/CompactDisc.cs @@ -26,7 +26,7 @@ namespace RedBookPlayer.Common.Discs return; // Data tracks only and flag disabled means we can't do anything - if(_image.Tracks.All(t => t.TrackType != TrackType.Audio) && !_loadDataTracks) + if(_image.Tracks.All(t => t.TrackType != TrackType.Audio) && !LoadDataTracks) return; // Cache the value and the current track number @@ -42,12 +42,12 @@ namespace RedBookPlayer.Common.Discs if(cachedValue > _image.Tracks.Max(t => t.TrackSequence)) { cachedValue = (int)_image.Tracks.Min(t => t.TrackSequence); - if(cachedValue == 0 && !_loadHiddenTracks) + if(cachedValue == 0 && !LoadHiddenTracks) cachedValue++; } // If we're under the first track and we're not loading hidden tracks, wrap around - else if(cachedValue < 1 && !_loadHiddenTracks) + else if(cachedValue < 1 && !LoadHiddenTracks) { cachedValue = (int)_image.Tracks.Max(t => t.TrackSequence); } @@ -69,7 +69,7 @@ namespace RedBookPlayer.Common.Discs SetTrackFlags(track); // If the track is playable, just return - if(TrackType == TrackType.Audio || _loadDataTracks) + if(TrackType == TrackType.Audio || LoadDataTracks) break; // If we're not playing the track, skip @@ -215,6 +215,16 @@ namespace RedBookPlayer.Common.Discs private set => this.RaiseAndSetIfChanged(ref _trackHasEmphasis, value); } + /// + /// Indicate if data tracks should be loaded + /// + public bool LoadDataTracks { get; set; } = false; + + /// + /// Indicate if hidden tracks should be loaded + /// + public bool LoadHiddenTracks { get; set; } = false; + private bool _quadChannel; private bool _isDataTrack; private bool _copyAllowed; @@ -244,16 +254,6 @@ namespace RedBookPlayer.Common.Discs /// private readonly bool _generateMissingToc = false; - /// - /// Indicate if data tracks should be loaded - /// - private bool _loadDataTracks = false; - - /// - /// Indicate if hidden tracks should be loaded - /// - private bool _loadHiddenTracks = false; - /// /// Current disc table of contents /// @@ -270,8 +270,8 @@ namespace RedBookPlayer.Common.Discs public CompactDisc(bool generateMissingToc, bool loadHiddenTracks, bool loadDataTracks) { _generateMissingToc = generateMissingToc; - _loadHiddenTracks = loadHiddenTracks; - _loadDataTracks = loadDataTracks; + LoadHiddenTracks = loadHiddenTracks; + LoadDataTracks = loadDataTracks; } /// @@ -422,18 +422,6 @@ namespace RedBookPlayer.Common.Discs LoadTrack(CurrentTrackNumber); } - /// - /// Set the value for loading data tracks - /// - /// True to enable loading data tracks, false otherwise - public void SetLoadDataTracks(bool load) => _loadDataTracks = load; - - /// - /// Set the value for loading hidden tracks - /// - /// True to enable loading hidden tracks, false otherwise - public void SetLoadHiddenTracks(bool load) => _loadHiddenTracks = load; - /// public override void SetTotalIndexes() { diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Common/Hardware/Player.cs index 4cf76d2..8bf225c 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Common/Hardware/Player.cs @@ -459,13 +459,21 @@ namespace RedBookPlayer.Common.Hardware /// Set the value for loading data tracks [CompactDisc only] /// /// True to enable loading data tracks, false otherwise - public void SetLoadDataTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadDataTracks(load); + public void SetLoadDataTracks(bool load) + { + if(_opticalDisc is CompactDisc compactDisc) + compactDisc.LoadDataTracks = load; + } /// /// Set the value for loading hidden tracks [CompactDisc only] /// /// True to enable loading hidden tracks, false otherwise - public void SetLoadHiddenTracks(bool load) => (_opticalDisc as CompactDisc)?.SetLoadHiddenTracks(load); + public void SetLoadHiddenTracks(bool load) + { + if(_opticalDisc is CompactDisc compactDisc) + compactDisc.LoadHiddenTracks = load; + } /// /// Update the player from the current OpticalDisc From 1e9b41f9973d1f3a76907641553282171a65c636 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 12:38:33 -0700 Subject: [PATCH 28/33] Move image loading to view model --- RedBookPlayer.GUI/MainWindow.xaml.cs | 4 +- RedBookPlayer.GUI/PlayerView.xaml | 2 +- RedBookPlayer.GUI/PlayerView.xaml.cs | 50 ----------------- .../ViewModels/PlayerViewModel.cs | 56 +++++++++++++++++++ 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs index 2172c1a..08fd2b1 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/MainWindow.xaml.cs @@ -111,7 +111,7 @@ namespace RedBookPlayer.GUI IEnumerable fileNames = e.Data.GetFileNames(); foreach(string filename in fileNames) { - bool loaded = await playerView.LoadImage(filename); + bool loaded = await playerView?.PlayerViewModel?.LoadImage(filename); if(loaded) break; } @@ -132,7 +132,7 @@ namespace RedBookPlayer.GUI // Load image else if (e.Key == App.Settings.LoadImageKey) { - playerView?.LoadButton_Click(this, null); + playerView?.PlayerViewModel?.ExecuteLoad(); } // Toggle playback diff --git a/RedBookPlayer.GUI/PlayerView.xaml b/RedBookPlayer.GUI/PlayerView.xaml index e531106..26d230d 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml +++ b/RedBookPlayer.GUI/PlayerView.xaml @@ -3,7 +3,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="RedBookPlayer.GUI.PlayerView" Width="900" Height="400"> - + diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index a1b763b..a4596fd 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -1,12 +1,8 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.IO; -using System.Linq; -using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; -using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; @@ -59,22 +55,6 @@ namespace RedBookPlayer.GUI #region Helpers - /// - /// Load an image from the path - /// - /// Path to the image to load - public async Task LoadImage(string path) - { - return await Dispatcher.UIThread.InvokeAsync(() => - { - PlayerViewModel.Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayHiddenTracks, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); - if (PlayerViewModel.Initialized) - MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); - - return PlayerViewModel.Initialized; - }); - } - /// /// Update the view model with new settings /// @@ -112,23 +92,6 @@ namespace RedBookPlayer.GUI } } - /// - /// Generate a path selection dialog box - /// - /// User-selected path, if possible - private async Task GetPath() - { - var dialog = new OpenFileDialog { AllowMultiple = false }; - List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); - dialog.Filters.Add(new FileDialogFilter() - { - Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", - Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) - }); - - return (await dialog.ShowAsync((Window)Parent.Parent))?.FirstOrDefault(); - } - /// /// Initialize the displayed digits array /// @@ -201,18 +164,5 @@ namespace RedBookPlayer.GUI } #endregion - - #region Event Handlers - - public async void LoadButton_Click(object sender, RoutedEventArgs e) - { - string path = await GetPath(); - if (path == null) - return; - - await LoadImage(path); - } - - #endregion } } \ No newline at end of file diff --git a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index a5cad4a..df8269b 100644 --- a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -1,6 +1,10 @@ +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reactive; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Threading; using ReactiveUI; using RedBookPlayer.Common.Hardware; @@ -180,6 +184,11 @@ namespace RedBookPlayer.GUI.ViewModels #region Commands + /// + /// Command for loading a disc + /// + public ReactiveCommand LoadCommand { get; } + #region Playback /// @@ -279,6 +288,8 @@ namespace RedBookPlayer.GUI.ViewModels /// public PlayerViewModel() { + LoadCommand = ReactiveCommand.Create(ExecuteLoad); + PlayCommand = ReactiveCommand.Create(ExecutePlay); PauseCommand = ReactiveCommand.Create(ExecutePause); TogglePlayPauseCommand = ReactiveCommand.Create(ExecuteTogglePlayPause); @@ -461,6 +472,34 @@ namespace RedBookPlayer.GUI.ViewModels return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); } + /// + /// Load a disc image from a selection box + /// + public async void ExecuteLoad() + { + string path = await GetPath(); + if(path == null) + return; + + await LoadImage(path); + } + + /// + /// Load an image from the path + /// + /// Path to the image to load + public async Task LoadImage(string path) + { + return await Dispatcher.UIThread.InvokeAsync(() => + { + Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayHiddenTracks, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); + if(Initialized) + MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); + + return Initialized; + }); + } + /// /// Set the value for loading data tracks [CompactDisc only] /// @@ -488,6 +527,23 @@ namespace RedBookPlayer.GUI.ViewModels return sectorTime; } + /// + /// Generate a path selection dialog box + /// + /// User-selected path, if possible + private async Task GetPath() + { + var dialog = new OpenFileDialog { AllowMultiple = false }; + List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); + dialog.Filters.Add(new FileDialogFilter() + { + Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", + Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) + }); + + return (await dialog.ShowAsync(MainWindow.Instance))?.FirstOrDefault(); + } + /// /// Update the view-model from the Player /// From b11ccc48db9a1b344204f24932fda2eef93b12e2 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 15:40:56 -0700 Subject: [PATCH 29/33] `Common` -> `Models` --- RedBookPlayer.GUI/MainWindow.xaml.cs | 4 + RedBookPlayer.GUI/PlayerView.xaml.cs | 99 +--------- RedBookPlayer.GUI/RedBookPlayer.GUI.csproj | 2 +- .../ViewModels/PlayerViewModel.cs | 173 +++++++++++++----- .../Discs/CompactDisc.cs | 2 +- .../Discs/OpticalDiscBase.cs | 2 +- .../Factories/OpticalDiscFactory.cs | 4 +- .../Hardware/DeEmphasisFilter.cs | 2 +- .../Hardware/Player.cs | 6 +- .../Hardware/PlayerSource.cs | 2 +- .../Hardware/SoundOutput.cs | 4 +- .../RedBookPlayer.Models.csproj | 0 .../nuget.config | 0 RedBookPlayer.sln | 2 +- 14 files changed, 150 insertions(+), 152 deletions(-) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Discs/CompactDisc.cs (99%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Discs/OpticalDiscBase.cs (99%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Factories/OpticalDiscFactory.cs (98%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Hardware/DeEmphasisFilter.cs (97%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Hardware/Player.cs (99%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Hardware/PlayerSource.cs (96%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/Hardware/SoundOutput.cs (99%) rename RedBookPlayer.Common/RedBookPlayer.Common.csproj => RedBookPlayer.Models/RedBookPlayer.Models.csproj (100%) rename {RedBookPlayer.Common => RedBookPlayer.Models}/nuget.config (100%) diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/MainWindow.xaml.cs index 08fd2b1..95d8ff0 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/MainWindow.xaml.cs @@ -65,6 +65,8 @@ namespace RedBookPlayer.GUI Instance.Width = ((PlayerView)Instance.ContentControl.Content).Width; Instance.Height = ((PlayerView)Instance.ContentControl.Content).Height; + + pvm.InitializeDigits(); } /// @@ -82,6 +84,8 @@ namespace RedBookPlayer.GUI ContentControl.Content = new PlayerView(); + ((PlayerView)ContentControl.Content).PlayerViewModel.InitializeDigits(); + CanResize = false; KeyDown += OnKeyDown; diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/PlayerView.xaml.cs index a4596fd..bda4343 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/PlayerView.xaml.cs @@ -1,12 +1,5 @@ -using System; -using System.ComponentModel; -using System.IO; -using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -using Avalonia.Media.Imaging; -using Avalonia.Platform; -using Avalonia.Threading; using RedBookPlayer.GUI.ViewModels; namespace RedBookPlayer.GUI @@ -18,11 +11,6 @@ namespace RedBookPlayer.GUI /// public PlayerViewModel PlayerViewModel => DataContext as PlayerViewModel; - /// - /// Set of images representing the digits for the UI - /// - private Image[] _digits; - /// /// Initialize the UI based on the default theme /// @@ -42,15 +30,12 @@ namespace RedBookPlayer.GUI /// Existing PlayerViewModel to load in instead of creating a new one public PlayerView(string xaml, PlayerViewModel playerViewModel) { + LoadTheme(xaml); + if(playerViewModel != null) DataContext = playerViewModel; else DataContext = new PlayerViewModel(); - - PlayerViewModel.PropertyChanged += PlayerViewModelStateChanged; - - LoadTheme(xaml); - InitializeDigits(); } #region Helpers @@ -64,69 +49,6 @@ namespace RedBookPlayer.GUI PlayerViewModel.SetLoadHiddenTracks(App.Settings.PlayHiddenTracks); } - /// - /// Load the png image for a given character based on the theme - /// - /// Character to load the image for - /// Bitmap representing the loaded image - private Bitmap GetBitmap(char character) - { - try - { - if(App.Settings.SelectedTheme == "default") - { - IAssetLoader assets = AvaloniaLocator.Current.GetService(); - - return new Bitmap(assets.Open(new Uri($"avares://RedBookPlayer/Assets/{character}.png"))); - } - else - { - string themeDirectory = $"{Directory.GetCurrentDirectory()}/themes/{App.Settings.SelectedTheme}"; - using FileStream stream = File.Open($"{themeDirectory}/{character}.png", FileMode.Open); - return new Bitmap(stream); - } - } - catch - { - return null; - } - } - - /// - /// Initialize the displayed digits array - /// - private void InitializeDigits() - { - _digits = new Image[] - { - this.FindControl("TrackDigit1"), - this.FindControl("TrackDigit2"), - - this.FindControl("IndexDigit1"), - this.FindControl("IndexDigit2"), - - this.FindControl("TimeDigit1"), - this.FindControl("TimeDigit2"), - this.FindControl("TimeDigit3"), - this.FindControl("TimeDigit4"), - this.FindControl("TimeDigit5"), - this.FindControl("TimeDigit6"), - - this.FindControl("TotalTracksDigit1"), - this.FindControl("TotalTracksDigit2"), - - this.FindControl("TotalIndexesDigit1"), - this.FindControl("TotalIndexesDigit2"), - - this.FindControl("TotalTimeDigit1"), - this.FindControl("TotalTimeDigit2"), - this.FindControl("TotalTimeDigit3"), - this.FindControl("TotalTimeDigit4"), - this.FindControl("TotalTimeDigit5"), - this.FindControl("TotalTimeDigit6"), - }; - } - /// /// Load the theme from a XAML, if possible /// @@ -146,23 +68,6 @@ namespace RedBookPlayer.GUI } } - /// - /// Update the UI from the view-model - /// - private void PlayerViewModelStateChanged(object sender, PropertyChangedEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - string digitString = PlayerViewModel?.GenerateDigitString() ?? string.Empty.PadLeft(20, '-'); - for(int i = 0; i < _digits.Length; i++) - { - Bitmap digitImage = GetBitmap(digitString[i]); - if(_digits[i] != null && digitImage != null) - _digits[i].Source = digitImage; - } - }); - } - #endregion } } \ No newline at end of file diff --git a/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj b/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj index d2a03b6..36d6bb4 100644 --- a/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj +++ b/RedBookPlayer.GUI/RedBookPlayer.GUI.csproj @@ -23,7 +23,7 @@ - + diff --git a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index df8269b..b7b227d 100644 --- a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -1,12 +1,17 @@ +using System; using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Linq; using System.Reactive; using System.Threading.Tasks; +using Avalonia; using Avalonia.Controls; +using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Threading; using ReactiveUI; -using RedBookPlayer.Common.Hardware; +using RedBookPlayer.Models.Hardware; namespace RedBookPlayer.GUI.ViewModels { @@ -17,6 +22,11 @@ namespace RedBookPlayer.GUI.ViewModels /// private Player _player; + /// + /// Set of images representing the digits for the UI + /// + private Image[] _digits; + #region Player Passthrough #region OpticalDisc Passthrough @@ -433,11 +443,88 @@ namespace RedBookPlayer.GUI.ViewModels #region Helpers + /// + /// Load a disc image from a selection box + /// + public async void ExecuteLoad() + { + string path = await GetPath(); + if(path == null) + return; + + await LoadImage(path); + } + + /// + /// Initialize the displayed digits array + /// + public void InitializeDigits() + { + PlayerView playerView = MainWindow.Instance.ContentControl.Content as PlayerView; + + _digits = new Image[] + { + playerView.FindControl("TrackDigit1"), + playerView.FindControl("TrackDigit2"), + + playerView.FindControl("IndexDigit1"), + playerView.FindControl("IndexDigit2"), + + playerView.FindControl("TimeDigit1"), + playerView.FindControl("TimeDigit2"), + playerView.FindControl("TimeDigit3"), + playerView.FindControl("TimeDigit4"), + playerView.FindControl("TimeDigit5"), + playerView.FindControl("TimeDigit6"), + + playerView.FindControl("TotalTracksDigit1"), + playerView.FindControl("TotalTracksDigit2"), + + playerView.FindControl("TotalIndexesDigit1"), + playerView.FindControl("TotalIndexesDigit2"), + + playerView.FindControl("TotalTimeDigit1"), + playerView.FindControl("TotalTimeDigit2"), + playerView.FindControl("TotalTimeDigit3"), + playerView.FindControl("TotalTimeDigit4"), + playerView.FindControl("TotalTimeDigit5"), + playerView.FindControl("TotalTimeDigit6"), + }; + } + + /// + /// Load an image from the path + /// + /// Path to the image to load + public async Task LoadImage(string path) + { + return await Dispatcher.UIThread.InvokeAsync(() => + { + Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayHiddenTracks, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); + if(Initialized) + MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); + + return Initialized; + }); + } + + /// + /// Set the value for loading data tracks [CompactDisc only] + /// + /// True to enable loading data tracks, false otherwise + public void SetLoadDataTracks(bool load) => _player?.SetLoadDataTracks(load); + + /// + /// Set the value for loading hidden tracks [CompactDisc only] + /// + /// True to enable loading hidden tracks, false otherwise + public void SetLoadHiddenTracks(bool load) => _player?.SetLoadHiddenTracks(load); + /// /// Generate the digit string to be interpreted by the frontend /// /// String representing the digits for the frontend - public string GenerateDigitString() + private string GenerateDigitString() { // If the disc isn't initialized, return all '-' characters if(Initialized != true) @@ -473,45 +560,33 @@ namespace RedBookPlayer.GUI.ViewModels } /// - /// Load a disc image from a selection box + /// Load the png image for a given character based on the theme /// - public async void ExecuteLoad() + /// Character to load the image for + /// Bitmap representing the loaded image + private Bitmap GetBitmap(char character) { - string path = await GetPath(); - if(path == null) - return; - - await LoadImage(path); - } - - /// - /// Load an image from the path - /// - /// Path to the image to load - public async Task LoadImage(string path) - { - return await Dispatcher.UIThread.InvokeAsync(() => + try { - Init(path, App.Settings.GenerateMissingTOC, App.Settings.PlayHiddenTracks, App.Settings.PlayDataTracks, App.Settings.AutoPlay, App.Settings.Volume); - if(Initialized) - MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); + if(App.Settings.SelectedTheme == "default") + { + IAssetLoader assets = AvaloniaLocator.Current.GetService(); - return Initialized; - }); + return new Bitmap(assets.Open(new Uri($"avares://RedBookPlayer/Assets/{character}.png"))); + } + else + { + string themeDirectory = $"{Directory.GetCurrentDirectory()}/themes/{App.Settings.SelectedTheme}"; + using FileStream stream = File.Open($"{themeDirectory}/{character}.png", FileMode.Open); + return new Bitmap(stream); + } + } + catch + { + return null; + } } - /// - /// Set the value for loading data tracks [CompactDisc only] - /// - /// True to enable loading data tracks, false otherwise - public void SetLoadDataTracks(bool load) => _player?.SetLoadDataTracks(load); - - /// - /// Set the value for loading hidden tracks [CompactDisc only] - /// - /// True to enable loading hidden tracks, false otherwise - public void SetLoadHiddenTracks(bool load) => _player?.SetLoadHiddenTracks(load); - /// /// Get current sector time, accounting for offsets /// @@ -533,15 +608,18 @@ namespace RedBookPlayer.GUI.ViewModels /// User-selected path, if possible private async Task GetPath() { - var dialog = new OpenFileDialog { AllowMultiple = false }; - List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); - dialog.Filters.Add(new FileDialogFilter() + return await Dispatcher.UIThread.InvokeAsync(async () => { - Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", - Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) - }); + var dialog = new OpenFileDialog { AllowMultiple = false }; + List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); + dialog.Filters.Add(new FileDialogFilter() + { + Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", + Extensions = knownExtensions.ConvertAll(e => e.TrimStart('.')) + }); - return (await dialog.ShowAsync(MainWindow.Instance))?.FirstOrDefault(); + return (await dialog.ShowAsync(MainWindow.Instance))?.FirstOrDefault(); + }); } /// @@ -567,6 +645,17 @@ namespace RedBookPlayer.GUI.ViewModels Playing = _player.Playing; ApplyDeEmphasis = _player.ApplyDeEmphasis; Volume = _player.Volume; + + Dispatcher.UIThread.InvokeAsync(() => + { + string digitString = GenerateDigitString() ?? string.Empty.PadLeft(20, '-'); + for(int i = 0; i < _digits.Length; i++) + { + Bitmap digitImage = GetBitmap(digitString[i]); + if(_digits[i] != null && digitImage != null) + _digits[i].Source = digitImage; + } + }); } #endregion diff --git a/RedBookPlayer.Common/Discs/CompactDisc.cs b/RedBookPlayer.Models/Discs/CompactDisc.cs similarity index 99% rename from RedBookPlayer.Common/Discs/CompactDisc.cs rename to RedBookPlayer.Models/Discs/CompactDisc.cs index 45c634e..6690b6e 100644 --- a/RedBookPlayer.Common/Discs/CompactDisc.cs +++ b/RedBookPlayer.Models/Discs/CompactDisc.cs @@ -9,7 +9,7 @@ using Aaru.Helpers; using ReactiveUI; using static Aaru.Decoders.CD.FullTOC; -namespace RedBookPlayer.Common.Discs +namespace RedBookPlayer.Models.Discs { public class CompactDisc : OpticalDiscBase, IReactiveObject { diff --git a/RedBookPlayer.Common/Discs/OpticalDiscBase.cs b/RedBookPlayer.Models/Discs/OpticalDiscBase.cs similarity index 99% rename from RedBookPlayer.Common/Discs/OpticalDiscBase.cs rename to RedBookPlayer.Models/Discs/OpticalDiscBase.cs index 0a0ab60..f2ce155 100644 --- a/RedBookPlayer.Common/Discs/OpticalDiscBase.cs +++ b/RedBookPlayer.Models/Discs/OpticalDiscBase.cs @@ -2,7 +2,7 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using ReactiveUI; -namespace RedBookPlayer.Common.Discs +namespace RedBookPlayer.Models.Discs { public abstract class OpticalDiscBase : ReactiveObject { diff --git a/RedBookPlayer.Common/Factories/OpticalDiscFactory.cs b/RedBookPlayer.Models/Factories/OpticalDiscFactory.cs similarity index 98% rename from RedBookPlayer.Common/Factories/OpticalDiscFactory.cs rename to RedBookPlayer.Models/Factories/OpticalDiscFactory.cs index b4f290c..d09beaf 100644 --- a/RedBookPlayer.Common/Factories/OpticalDiscFactory.cs +++ b/RedBookPlayer.Models/Factories/OpticalDiscFactory.cs @@ -3,9 +3,9 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Metadata; using Aaru.DiscImages; using Aaru.Filters; -using RedBookPlayer.Common.Discs; +using RedBookPlayer.Models.Discs; -namespace RedBookPlayer.Common.Factories +namespace RedBookPlayer.Models.Factories { public static class OpticalDiscFactory { diff --git a/RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs b/RedBookPlayer.Models/Hardware/DeEmphasisFilter.cs similarity index 97% rename from RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs rename to RedBookPlayer.Models/Hardware/DeEmphasisFilter.cs index d33117e..5b74b1d 100644 --- a/RedBookPlayer.Common/Hardware/DeEmphasisFilter.cs +++ b/RedBookPlayer.Models/Hardware/DeEmphasisFilter.cs @@ -1,7 +1,7 @@ using System; using NWaves.Filters.BiQuad; -namespace RedBookPlayer.Common.Hardware +namespace RedBookPlayer.Models.Hardware { /// /// Filter for applying de-emphasis to audio diff --git a/RedBookPlayer.Common/Hardware/Player.cs b/RedBookPlayer.Models/Hardware/Player.cs similarity index 99% rename from RedBookPlayer.Common/Hardware/Player.cs rename to RedBookPlayer.Models/Hardware/Player.cs index 8bf225c..7db9f0c 100644 --- a/RedBookPlayer.Common/Hardware/Player.cs +++ b/RedBookPlayer.Models/Hardware/Player.cs @@ -2,10 +2,10 @@ using System; using System.ComponentModel; using Aaru.CommonTypes.Enums; using ReactiveUI; -using RedBookPlayer.Common.Discs; -using RedBookPlayer.Common.Factories; +using RedBookPlayer.Models.Discs; +using RedBookPlayer.Models.Factories; -namespace RedBookPlayer.Common.Hardware +namespace RedBookPlayer.Models.Hardware { public class Player : ReactiveObject { diff --git a/RedBookPlayer.Common/Hardware/PlayerSource.cs b/RedBookPlayer.Models/Hardware/PlayerSource.cs similarity index 96% rename from RedBookPlayer.Common/Hardware/PlayerSource.cs rename to RedBookPlayer.Models/Hardware/PlayerSource.cs index 630a8d1..203ff4f 100644 --- a/RedBookPlayer.Common/Hardware/PlayerSource.cs +++ b/RedBookPlayer.Models/Hardware/PlayerSource.cs @@ -2,7 +2,7 @@ using System; using CSCore; using WaveFormat = CSCore.WaveFormat; -namespace RedBookPlayer.Common.Hardware +namespace RedBookPlayer.Models.Hardware { public class PlayerSource : IWaveSource { diff --git a/RedBookPlayer.Common/Hardware/SoundOutput.cs b/RedBookPlayer.Models/Hardware/SoundOutput.cs similarity index 99% rename from RedBookPlayer.Common/Hardware/SoundOutput.cs rename to RedBookPlayer.Models/Hardware/SoundOutput.cs index 89f2b8c..743c35b 100644 --- a/RedBookPlayer.Common/Hardware/SoundOutput.cs +++ b/RedBookPlayer.Models/Hardware/SoundOutput.cs @@ -5,9 +5,9 @@ using CSCore.SoundOut; using NWaves.Audio; using NWaves.Filters.BiQuad; using ReactiveUI; -using RedBookPlayer.Common.Discs; +using RedBookPlayer.Models.Discs; -namespace RedBookPlayer.Common.Hardware +namespace RedBookPlayer.Models.Hardware { public class SoundOutput : ReactiveObject { diff --git a/RedBookPlayer.Common/RedBookPlayer.Common.csproj b/RedBookPlayer.Models/RedBookPlayer.Models.csproj similarity index 100% rename from RedBookPlayer.Common/RedBookPlayer.Common.csproj rename to RedBookPlayer.Models/RedBookPlayer.Models.csproj diff --git a/RedBookPlayer.Common/nuget.config b/RedBookPlayer.Models/nuget.config similarity index 100% rename from RedBookPlayer.Common/nuget.config rename to RedBookPlayer.Models/nuget.config diff --git a/RedBookPlayer.sln b/RedBookPlayer.sln index a36debc..3b35228 100644 --- a/RedBookPlayer.sln +++ b/RedBookPlayer.sln @@ -40,7 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedBookPlayer.Common", "RedBookPlayer.Common\RedBookPlayer.Common.csproj", "{462A3B8E-A5D4-4539-8469-1647B47AB2A8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedBookPlayer.Models", "RedBookPlayer.Models\RedBookPlayer.Models.csproj", "{462A3B8E-A5D4-4539-8469-1647B47AB2A8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From f2712d32384f82c09117d4367fd3a84d059377e4 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 15:49:43 -0700 Subject: [PATCH 30/33] Add Views namespace --- RedBookPlayer.GUI/App.xaml.cs | 1 + RedBookPlayer.GUI/{ => Views}/MainWindow.xaml | 2 +- RedBookPlayer.GUI/{ => Views}/MainWindow.xaml.cs | 2 +- RedBookPlayer.GUI/{ => Views}/PlayerView.xaml | 2 +- RedBookPlayer.GUI/{ => Views}/PlayerView.xaml.cs | 2 +- RedBookPlayer.GUI/{ => Views}/SettingsWindow.xaml | 2 +- RedBookPlayer.GUI/{ => Views}/SettingsWindow.xaml.cs | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) rename RedBookPlayer.GUI/{ => Views}/MainWindow.xaml (76%) rename RedBookPlayer.GUI/{ => Views}/MainWindow.xaml.cs (99%) rename RedBookPlayer.GUI/{ => Views}/PlayerView.xaml (98%) rename RedBookPlayer.GUI/{ => Views}/PlayerView.xaml.cs (98%) rename RedBookPlayer.GUI/{ => Views}/SettingsWindow.xaml (98%) rename RedBookPlayer.GUI/{ => Views}/SettingsWindow.xaml.cs (99%) diff --git a/RedBookPlayer.GUI/App.xaml.cs b/RedBookPlayer.GUI/App.xaml.cs index 03c77fd..20e6a9d 100644 --- a/RedBookPlayer.GUI/App.xaml.cs +++ b/RedBookPlayer.GUI/App.xaml.cs @@ -6,6 +6,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using RedBookPlayer.GUI; +using RedBookPlayer.GUI.Views; namespace RedBookPlayer { diff --git a/RedBookPlayer.GUI/MainWindow.xaml b/RedBookPlayer.GUI/Views/MainWindow.xaml similarity index 76% rename from RedBookPlayer.GUI/MainWindow.xaml rename to RedBookPlayer.GUI/Views/MainWindow.xaml index 56dbf79..1a3e211 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml +++ b/RedBookPlayer.GUI/Views/MainWindow.xaml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/RedBookPlayer.GUI/MainWindow.xaml.cs b/RedBookPlayer.GUI/Views/MainWindow.xaml.cs similarity index 99% rename from RedBookPlayer.GUI/MainWindow.xaml.cs rename to RedBookPlayer.GUI/Views/MainWindow.xaml.cs index 95d8ff0..3e5a26a 100644 --- a/RedBookPlayer.GUI/MainWindow.xaml.cs +++ b/RedBookPlayer.GUI/Views/MainWindow.xaml.cs @@ -7,7 +7,7 @@ using Avalonia.Input; using Avalonia.Markup.Xaml; using RedBookPlayer.GUI.ViewModels; -namespace RedBookPlayer.GUI +namespace RedBookPlayer.GUI.Views { public class MainWindow : Window { diff --git a/RedBookPlayer.GUI/PlayerView.xaml b/RedBookPlayer.GUI/Views/PlayerView.xaml similarity index 98% rename from RedBookPlayer.GUI/PlayerView.xaml rename to RedBookPlayer.GUI/Views/PlayerView.xaml index 26d230d..941522e 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml +++ b/RedBookPlayer.GUI/Views/PlayerView.xaml @@ -1,7 +1,7 @@ + x:Class="RedBookPlayer.GUI.Views.PlayerView" Width="900" Height="400"> diff --git a/RedBookPlayer.GUI/PlayerView.xaml.cs b/RedBookPlayer.GUI/Views/PlayerView.xaml.cs similarity index 98% rename from RedBookPlayer.GUI/PlayerView.xaml.cs rename to RedBookPlayer.GUI/Views/PlayerView.xaml.cs index bda4343..253fd9b 100644 --- a/RedBookPlayer.GUI/PlayerView.xaml.cs +++ b/RedBookPlayer.GUI/Views/PlayerView.xaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RedBookPlayer.GUI.ViewModels; -namespace RedBookPlayer.GUI +namespace RedBookPlayer.GUI.Views { public class PlayerView : UserControl { diff --git a/RedBookPlayer.GUI/SettingsWindow.xaml b/RedBookPlayer.GUI/Views/SettingsWindow.xaml similarity index 98% rename from RedBookPlayer.GUI/SettingsWindow.xaml rename to RedBookPlayer.GUI/Views/SettingsWindow.xaml index 158ba9e..d64c388 100644 --- a/RedBookPlayer.GUI/SettingsWindow.xaml +++ b/RedBookPlayer.GUI/Views/SettingsWindow.xaml @@ -1,7 +1,7 @@ + d:DesignHeight="450" x:Class="RedBookPlayer.GUI.Views.SettingsWindow" Title="Settings" SizeToContent="WidthAndHeight"> diff --git a/RedBookPlayer.GUI/SettingsWindow.xaml.cs b/RedBookPlayer.GUI/Views/SettingsWindow.xaml.cs similarity index 99% rename from RedBookPlayer.GUI/SettingsWindow.xaml.cs rename to RedBookPlayer.GUI/Views/SettingsWindow.xaml.cs index 3f1d226..9b5c15d 100644 --- a/RedBookPlayer.GUI/SettingsWindow.xaml.cs +++ b/RedBookPlayer.GUI/Views/SettingsWindow.xaml.cs @@ -6,7 +6,7 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; -namespace RedBookPlayer.GUI +namespace RedBookPlayer.GUI.Views { public class SettingsWindow : Window { From 9288cc103b97a3363349ef1b6b35e2b16786cfe7 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 15:53:44 -0700 Subject: [PATCH 31/33] Fix build because of missing namespaces --- RedBookPlayer.GUI/Settings.cs | 2 +- RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RedBookPlayer.GUI/Settings.cs b/RedBookPlayer.GUI/Settings.cs index 855b2c8..df14848 100644 --- a/RedBookPlayer.GUI/Settings.cs +++ b/RedBookPlayer.GUI/Settings.cs @@ -2,7 +2,7 @@ using System; using System.IO; using System.Text.Json; using Avalonia.Input; -using RedBookPlayer.GUI; +using RedBookPlayer.GUI.Views; namespace RedBookPlayer.GUI { diff --git a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index b7b227d..73fd911 100644 --- a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -11,6 +11,7 @@ using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Threading; using ReactiveUI; +using RedBookPlayer.GUI.Views; using RedBookPlayer.Models.Hardware; namespace RedBookPlayer.GUI.ViewModels From 0c76752ac0c35f94d1e64e261708a2a910503354 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 16:37:12 -0700 Subject: [PATCH 32/33] Fix UI thread issues --- RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs index 73fd911..41edc50 100644 --- a/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs +++ b/RedBookPlayer.GUI/ViewModels/PlayerViewModel.cs @@ -647,7 +647,15 @@ namespace RedBookPlayer.GUI.ViewModels ApplyDeEmphasis = _player.ApplyDeEmphasis; Volume = _player.Volume; - Dispatcher.UIThread.InvokeAsync(() => + UpdateDigits(); + } + + /// + /// Update UI + /// + private void UpdateDigits() + { + Dispatcher.UIThread.Post(() => { string digitString = GenerateDigitString() ?? string.Empty.PadLeft(20, '-'); for(int i = 0; i < _digits.Length; i++) From 53e9cbc6958ffa54bb58ac970ea1c4e24431d24b Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 12 Jul 2021 23:08:50 -0700 Subject: [PATCH 33/33] Update gitmodules The standard version of the library does NOT support OpenAL on both Windows and Linux. This branch supports it and has been open as a PR on the main netstandard support branch for a while now. --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 38b3a5b..59d2a97 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,5 @@ url = https://github.com/aaru-dps/Aaru.git [submodule "cscore"] path = cscore - url = https://github.com/filoe/cscore.git + url = https://github.com/sk-zk/cscore.git + branch = netstandard