diff --git a/RedBookPlayer/GUI/PlayerView.xaml.cs b/RedBookPlayer/GUI/PlayerView.xaml.cs
index b3c4166..6989a33 100644
--- a/RedBookPlayer/GUI/PlayerView.xaml.cs
+++ b/RedBookPlayer/GUI/PlayerView.xaml.cs
@@ -12,16 +12,15 @@ using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Threading;
-using RedBookPlayer.Hardware;
namespace RedBookPlayer.GUI
{
public class PlayerView : UserControl
{
///
- /// Player representing the internal state
+ /// Read-only access to the view model
///
- public static Player Player = new Player();
+ public PlayerViewModel PlayerViewModel => DataContext as PlayerViewModel;
///
/// Set of images representing the digits for the UI
@@ -71,13 +70,12 @@ namespace RedBookPlayer.GUI
public async Task LoadImage(string path)
{
// If the player is currently running, stop it
- if((DataContext as PlayerViewModel).Playing != true)
- (DataContext as PlayerViewModel).Playing = null;
+ if(PlayerViewModel.Playing != true) PlayerViewModel.Playing = null;
- bool result = await Task.Run(() =>
+ bool result = await Dispatcher.UIThread.InvokeAsync(() =>
{
- Player.Init(path, App.Settings.AutoPlay);
- return Player.Initialized;
+ PlayerViewModel.Init(path, App.Settings.AutoPlay);
+ return PlayerViewModel.Initialized;
});
if(result)
@@ -122,7 +120,7 @@ namespace RedBookPlayer.GUI
private void InitializeComponent(string xaml)
{
DataContext = new PlayerViewModel();
- (DataContext as PlayerViewModel).PropertyChanged += UpdateModel;
+ PlayerViewModel.PropertyChanged += UpdateModel;
// Load the theme
try
@@ -199,7 +197,7 @@ namespace RedBookPlayer.GUI
{
Dispatcher.UIThread.InvokeAsync(() =>
{
- Player.UpdateModel(DataContext as PlayerViewModel);
+ PlayerViewModel.UpdateModel();
});
}
@@ -210,14 +208,14 @@ namespace RedBookPlayer.GUI
{
Dispatcher.UIThread.InvokeAsync(() =>
{
- string digitString = Player.GenerateDigitString();
+ string digitString = PlayerViewModel.GenerateDigitString();
for (int i = 0; i < _digits.Length; i++)
{
if (_digits[i] != null)
_digits[i].Source = GetBitmap(digitString[i]);
}
- Player.UpdateDataContext(DataContext as PlayerViewModel);
+ PlayerViewModel?.UpdateView();
});
}
@@ -234,49 +232,55 @@ namespace RedBookPlayer.GUI
await LoadImage(path);
}
- public void PlayButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Playing = true;
+ public void PlayButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = true;
- public void PauseButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Playing = false;
+ public void PauseButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = false;
- public void PlayPauseButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Playing = !(DataContext as PlayerViewModel).Playing;
+ public void PlayPauseButton_Click(object sender, RoutedEventArgs e)
+ {
+ if(PlayerViewModel.Playing == true)
+ PlayerViewModel.Playing = false;
+ else
+ PlayerViewModel.Playing = true;
+ }
- public void StopButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Playing = null;
+ public void StopButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Playing = null;
- public void NextTrackButton_Click(object sender, RoutedEventArgs e) => Player.NextTrack();
+ public void NextTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextTrack();
- public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => Player.PreviousTrack();
+ public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousTrack();
- public void NextIndexButton_Click(object sender, RoutedEventArgs e) => Player.NextIndex(App.Settings.IndexButtonChangeTrack);
+ public void NextIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.NextIndex(App.Settings.IndexButtonChangeTrack);
- public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => Player.PreviousIndex(App.Settings.IndexButtonChangeTrack);
+ public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.PreviousIndex(App.Settings.IndexButtonChangeTrack);
- public void FastForwardButton_Click(object sender, RoutedEventArgs e) => Player.FastForward();
+ public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.FastForward();
- public void RewindButton_Click(object sender, RoutedEventArgs e) => Player.Rewind();
+ public void RewindButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Rewind();
- public void VolumeUpButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Volume++;
+ public void VolumeUpButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Volume++;
- public void VolumeDownButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).Volume--;
+ public void VolumeDownButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.Volume--;
public void MuteToggleButton_Click(object sender, RoutedEventArgs e)
{
if (_lastVolume == null)
{
- _lastVolume = (DataContext as PlayerViewModel).Volume;
- (DataContext as PlayerViewModel).Volume = 0;
+ _lastVolume = PlayerViewModel.Volume;
+ PlayerViewModel.Volume = 0;
}
else
{
- (DataContext as PlayerViewModel).Volume = _lastVolume.Value;
+ PlayerViewModel.Volume = _lastVolume.Value;
_lastVolume = null;
}
}
- public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).ApplyDeEmphasis = true;
+ public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = true;
- public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).ApplyDeEmphasis = false;
+ public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = false;
- public void EnableDisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => (DataContext as PlayerViewModel).ApplyDeEmphasis = !(DataContext as PlayerViewModel).ApplyDeEmphasis;
+ public void EnableDisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayerViewModel.ApplyDeEmphasis = !PlayerViewModel.ApplyDeEmphasis;
#endregion
}
diff --git a/RedBookPlayer/GUI/PlayerViewModel.cs b/RedBookPlayer/GUI/PlayerViewModel.cs
index 55cb5d3..a01caf9 100644
--- a/RedBookPlayer/GUI/PlayerViewModel.cs
+++ b/RedBookPlayer/GUI/PlayerViewModel.cs
@@ -1,11 +1,22 @@
+using System.Linq;
+using Aaru.CommonTypes.Enums;
using ReactiveUI;
+using RedBookPlayer.Discs;
+using RedBookPlayer.Hardware;
namespace RedBookPlayer.GUI
{
public class PlayerViewModel : ReactiveObject
{
+ ///
+ /// Player representing the internal state
+ ///
+ private Player _player;
+
#region Player Status
+ public bool Initialized => _player?.Initialized ?? false;
+
private bool? _playing;
public bool? Playing
{
@@ -93,5 +104,137 @@ namespace RedBookPlayer.GUI
}
#endregion
+
+ ///
+ /// Initialize the view model with a given image path
+ ///
+ /// Path to the disc image
+ /// True if playback should begin immediately, false otherwise
+ public void Init(string path, bool autoPlay)
+ {
+ _player = new Player();
+ _player.Init(path, autoPlay);
+
+ if(Initialized)
+ UpdateModel();
+ }
+
+ #region Playback
+
+ ///
+ /// Move to the next playable track
+ ///
+ public void NextTrack() => _player?.NextTrack();
+
+ ///
+ /// Move to the previous playable track
+ ///
+ public void PreviousTrack() => _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);
+
+ ///
+ /// Move to the previous index
+ ///
+ /// True if index changes can trigger a track change, false otherwise
+ public void PreviousIndex(bool changeTrack) => _player?.PreviousIndex(changeTrack);
+
+ ///
+ /// Fast-forward playback by 75 sectors, if possible
+ ///
+ public void FastForward() => _player?.FastForward();
+
+ ///
+ /// Rewind playback by 75 sectors, if possible
+ ///
+ public void Rewind() => _player?.Rewind();
+
+ #endregion
+
+ #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(_player?.OpticalDisc == null || !_player.OpticalDisc.Initialized)
+ return string.Empty.PadLeft(20, '-');
+
+ // Otherwise, take the current time into account
+ ulong sectorTime = _player.GetCurrentSectorTime();
+
+ int[] numbers = new int[]
+ {
+ _player.OpticalDisc.CurrentTrackNumber + 1,
+ _player.OpticalDisc.CurrentTrackIndex,
+
+ (int)(sectorTime / (75 * 60)),
+ (int)(sectorTime / 75 % 60),
+ (int)(sectorTime % 75),
+
+ _player.OpticalDisc.TotalTracks,
+ _player.OpticalDisc.TotalIndexes,
+
+ (int)(_player.OpticalDisc.TotalTime / (75 * 60)),
+ (int)(_player.OpticalDisc.TotalTime / 75 % 60),
+ (int)(_player.OpticalDisc.TotalTime % 75),
+ };
+
+ return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2)));
+ }
+
+ ///
+ /// Update the UI from the internal player
+ ///
+ public void UpdateView()
+ {
+ if(_player?.Initialized != true)
+ return;
+
+ Playing = _player.Playing;
+ CurrentSector = _player.GetCurrentSectorTime();
+ TotalSectors = _player.OpticalDisc.TotalTime;
+ Volume = App.Settings.Volume;
+
+ ApplyDeEmphasis = _player.ApplyDeEmphasis;
+ HiddenTrack = _player.OpticalDisc.TimeOffset > 150;
+
+ 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;
+ }
+ }
+
+ ///
+ /// Update the internal player from the UI
+ ///
+ public void UpdateModel()
+ {
+ if(_player?.Initialized != true)
+ return;
+
+ _player.SetPlayingState(Playing);
+ App.Settings.Volume = Volume;
+ _player.SetDeEmphasis(ApplyDeEmphasis);
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/RedBookPlayer/Hardware/Player.cs b/RedBookPlayer/Hardware/Player.cs
index 5e63a79..c18628e 100644
--- a/RedBookPlayer/Hardware/Player.cs
+++ b/RedBookPlayer/Hardware/Player.cs
@@ -1,11 +1,8 @@
using System;
using System.IO;
-using System.Linq;
-using Aaru.CommonTypes.Enums;
using Aaru.DiscImages;
using Aaru.Filters;
using RedBookPlayer.Discs;
-using RedBookPlayer.GUI;
namespace RedBookPlayer.Hardware
{
@@ -18,10 +15,15 @@ namespace RedBookPlayer.Hardware
///
public bool Initialized { get; private set; } = false;
+ ///
+ /// OpticalDisc object
+ ///
+ public OpticalDisc OpticalDisc { get; private set; }
+
///
/// Indicate if the disc is playing
///
- public bool Playing => _soundOutput?.Playing ?? false;
+ public bool? Playing => _soundOutput?.Playing;
///
/// Indicates if de-emphasis should be applied
@@ -32,11 +34,6 @@ namespace RedBookPlayer.Hardware
#region Private State Variables
- ///
- /// OpticalDisc object
- ///
- private OpticalDisc _opticalDisc;
-
///
/// Sound output handling class
///
@@ -55,7 +52,7 @@ namespace RedBookPlayer.Hardware
Initialized = false;
_soundOutput = new SoundOutput();
_soundOutput.ApplyDeEmphasis = false;
- _opticalDisc = null;
+ OpticalDisc = null;
try
{
@@ -70,7 +67,7 @@ namespace RedBookPlayer.Hardware
image.Open(filter);
// Generate and instantiate the disc
- _opticalDisc = OpticalDiscFactory.GenerateFromImage(image, App.Settings.AutoPlay);
+ OpticalDisc = OpticalDiscFactory.GenerateFromImage(image, App.Settings.AutoPlay);
}
catch
{
@@ -79,7 +76,7 @@ namespace RedBookPlayer.Hardware
}
// Initialize the sound output
- _soundOutput.Init(_opticalDisc, autoPlay);
+ _soundOutput.Init(OpticalDisc, autoPlay);
if(_soundOutput == null || !_soundOutput.Initialized)
return;
@@ -93,15 +90,19 @@ namespace RedBookPlayer.Hardware
/// Set the current audio playback state
///
/// True to start playback, false to pause, null to stop
- private void SetPlayingState(bool? start)
+ public void SetPlayingState(bool? start)
{
- if(_opticalDisc == null || !_opticalDisc.Initialized)
+ if(OpticalDisc == null || !OpticalDisc.Initialized)
+ return;
+
+ // If the playing state has not changed, do nothing
+ if(start == Playing)
return;
if(start == true)
{
_soundOutput.Play();
- _opticalDisc.SetTotalIndexes();
+ OpticalDisc.SetTotalIndexes();
}
else if(start == false)
{
@@ -110,7 +111,7 @@ namespace RedBookPlayer.Hardware
else
{
_soundOutput.Stop();
- _opticalDisc.LoadFirstTrack();
+ OpticalDisc.LoadFirstTrack();
}
}
@@ -119,17 +120,17 @@ namespace RedBookPlayer.Hardware
///
public void NextTrack()
{
- if(_opticalDisc == null || !_opticalDisc.Initialized)
+ if(OpticalDisc == null || !OpticalDisc.Initialized)
return;
- bool wasPlaying = Playing;
- if(wasPlaying) SetPlayingState(false);
+ bool? wasPlaying = Playing;
+ if(wasPlaying == true) SetPlayingState(false);
- _opticalDisc.NextTrack();
- if(_opticalDisc is CompactDisc compactDisc)
+ OpticalDisc.NextTrack();
+ if(OpticalDisc is CompactDisc compactDisc)
_soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis;
- if(wasPlaying) SetPlayingState(true);
+ if(wasPlaying == true) SetPlayingState(true);
}
///
@@ -137,17 +138,17 @@ namespace RedBookPlayer.Hardware
///
public void PreviousTrack()
{
- if(_opticalDisc == null || !_opticalDisc.Initialized)
+ if(OpticalDisc == null || !OpticalDisc.Initialized)
return;
- bool wasPlaying = Playing;
- if(wasPlaying) SetPlayingState(false);
+ bool? wasPlaying = Playing;
+ if(wasPlaying == true) SetPlayingState(false);
- _opticalDisc.PreviousTrack();
- if(_opticalDisc is CompactDisc compactDisc)
+ OpticalDisc.PreviousTrack();
+ if(OpticalDisc is CompactDisc compactDisc)
_soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis;
- if(wasPlaying) SetPlayingState(true);
+ if(wasPlaying == true) SetPlayingState(true);
}
///
@@ -156,17 +157,17 @@ 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) SetPlayingState(false);
+ bool? wasPlaying = Playing;
+ if(wasPlaying == true) SetPlayingState(false);
- _opticalDisc.NextIndex(changeTrack);
- if(_opticalDisc is CompactDisc compactDisc)
+ OpticalDisc.NextIndex(changeTrack);
+ if(OpticalDisc is CompactDisc compactDisc)
_soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis;
- if(wasPlaying) SetPlayingState(true);
+ if(wasPlaying == true) SetPlayingState(true);
}
///
@@ -175,17 +176,17 @@ 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) SetPlayingState(false);
+ bool? wasPlaying = Playing;
+ if(wasPlaying == true) SetPlayingState(false);
- _opticalDisc.PreviousIndex(changeTrack);
- if(_opticalDisc is CompactDisc compactDisc)
+ OpticalDisc.PreviousIndex(changeTrack);
+ if(OpticalDisc is CompactDisc compactDisc)
_soundOutput.ApplyDeEmphasis = compactDisc.TrackHasEmphasis;
- if(wasPlaying) SetPlayingState(true);
+ if(wasPlaying == true) SetPlayingState(true);
}
///
@@ -193,10 +194,10 @@ 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);
}
///
@@ -204,112 +205,38 @@ 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;
}
#endregion
#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(_opticalDisc == null || !_opticalDisc.Initialized)
- return string.Empty.PadLeft(20, '-');
-
- // Otherwise, take the current time into account
- ulong sectorTime = GetCurrentSectorTime();
-
- int[] numbers = new int[]
- {
- _opticalDisc.CurrentTrackNumber + 1,
- _opticalDisc.CurrentTrackIndex,
-
- (int)(sectorTime / (75 * 60)),
- (int)(sectorTime / 75 % 60),
- (int)(sectorTime % 75),
-
- _opticalDisc.TotalTracks,
- _opticalDisc.TotalIndexes,
-
- (int)(_opticalDisc.TotalTime / (75 * 60)),
- (int)(_opticalDisc.TotalTime / 75 % 60),
- (int)(_opticalDisc.TotalTime % 75),
- };
-
- return string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2)));
- }
-
- ///
- /// Update the data context for the frontend
- ///
- /// Data context to be updated
- public void UpdateDataContext(PlayerViewModel dataContext)
- {
- if(!Initialized || dataContext == null)
- return;
-
- dataContext.Playing = Playing;
- dataContext.CurrentSector = GetCurrentSectorTime();
- dataContext.TotalSectors = _opticalDisc.TotalTime;
- dataContext.Volume = App.Settings.Volume;
-
- dataContext.ApplyDeEmphasis = ApplyDeEmphasis;
- dataContext.HiddenTrack = _opticalDisc.TimeOffset > 150;
-
- if(_opticalDisc is CompactDisc compactDisc)
- {
- dataContext.QuadChannel = compactDisc.QuadChannel;
- dataContext.IsDataTrack = compactDisc.IsDataTrack;
- dataContext.CopyAllowed = compactDisc.CopyAllowed;
- dataContext.TrackHasEmphasis = compactDisc.TrackHasEmphasis;
- }
- else
- {
- dataContext.QuadChannel = false;
- dataContext.IsDataTrack = _opticalDisc.TrackType != TrackType.Audio;
- dataContext.CopyAllowed = false;
- dataContext.TrackHasEmphasis = false;
- }
- }
-
- ///
- /// Update the internal values from the frontend
- ///
- /// Data context to update from
- public void UpdateModel(PlayerViewModel dataContext)
- {
- if(!Initialized || dataContext == null)
- return;
-
- SetPlayingState(dataContext.Playing);
- App.Settings.Volume = dataContext.Volume;
- _soundOutput?.ToggleDeEmphasis(dataContext.ApplyDeEmphasis);
- }
-
///
/// Get current sector time, accounting for offsets
///
/// ulong representing the current sector time
- private ulong GetCurrentSectorTime()
+ 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
+ ///
+ /// True to enable, false to disable
+ public void SetDeEmphasis(bool apply) => _soundOutput?.ToggleDeEmphasis(apply);
+
#endregion
}
}
\ No newline at end of file