using System.ComponentModel; using ReactiveUI; using RedBookPlayer.Common.Hardware; namespace RedBookPlayer.Common { public class PlayerViewModel : ReactiveObject { /// /// Player representing the internal state /// private Player _player; /// /// Last volume for mute toggling /// private int? _lastVolume = null; #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 the sector starting the section /// public ulong SectionStartSector { get => _sectionStartSector; private set => this.RaiseAndSetIfChanged(ref _sectionStartSector, 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 ulong _sectionStartSector; 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 /// public bool Initialized => _player?.Initialized ?? false; /// /// Indicate if the output is 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 /// public int Volume { get => _volume; private set => this.RaiseAndSetIfChanged(ref _volume, value); } private bool? _playing; private bool _applyDeEmphasis; private int _volume; #endregion #endregion /// /// 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 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 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, loadHiddenTracks, loadDataTracks, autoPlay, defaultVolume); if(Initialized) { _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 /// 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 /// /// Set de-emphasis status /// /// 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); /// /// 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 /// /// 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 /// private void PlayerStateChanged(object sender, PropertyChangedEventArgs e) { if(_player?.Initialized != true) return; CurrentTrackNumber = _player.CurrentTrackNumber; CurrentTrackIndex = _player.CurrentTrackIndex; CurrentSector = _player.CurrentSector; SectionStartSector = _player.SectionStartSector; HiddenTrack = _player.HiddenTrack; QuadChannel = _player.QuadChannel; IsDataTrack = _player.IsDataTrack; CopyAllowed = _player.CopyAllowed; TrackHasEmphasis = _player.TrackHasEmphasis; Playing = _player.Playing; ApplyDeEmphasis = _player.ApplyDeEmphasis; Volume = _player.Volume; } #endregion } }