diff --git a/RedBookPlayer/PlayableDisc.cs b/RedBookPlayer/Discs/CompactDisc.cs similarity index 76% rename from RedBookPlayer/PlayableDisc.cs rename to RedBookPlayer/Discs/CompactDisc.cs index f030fa9..ce714a3 100644 --- a/RedBookPlayer/PlayableDisc.cs +++ b/RedBookPlayer/Discs/CompactDisc.cs @@ -8,24 +8,17 @@ using Aaru.Decoders.CD; using Aaru.Helpers; using static Aaru.Decoders.CD.FullTOC; -namespace RedBookPlayer +namespace RedBookPlayer.Discs { - public class PlayableDisc + public class CompactDisc : OpticalDisc { #region Public Fields - /// - /// Indicate if the disc is ready to be used - /// - public bool Initialized { get; private set; } = false; - - /// - /// Current track number - /// - public int CurrentTrackNumber + /// + public override int CurrentTrackNumber { get => _currentTrackNumber; - set + protected set { // Unset image means we can't do anything if(_image == null) @@ -54,8 +47,6 @@ namespace RedBookPlayer // Set track flags from subchannel data, if possible SetTrackFlags(track); - ApplyDeEmphasis = TrackHasEmphasis; - TotalIndexes = track.Indexes.Keys.Max(); CurrentTrackIndex = track.Indexes.Keys.Min(); @@ -73,13 +64,11 @@ namespace RedBookPlayer } } - /// - /// Current track index - /// - public ushort CurrentTrackIndex + /// + public override ushort CurrentTrackIndex { get => _currentTrackIndex; - set + protected set { // Unset image means we can't do anything if(_image == null) @@ -102,10 +91,8 @@ namespace RedBookPlayer } } - /// - /// Current sector number - /// - public ulong CurrentSector + /// + public override ulong CurrentSector { get => _currentSector; set @@ -146,14 +133,14 @@ namespace RedBookPlayer } /// - /// Represents the PRE flag + /// Represents the 4CH flag /// - public bool TrackHasEmphasis { get; private set; } = false; + public bool QuadChannel { get; private set; } = false; /// - /// Indicates if de-emphasis should be applied + /// Represents the DATA flag /// - public bool ApplyDeEmphasis { get; private set; } = false; + public bool IsDataTrack => TrackType != TrackType.Audio; /// /// Represents the DCP flag @@ -161,54 +148,14 @@ namespace RedBookPlayer public bool CopyAllowed { get; private set; } = false; /// - /// Represents the track type + /// Represents the PRE flag /// - public TrackType TrackType { get; private set; } - - /// - /// Represents the 4CH flag - /// - public bool QuadChannel { get; private set; } = false; - - /// - /// Represents the sector starting the section - /// - public ulong SectionStartSector { get; private set; } - - /// - /// Represents the total tracks on the disc - /// - public int TotalTracks { get; private set; } = 0; - - /// - /// Represents the total indices on the disc - /// - public int TotalIndexes { get; private set; } = 0; - - /// - /// Total sectors in the image - /// - public ulong TotalSectors => _image.Info.Sectors; - - /// - /// Represents the time adjustment offset for the disc - /// - public ulong TimeOffset { get; private set; } = 0; - - /// - /// Represents the total playing time for the disc - /// - public ulong TotalTime { get; private set; } = 0; + public bool TrackHasEmphasis { get; private set; } = false; #endregion #region Private State Variables - /// - /// Currently loaded disc image - /// - private IOpticalMediaImage _image; - /// /// Current track number /// @@ -231,12 +178,8 @@ namespace RedBookPlayer #endregion - /// - /// Initialize the disc with a given image - /// - /// Aaruformat image to load - /// True if playback should begin immediately, false otherwise - public void Init(IOpticalMediaImage image, bool autoPlay = false) + /// + public override void Init(IOpticalMediaImage image, bool autoPlay = false) { // If the image is null, we can't do anything if(image == null) @@ -268,45 +211,8 @@ namespace RedBookPlayer #region Seeking - /// - /// Try to move to the next track, wrapping around if necessary - /// - public void NextTrack() - { - if(_image == null) - return; - - CurrentTrackNumber++; - LoadTrack(CurrentTrackNumber); - } - - /// - /// Try to move to the previous track, wrapping around if necessary - /// - public void PreviousTrack() - { - if(_image == null) - return; - - if(CurrentSector < (ulong)_image.Tracks[CurrentTrackNumber].Indexes[1] + 75) - { - if(App.Settings.AllowSkipHiddenTrack && CurrentTrackNumber == 0 && CurrentSector >= 75) - CurrentSector = 0; - else - CurrentTrackNumber--; - } - else - CurrentTrackNumber--; - - LoadTrack(CurrentTrackNumber); - } - - /// - /// Try to move to the next track index - /// - /// True if index changes can trigger a track change, false otherwise - /// True if the track was changed, false otherwise - public bool NextIndex(bool changeTrack) + /// + public override bool NextIndex(bool changeTrack) { if(_image == null) return false; @@ -328,12 +234,8 @@ namespace RedBookPlayer return false; } - /// - /// Try to move to the previous track index - /// - /// True if index changes can trigger a track change, false otherwise - /// True if the track was changed, false otherwise - public bool PreviousIndex(bool changeTrack) + /// + public override bool PreviousIndex(bool changeTrack) { if(_image == null) return false; @@ -355,59 +257,19 @@ namespace RedBookPlayer return false; } - /// - /// Fast-forward playback by 75 sectors, if possible - /// - public void FastForward() - { - if(_image == null) - return; - - CurrentSector = Math.Min(_image.Info.Sectors - 1, CurrentSector + 75); - } - - /// - /// Rewind playback by 75 sectors, if possible - /// - public void Rewind() - { - if(_image == null) - return; - - if(CurrentSector >= 75) - CurrentSector -= 75; - } - - /// - /// Toggle de-emphasis processing - /// - /// True to apply de-emphasis, false otherwise - public void ToggleDeEmphasis(bool enable) => ApplyDeEmphasis = enable; - #endregion #region Helpers - /// - /// Load the first valid track in the image - /// - public void LoadFirstTrack() + /// + public override void LoadFirstTrack() { CurrentTrackNumber = 0; LoadTrack(CurrentTrackNumber); } - /// - /// Read sector data from the base image starting from the current sector - /// - /// Current number of sectors to read - /// Byte array representing the read sectors, if possible - public byte[] ReadSectors(uint sectorsToRead) => _image.ReadSectors(CurrentSector, sectorsToRead); - - /// - /// Set the total indexes from the current track - /// - public void SetTotalIndexes() + /// + public override void SetTotalIndexes() { if(_image == null) return; @@ -415,6 +277,20 @@ namespace RedBookPlayer TotalIndexes = _image.Tracks[CurrentTrackNumber].Indexes.Keys.Max(); } + /// + protected override void LoadTrack(int track) + { + if(_image == null) + return; + + if(track < 0 || track >= _image.Tracks.Count) + 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]); + } + /// /// Generate a CDFullTOC object from the current image /// @@ -639,23 +515,6 @@ namespace RedBookPlayer return true; } - /// - /// Load the desired track, if possible - /// - /// Track number to load - private void LoadTrack(int track) - { - if(_image == null) - return; - - if(track < 0 || track >= _image.Tracks.Count) - 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]); - } - /// /// Set default track flags for the current track /// diff --git a/RedBookPlayer/Discs/OpticalDisc.cs b/RedBookPlayer/Discs/OpticalDisc.cs new file mode 100644 index 0000000..817c626 --- /dev/null +++ b/RedBookPlayer/Discs/OpticalDisc.cs @@ -0,0 +1,166 @@ +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; + +namespace RedBookPlayer.Discs +{ + public abstract class OpticalDisc + { + #region Public Fields + + /// + /// Indicate if the disc is ready to be used + /// + public bool Initialized { get; protected set; } = false; + + /// + /// Current track number + /// + public abstract int CurrentTrackNumber { get; protected set; } + + /// + /// Current track index + /// + public abstract ushort CurrentTrackIndex { get; protected set; } + + /// + /// Current sector number + /// + public abstract ulong CurrentSector { get; set; } + + /// + /// Represents the sector starting the section + /// + public ulong SectionStartSector { get; protected set; } + + /// + /// Number of bytes per sector for the current track + /// + public int BytesPerSector => _image.Tracks[CurrentTrackNumber].TrackBytesPerSector; + + /// + /// Represents the track type + /// + public TrackType TrackType { get; protected set; } + + /// + /// Represents the total tracks on the disc + /// + public int TotalTracks { get; protected set; } = 0; + + /// + /// Represents the total indices on the disc + /// + public int TotalIndexes { get; protected set; } = 0; + + /// + /// Total sectors in the image + /// + public ulong TotalSectors => _image.Info.Sectors; + + /// + /// Represents the time adjustment offset for the disc + /// + public ulong TimeOffset { get; protected set; } = 0; + + /// + /// Represents the total playing time for the disc + /// + public ulong TotalTime { get; protected set; } = 0; + + #endregion + + #region Protected State Variables + + /// + /// Currently loaded disc image + /// + protected IOpticalMediaImage _image; + + #endregion + + /// + /// Initialize the disc with a given image + /// + /// Aaruformat image to load + /// True if playback should begin immediately, false otherwise + public abstract void Init(IOpticalMediaImage image, bool autoPlay = false); + + #region Seeking + + /// + /// Try to move to the next track, wrapping around if necessary + /// + public void NextTrack() + { + if(_image == null) + return; + + CurrentTrackNumber++; + LoadTrack(CurrentTrackNumber); + } + + /// + /// Try to move to the previous track, wrapping around if necessary + /// + public void PreviousTrack() + { + if(_image == null) + return; + + if(CurrentSector < (ulong)_image.Tracks[CurrentTrackNumber].Indexes[1] + 75) + { + if(App.Settings.AllowSkipHiddenTrack && CurrentTrackNumber == 0 && CurrentSector >= 75) + CurrentSector = 0; + else + CurrentTrackNumber--; + } + else + CurrentTrackNumber--; + + LoadTrack(CurrentTrackNumber); + } + + /// + /// Try to move to the next track index + /// + /// True if index changes can trigger a track change, false otherwise + /// True if the track was changed, false otherwise + public abstract bool NextIndex(bool changeTrack); + + /// + /// Try to move to the previous track index + /// + /// True if index changes can trigger a track change, false otherwise + /// True if the track was changed, false otherwise + public abstract bool PreviousIndex(bool changeTrack); + + #endregion + + #region Helpers + + /// + /// Load the first valid track in the image + /// + public abstract void LoadFirstTrack(); + + /// + /// Read sector data from the base image starting from the current sector + /// + /// Current number of sectors to read + /// Byte array representing the read sectors, if possible + public byte[] ReadSectors(uint sectorsToRead) => _image.ReadSectors(CurrentSector, sectorsToRead); + + /// + /// Set the total indexes from the current track + /// + public abstract void SetTotalIndexes(); + + /// + /// Load the desired track, if possible + /// + /// Track number to load + protected abstract void LoadTrack(int track); + + #endregion + } +} diff --git a/RedBookPlayer/Discs/OpticalDiscFactory.cs b/RedBookPlayer/Discs/OpticalDiscFactory.cs new file mode 100644 index 0000000..99d9257 --- /dev/null +++ b/RedBookPlayer/Discs/OpticalDiscFactory.cs @@ -0,0 +1,80 @@ +using Aaru.CommonTypes.Interfaces; +using Aaru.CommonTypes.Metadata; + +namespace RedBookPlayer.Discs +{ + public static class OpticalDiscFactory + { + /// + /// Generate an OpticalDisc from an input IOpticalMediaImage + /// + /// IOpticalMediaImage to create from + /// True if the image should be playable immediately, false otherwise + /// Instantiated OpticalDisc, if possible + public static OpticalDisc GenerateFromImage(IOpticalMediaImage image, bool autoPlay) + { + // If the image is not usable, we don't do anything + if(!IsUsableImage(image)) + return null; + + // Create the output object + OpticalDisc opticalDisc; + + // Create the proper disc type + switch(GetMediaType(image)) + { + case "Compact Disc": + case "GD": + opticalDisc = new CompactDisc(); + break; + default: + opticalDisc = null; + break; + } + + // Null image means we don't do anything + if(opticalDisc == null) + return opticalDisc; + + // Instantiate the disc and return + opticalDisc.Init(image, autoPlay); + return opticalDisc; + } + + /// + /// Gets the human-readable media type from an image + /// + /// Media image to check + /// Type from the image, empty string on error + /// TODO: Can we be more granular with sub types? + private static string GetMediaType(IOpticalMediaImage image) + { + // Null image means we don't do anything + if(image == null) + return string.Empty; + + (string type, string _) = MediaType.MediaTypeToString(image.Info.MediaType); + return type; + } + + /// + /// Indicates if the image is considered "usable" or not + /// + /// Aaruformat image file + /// True if the image is playble, false otherwise + private static bool IsUsableImage(IOpticalMediaImage image) + { + // Invalid images can't be used + if(image == null) + return false; + + // Determine based on media type + return GetMediaType(image) switch + { + "Compact Disc" => true, + "GD" => true, // Requires TOC generation + _ => false, + }; + } + } +} diff --git a/RedBookPlayer/Player.cs b/RedBookPlayer/Player.cs index 6ef92d4..a471d79 100644 --- a/RedBookPlayer/Player.cs +++ b/RedBookPlayer/Player.cs @@ -1,9 +1,14 @@ using System; +using System.IO; using System.Linq; using System.Threading.Tasks; +using Aaru.CommonTypes.Enums; +using Aaru.DiscImages; +using Aaru.Filters; using CSCore.SoundOut; using NWaves.Audio; using NWaves.Filters.BiQuad; +using RedBookPlayer.Discs; namespace RedBookPlayer { @@ -16,6 +21,11 @@ namespace RedBookPlayer /// public bool Initialized { get; private set; } = false; + /// + /// Indicates if de-emphasis should be applied + /// + public bool ApplyDeEmphasis { get; private set; } = false; + /// /// Indicate if the disc is playing /// @@ -25,16 +35,16 @@ namespace RedBookPlayer #region Private State Variables + /// + /// OpticalDisc object + /// + private OpticalDisc _opticalDisc; + /// /// Current position in the sector /// private int _currentSectorReadPosition = 0; - /// - /// PlaybableDisc object - /// - private PlayableDisc _playableDisc; - /// /// Data provider for sound output /// @@ -63,20 +73,47 @@ namespace RedBookPlayer #endregion /// - /// Initialize the player with a given image + /// Initialize the player with a given image path /// - /// Initialized disc image + /// Path to the disc image /// True if playback should begin immediately, false otherwise - public void Init(PlayableDisc disc, bool autoPlay = false) + public void Init(string path, bool autoPlay = false) { - // If the disc is not initalized, we can't do anything - if(!disc.Initialized) + // Reset the internal state for initialization + Initialized = false; + ApplyDeEmphasis = 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, App.Settings.AutoPlay); + } + catch + { + // All errors mean an invalid image in some way + return; + } + + // If we have an unusable disc, just return + if(_opticalDisc == null || !_opticalDisc.Initialized) return; - // Set the internal reference to the disc - _playableDisc = disc; + // Enable de-emphasis for CDs, if necessary + if(_opticalDisc is CompactDisc compactDisc) + ApplyDeEmphasis = compactDisc.TrackHasEmphasis; - // Setup the de-emphasis filters + // Setup de-emphasis filters SetupFilters(); // Setup the audio output @@ -111,27 +148,27 @@ namespace RedBookPlayer do { // Attempt to read 2 more sectors than requested - sectorsToRead = ((ulong)count / 2352) + 2; + sectorsToRead = ((ulong)(count / _opticalDisc.BytesPerSector)) + 2; zeroSectorsAmount = 0; // Avoid overreads by padding with 0-byte data at the end - if(_playableDisc.CurrentSector + sectorsToRead > _playableDisc.TotalSectors) + if(_opticalDisc.CurrentSector + sectorsToRead > _opticalDisc.TotalSectors) { ulong oldSectorsToRead = sectorsToRead; - sectorsToRead = _playableDisc.TotalSectors - _playableDisc.CurrentSector; + sectorsToRead = _opticalDisc.TotalSectors - _opticalDisc.CurrentSector; zeroSectorsAmount = oldSectorsToRead - sectorsToRead; } // TODO: Figure out when this value could be negative if(sectorsToRead <= 0) { - _playableDisc.LoadFirstTrack(); + _opticalDisc.LoadFirstTrack(); _currentSectorReadPosition = 0; } } while(sectorsToRead <= 0); // Create padding data for overreads - byte[] zeroSectors = new byte[zeroSectorsAmount * 2352]; + byte[] zeroSectors = new byte[(int)zeroSectorsAmount * _opticalDisc.BytesPerSector]; byte[] audioData; // Attempt to read the required number of sectors @@ -141,12 +178,12 @@ namespace RedBookPlayer { try { - return _playableDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray(); + return _opticalDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray(); } catch(ArgumentOutOfRangeException) { - _playableDisc.LoadFirstTrack(); - return _playableDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray(); + _opticalDisc.LoadFirstTrack(); + return _opticalDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray(); } } }); @@ -167,7 +204,7 @@ namespace RedBookPlayer Array.Copy(audioData, _currentSectorReadPosition, audioDataSegment, 0, Math.Min(count, audioData.Length - _currentSectorReadPosition)); // Apply de-emphasis filtering, only if enabled - if(_playableDisc.ApplyDeEmphasis) + if(ApplyDeEmphasis) { float[][] floatAudioData = new float[2][]; floatAudioData[0] = new float[audioDataSegment.Length / 4]; @@ -188,10 +225,10 @@ namespace RedBookPlayer // Set the read position in the sector for easier access _currentSectorReadPosition += count; - if(_currentSectorReadPosition >= 2352) + if(_currentSectorReadPosition >= _opticalDisc.BytesPerSector) { - _playableDisc.CurrentSector += (ulong)_currentSectorReadPosition / 2352; - _currentSectorReadPosition %= 2352; + _opticalDisc.CurrentSector += (ulong)(_currentSectorReadPosition / _opticalDisc.BytesPerSector); + _currentSectorReadPosition %= _opticalDisc.BytesPerSector; } return count; @@ -200,26 +237,23 @@ namespace RedBookPlayer #region Playback /// - /// Start audio playback + /// Toggle audio playback /// - public void Play() + /// True to start playback, false to pause + public void TogglePlayPause(bool start) { - if(!_playableDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; - _soundOut.Play(); - _playableDisc.SetTotalIndexes(); - } - - /// - /// Pause the current audio playback - /// - public void Pause() - { - if(!_playableDisc.Initialized) - return; - - _soundOut.Stop(); + if(start) + { + _soundOut.Play(); + _opticalDisc.SetTotalIndexes(); + } + else + { + _soundOut.Stop(); + } } /// @@ -227,17 +261,185 @@ namespace RedBookPlayer /// public void Stop() { - if(!_playableDisc.Initialized) + if(_opticalDisc == null || !_opticalDisc.Initialized) return; _soundOut.Stop(); - _playableDisc.LoadFirstTrack(); + _opticalDisc.LoadFirstTrack(); + } + + /// + /// Move to the next playable track + /// + public void NextTrack() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + bool wasPlaying = Playing; + if(wasPlaying) TogglePlayPause(false); + + _opticalDisc.NextTrack(); + if(_opticalDisc is CompactDisc compactDisc) + ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + + if(wasPlaying) TogglePlayPause(true); + } + + /// + /// Move to the previous playable track + /// + public void PreviousTrack() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + bool wasPlaying = Playing; + if(wasPlaying) TogglePlayPause(false); + + _opticalDisc.PreviousTrack(); + if(_opticalDisc is CompactDisc compactDisc) + ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + + if(wasPlaying) TogglePlayPause(true); + } + + /// + /// Move to the next index + /// + /// True if index changes can trigger a track change, false otherwise + public void NextIndex(bool changeTrack) + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + bool wasPlaying = Playing; + if(wasPlaying) TogglePlayPause(false); + + _opticalDisc.NextIndex(changeTrack); + if(_opticalDisc is CompactDisc compactDisc) + ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + + if(wasPlaying) TogglePlayPause(true); + } + + /// + /// Move to the previous index + /// + /// True if index changes can trigger a track change, false otherwise + public void PreviousIndex(bool changeTrack) + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + bool wasPlaying = Playing; + if(wasPlaying) TogglePlayPause(false); + + _opticalDisc.PreviousIndex(changeTrack); + if(_opticalDisc is CompactDisc compactDisc) + ApplyDeEmphasis = compactDisc.TrackHasEmphasis; + + if(wasPlaying) TogglePlayPause(true); + } + + /// + /// Fast-forward playback by 75 sectors, if possible + /// + public void FastForward() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + _opticalDisc.CurrentSector = Math.Min(_opticalDisc.TotalSectors, _opticalDisc.CurrentSector + 75); + } + + /// + /// Rewind playback by 75 sectors, if possible + /// + public void Rewind() + { + if(_opticalDisc == null || !_opticalDisc.Initialized) + return; + + 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 = _opticalDisc.CurrentSector; + if(_opticalDisc.SectionStartSector != 0) + sectorTime -= _opticalDisc.SectionStartSector; + else + sectorTime += _opticalDisc.TimeOffset; + + 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))); + } + + /// + /// Toggle de-emphasis processing + /// + /// True to apply de-emphasis, false otherwise + public void ToggleDeEmphasis(bool enable) => ApplyDeEmphasis = enable; + + /// + /// Update the data context for the frontend + /// + /// Data context to be updated + public void UpdateDataContext(PlayerViewModel dataContext) + { + if(!Initialized || dataContext == null) + return; + + dataContext.HiddenTrack = _opticalDisc.TimeOffset > 150; + dataContext.ApplyDeEmphasis = ApplyDeEmphasis; + + 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; + } + } + /// /// Sets or resets the de-emphasis filters /// diff --git a/RedBookPlayer/PlayerView.xaml b/RedBookPlayer/PlayerView.xaml index 2213eba..ad79b76 100644 --- a/RedBookPlayer/PlayerView.xaml +++ b/RedBookPlayer/PlayerView.xaml @@ -86,8 +86,8 @@ - AUDIO - AUDIO + AUDIO + AUDIO DATA DATA EMPHASIS diff --git a/RedBookPlayer/PlayerView.xaml.cs b/RedBookPlayer/PlayerView.xaml.cs index 72dc4b3..2f1a092 100644 --- a/RedBookPlayer/PlayerView.xaml.cs +++ b/RedBookPlayer/PlayerView.xaml.cs @@ -4,10 +4,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Timers; -using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Interfaces; -using Aaru.DiscImages; -using Aaru.Filters; using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; @@ -25,11 +21,6 @@ namespace RedBookPlayer /// public static Player Player = new Player(); - /// - /// Disc representing the loaded image - /// - public static PlayableDisc PlayableDisc = new PlayableDisc(); - /// /// Set of images representing the digits for the UI /// @@ -56,7 +47,7 @@ namespace RedBookPlayer public async Task GetPath() { var dialog = new OpenFileDialog { AllowMultiple = false }; - List knownExtensions = new AaruFormat().KnownExtensions.ToList(); + List knownExtensions = new Aaru.DiscImages.AaruFormat().KnownExtensions.ToList(); dialog.Filters.Add(new FileDialogFilter() { Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", @@ -66,43 +57,6 @@ namespace RedBookPlayer return (await dialog.ShowAsync((Window)Parent.Parent))?.FirstOrDefault(); } - /// - /// Generate the digit string to be interpreted by the UI - /// - /// String representing the digits for the player - private string GenerateDigitString() - { - // If the disc or player aren't initialized, return all '-' characters - if (!PlayableDisc.Initialized) - return string.Empty.PadLeft(20, '-'); - - // Otherwise, take the current time into account - ulong sectorTime = PlayableDisc.CurrentSector; - if (PlayableDisc.SectionStartSector != 0) - sectorTime -= PlayableDisc.SectionStartSector; - else - sectorTime += PlayableDisc.TimeOffset; - - int[] numbers = new int[] - { - PlayableDisc.CurrentTrackNumber + 1, - PlayableDisc.CurrentTrackIndex, - - (int)(sectorTime / (75 * 60)), - (int)(sectorTime / 75 % 60), - (int)(sectorTime % 75), - - PlayableDisc.TotalTracks, - PlayableDisc.TotalIndexes, - - (int)(PlayableDisc.TotalTime / (75 * 60)), - (int)(PlayableDisc.TotalTime / 75 % 60), - (int)(PlayableDisc.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 /// @@ -195,28 +149,6 @@ namespace RedBookPlayer _updateTimer.Start(); } - /// - /// Indicates if the image is considered "playable" or not - /// - /// Aaruformat image file - /// True if the image is playble, false otherwise - private bool IsPlayableImage(IOpticalMediaImage image) - { - // Invalid images can't be played - if (image == null) - return false; - - // Determine based on media type - // TODO: Can we be more granular with sub types? - (string type, string _) = Aaru.CommonTypes.Metadata.MediaType.MediaTypeToString(image.Info.MediaType); - return type switch - { - "Compact Disc" => true, - "GD" => true, // Requires TOC generation - _ => false, - }; - } - /// /// Load an image from the path /// @@ -225,24 +157,8 @@ namespace RedBookPlayer { bool result = await Task.Run(() => { - var image = new AaruFormat(); - var filter = new ZZZNoFilter(); - filter.Open(path); - image.Open(filter); - - if(IsPlayableImage(image)) - { - PlayableDisc.Init(image, App.Settings.AutoPlay); - if(PlayableDisc.Initialized) - { - Player.Init(PlayableDisc, App.Settings.AutoPlay); - return true; - } - - return false; - } - else - return false; + Player.Init(path, App.Settings.AutoPlay); + return Player.Initialized; }); if(result) @@ -261,24 +177,14 @@ namespace RedBookPlayer { Dispatcher.UIThread.InvokeAsync(() => { - string digitString = GenerateDigitString(); + string digitString = Player.GenerateDigitString(); for (int i = 0; i < _digits.Length; i++) { if (_digits[i] != null) _digits[i].Source = GetBitmap(digitString[i]); } - if (Player.Initialized) - { - PlayerViewModel dataContext = (PlayerViewModel)DataContext; - dataContext.HiddenTrack = PlayableDisc.TimeOffset > 150; - dataContext.ApplyDeEmphasis = PlayableDisc.ApplyDeEmphasis; - dataContext.TrackHasEmphasis = PlayableDisc.TrackHasEmphasis; - dataContext.CopyAllowed = PlayableDisc.CopyAllowed; - dataContext.QuadChannel = PlayableDisc.QuadChannel; - dataContext.IsAudioTrack = PlayableDisc.TrackType == TrackType.Audio; - dataContext.IsDataTrack = PlayableDisc.TrackType != TrackType.Audio; - } + Player.UpdateDataContext(DataContext as PlayerViewModel); }); } @@ -295,51 +201,27 @@ namespace RedBookPlayer LoadImage(path); } - public void PlayButton_Click(object sender, RoutedEventArgs e) => Player.Play(); + public void PlayButton_Click(object sender, RoutedEventArgs e) => Player.TogglePlayPause(true); - public void PauseButton_Click(object sender, RoutedEventArgs e) => Player.Pause(); + public void PauseButton_Click(object sender, RoutedEventArgs e) => Player.TogglePlayPause(false); public void StopButton_Click(object sender, RoutedEventArgs e) => Player.Stop(); - public void NextTrackButton_Click(object sender, RoutedEventArgs e) - { - bool wasPlaying = Player.Playing; - if(wasPlaying) Player.Pause(); - PlayableDisc.NextTrack(); - if(wasPlaying) Player.Play(); - } + public void NextTrackButton_Click(object sender, RoutedEventArgs e) => Player.NextTrack(); - public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) - { - bool wasPlaying = Player.Playing; - if(wasPlaying) Player.Pause(); - PlayableDisc.PreviousTrack(); - if(wasPlaying) Player.Play(); - } + public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => Player.PreviousTrack(); - public void NextIndexButton_Click(object sender, RoutedEventArgs e) - { - bool wasPlaying = Player.Playing; - if(wasPlaying) Player.Pause(); - PlayableDisc.NextIndex(App.Settings.IndexButtonChangeTrack); - if(wasPlaying) Player.Play(); - } + public void NextIndexButton_Click(object sender, RoutedEventArgs e) => Player.NextIndex(App.Settings.IndexButtonChangeTrack); - public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) - { - bool wasPlaying = Player.Playing; - if(wasPlaying) Player.Pause(); - PlayableDisc.PreviousIndex(App.Settings.IndexButtonChangeTrack); - if(wasPlaying) Player.Play(); - } + public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => Player.PreviousIndex(App.Settings.IndexButtonChangeTrack); - public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.FastForward(); + public void FastForwardButton_Click(object sender, RoutedEventArgs e) => Player.FastForward(); - public void RewindButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.Rewind(); + public void RewindButton_Click(object sender, RoutedEventArgs e) => Player.Rewind(); - public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.ToggleDeEmphasis(true); + public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.ToggleDeEmphasis(true); - public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.ToggleDeEmphasis(false); + public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.ToggleDeEmphasis(false); #endregion } diff --git a/RedBookPlayer/PlayerViewModel.cs b/RedBookPlayer/PlayerViewModel.cs index 17e058f..f87664b 100644 --- a/RedBookPlayer/PlayerViewModel.cs +++ b/RedBookPlayer/PlayerViewModel.cs @@ -11,6 +11,27 @@ namespace RedBookPlayer set => this.RaiseAndSetIfChanged(ref _applyDeEmphasis, value); } + 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 { @@ -24,33 +45,5 @@ namespace RedBookPlayer get => _hiddenTrack; set => this.RaiseAndSetIfChanged(ref _hiddenTrack, value); } - - private bool _copyAllowed; - public bool CopyAllowed - { - get => _copyAllowed; - set => this.RaiseAndSetIfChanged(ref _copyAllowed, value); - } - - private bool _quadChannel; - public bool QuadChannel - { - get => _quadChannel; - set => this.RaiseAndSetIfChanged(ref _quadChannel, value); - } - - private bool _isAudioTrack; - public bool IsAudioTrack - { - get => _isAudioTrack; - set => this.RaiseAndSetIfChanged(ref _isAudioTrack, value); - } - - private bool _isDataTrack; - public bool IsDataTrack - { - get => _isDataTrack; - set => this.RaiseAndSetIfChanged(ref _isDataTrack, value); - } } } \ No newline at end of file