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