diff --git a/.gitmodules b/.gitmodules
index 361306b..38b3a5b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -3,4 +3,4 @@
url = https://github.com/aaru-dps/Aaru.git
[submodule "cscore"]
path = cscore
- url = https://github.com/deagahelio/cscore.git
+ url = https://github.com/filoe/cscore.git
diff --git a/Aaru b/Aaru
index 65739fa..b41b167 160000
--- a/Aaru
+++ b/Aaru
@@ -1 +1 @@
-Subproject commit 65739fa9660fd4637204288898b80779484b359f
+Subproject commit b41b1679117927df188b2f14bfaa5c2190af05d1
diff --git a/RedBookPlayer/PlayableDisc.cs b/RedBookPlayer/PlayableDisc.cs
new file mode 100644
index 0000000..f030fa9
--- /dev/null
+++ b/RedBookPlayer/PlayableDisc.cs
@@ -0,0 +1,699 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
+using Aaru.CommonTypes.Structs;
+using Aaru.Decoders.CD;
+using Aaru.Helpers;
+using static Aaru.Decoders.CD.FullTOC;
+
+namespace RedBookPlayer
+{
+ public class PlayableDisc
+ {
+ #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
+ {
+ get => _currentTrackNumber;
+ set
+ {
+ // Unset image means we can't do anything
+ if(_image == null)
+ return;
+
+ // Cache the value and the current track number
+ int cachedValue = value;
+ int cachedTrackNumber = _currentTrackNumber;
+
+ // Check if we're incrementing or decrementing the track
+ bool increment = cachedValue >= _currentTrackNumber;
+
+ do
+ {
+ // Ensure that the value is valid, wrapping around if necessary
+ if(cachedValue >= _image.Tracks.Count)
+ cachedValue = 0;
+ else if(cachedValue < 0)
+ cachedValue = _image.Tracks.Count - 1;
+
+ _currentTrackNumber = cachedValue;
+
+ // Cache the current track for easy access
+ Track track = _image.Tracks[_currentTrackNumber];
+
+ // Set track flags from subchannel data, if possible
+ SetTrackFlags(track);
+
+ ApplyDeEmphasis = TrackHasEmphasis;
+
+ TotalIndexes = track.Indexes.Keys.Max();
+ CurrentTrackIndex = track.Indexes.Keys.Min();
+
+ // If the track is playable, just return
+ if(TrackType == TrackType.Audio || App.Settings.PlayDataTracks)
+ return;
+
+ // If we're not playing the track, skip
+ if(increment)
+ cachedValue++;
+ else
+ cachedValue--;
+ }
+ while(cachedValue != cachedTrackNumber);
+ }
+ }
+
+ ///
+ /// Current track index
+ ///
+ public ushort CurrentTrackIndex
+ {
+ get => _currentTrackIndex;
+ set
+ {
+ // Unset image means we can't do anything
+ if(_image == null)
+ return;
+
+ // Cache the current track for easy access
+ Track track = _image.Tracks[CurrentTrackNumber];
+
+ // Ensure that the value is valid, wrapping around if necessary
+ if(value > track.Indexes.Keys.Max())
+ _currentTrackIndex = 0;
+ else if(value < 0)
+ _currentTrackIndex = track.Indexes.Keys.Max();
+ else
+ _currentTrackIndex = value;
+
+ // Set new index-specific data
+ SectionStartSector = (ulong)track.Indexes[CurrentTrackIndex];
+ TotalTime = track.TrackEndSector - track.TrackStartSector;
+ }
+ }
+
+ ///
+ /// Current sector number
+ ///
+ public ulong CurrentSector
+ {
+ get => _currentSector;
+ set
+ {
+ // Unset image means we can't do anything
+ if(_image == null)
+ return;
+
+ // Cache the current track for easy access
+ Track track = _image.Tracks[CurrentTrackNumber];
+
+ _currentSector = value;
+
+ if((CurrentTrackNumber < _image.Tracks.Count - 1 && CurrentSector >= _image.Tracks[CurrentTrackNumber + 1].TrackStartSector)
+ || (CurrentTrackNumber > 0 && CurrentSector < track.TrackStartSector))
+ {
+ foreach(Track trackData in _image.Tracks.ToArray().Reverse())
+ {
+ if(CurrentSector >= trackData.TrackStartSector)
+ {
+ CurrentTrackNumber = (int)trackData.TrackSequence - 1;
+ break;
+ }
+ }
+ }
+
+ foreach((ushort key, int i) in track.Indexes.Reverse())
+ {
+ if((int)CurrentSector >= i)
+ {
+ CurrentTrackIndex = key;
+ return;
+ }
+ }
+
+ CurrentTrackIndex = 0;
+ }
+ }
+
+ ///
+ /// Represents the PRE flag
+ ///
+ public bool TrackHasEmphasis { get; private set; } = false;
+
+ ///
+ /// Indicates if de-emphasis should be applied
+ ///
+ public bool ApplyDeEmphasis { get; private set; } = false;
+
+ ///
+ /// Represents the DCP flag
+ ///
+ public bool CopyAllowed { get; private set; } = false;
+
+ ///
+ /// Represents the track type
+ ///
+ 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;
+
+ #endregion
+
+ #region Private State Variables
+
+ ///
+ /// Currently loaded disc image
+ ///
+ private IOpticalMediaImage _image;
+
+ ///
+ /// Current track number
+ ///
+ private int _currentTrackNumber = 0;
+
+ ///
+ /// Current track index
+ ///
+ private ushort _currentTrackIndex = 0;
+
+ ///
+ /// Current sector number
+ ///
+ private ulong _currentSector = 0;
+
+ ///
+ /// Current disc table of contents
+ ///
+ private CDFullTOC _toc;
+
+ #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)
+ {
+ // If the image is null, we can't do anything
+ if(image == null)
+ return;
+
+ // Set the current disc image
+ _image = image;
+
+ // Attempt to load the TOC
+ if(!LoadTOC())
+ return;
+
+ // Load the first track
+ LoadFirstTrack();
+
+ // Reset total indexes if not in autoplay
+ if(!autoPlay)
+ TotalIndexes = 0;
+
+ // Set the internal disc state
+ TotalTracks = _image.Tracks.Count;
+ TrackDataDescriptor firstTrack = _toc.TrackDescriptors.First(d => d.ADR == 1 && d.POINT == 1);
+ TimeOffset = (ulong)((firstTrack.PMIN * 60 * 75) + (firstTrack.PSEC * 75) + firstTrack.PFRAME);
+ TotalTime = TimeOffset + _image.Tracks.Last().TrackEndSector;
+
+ // Mark the disc as ready
+ Initialized = true;
+ }
+
+ #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)
+ {
+ if(_image == null)
+ return false;
+
+ if(CurrentTrackIndex + 1 > _image.Tracks[CurrentTrackNumber].Indexes.Keys.Max())
+ {
+ if(changeTrack)
+ {
+ NextTrack();
+ CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes.Values.Min();
+ return true;
+ }
+ }
+ else
+ {
+ CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes[++CurrentTrackIndex];
+ }
+
+ 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)
+ {
+ if(_image == null)
+ return false;
+
+ if(CurrentTrackIndex - 1 < _image.Tracks[CurrentTrackNumber].Indexes.Keys.Min())
+ {
+ if(changeTrack)
+ {
+ PreviousTrack();
+ CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes.Values.Max();
+ return true;
+ }
+ }
+ else
+ {
+ CurrentSector = (ulong)_image.Tracks[CurrentTrackNumber].Indexes[--CurrentTrackIndex];
+ }
+
+ 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()
+ {
+ 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()
+ {
+ if(_image == null)
+ return;
+
+ TotalIndexes = _image.Tracks[CurrentTrackNumber].Indexes.Keys.Max();
+ }
+
+ ///
+ /// Generate a CDFullTOC object from the current image
+ ///
+ /// CDFullTOC object, if possible
+ /// Copied from
+ private bool GenerateTOC()
+ {
+ // Invalid image means we can't generate anything
+ if(_image == null)
+ return false;
+
+ _toc = new CDFullTOC();
+ Dictionary _trackFlags = new Dictionary();
+ Dictionary sessionEndingTrack = new Dictionary();
+ _toc.FirstCompleteSession = byte.MaxValue;
+ _toc.LastCompleteSession = byte.MinValue;
+ List trackDescriptors = new List();
+ byte currentTrack = 0;
+
+ foreach(Track track in _image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
+ {
+ byte[] trackFlags = _image.ReadSectorTag(track.TrackStartSector + 1, SectorTagType.CdTrackFlags);
+ if(trackFlags != null)
+ _trackFlags.Add((byte)track.TrackStartSector, trackFlags[0]);
+ }
+
+ foreach(Track track in _image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
+ {
+ if(track.TrackSession < _toc.FirstCompleteSession)
+ _toc.FirstCompleteSession = (byte)track.TrackSession;
+
+ if(track.TrackSession <= _toc.LastCompleteSession)
+ {
+ currentTrack = (byte)track.TrackSequence;
+
+ continue;
+ }
+
+ if(_toc.LastCompleteSession > 0)
+ sessionEndingTrack.Add(_toc.LastCompleteSession, currentTrack);
+
+ _toc.LastCompleteSession = (byte)track.TrackSession;
+ }
+
+ byte currentSession = 0;
+
+ foreach(Track track in _image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
+ {
+ _trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl);
+
+ if(trackControl == 0 &&
+ track.TrackType != Aaru.CommonTypes.Enums.TrackType.Audio)
+ trackControl = (byte)CdFlags.DataTrack;
+
+ // Lead-Out
+ if(track.TrackSession > currentSession &&
+ currentSession != 0)
+ {
+ (byte minute, byte second, byte frame) leadoutAmsf = LbaToMsf(track.TrackStartSector - 150);
+
+ (byte minute, byte second, byte frame) leadoutPmsf =
+ LbaToMsf(_image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last().
+ TrackStartSector);
+
+ // Lead-out
+ trackDescriptors.Add(new TrackDataDescriptor
+ {
+ SessionNumber = currentSession,
+ POINT = 0xB0,
+ ADR = 5,
+ CONTROL = 0,
+ HOUR = 0,
+ Min = leadoutAmsf.minute,
+ Sec = leadoutAmsf.second,
+ Frame = leadoutAmsf.frame,
+ PHOUR = 2,
+ PMIN = leadoutPmsf.minute,
+ PSEC = leadoutPmsf.second,
+ PFRAME = leadoutPmsf.frame
+ });
+
+ // This seems to be constant? It should not exist on CD-ROM but CloneCD creates them anyway
+ // Format seems like ATIP, but ATIP should not be as 0xC0 in TOC...
+ //trackDescriptors.Add(new TrackDataDescriptor
+ //{
+ // SessionNumber = currentSession,
+ // POINT = 0xC0,
+ // ADR = 5,
+ // CONTROL = 0,
+ // Min = 128,
+ // PMIN = 97,
+ // PSEC = 25
+ //});
+ }
+
+ // Lead-in
+ if(track.TrackSession > currentSession)
+ {
+ currentSession = (byte)track.TrackSession;
+ sessionEndingTrack.TryGetValue(currentSession, out byte endingTrackNumber);
+
+ (byte minute, byte second, byte frame) leadinPmsf =
+ LbaToMsf(_image.Tracks.FirstOrDefault(t => t.TrackSequence == endingTrackNumber)?.TrackEndSector ??
+ 0 + 1);
+
+ // Starting track
+ trackDescriptors.Add(new TrackDataDescriptor
+ {
+ SessionNumber = currentSession,
+ POINT = 0xA0,
+ ADR = 1,
+ CONTROL = trackControl,
+ PMIN = (byte)track.TrackSequence
+ });
+
+ // Ending track
+ trackDescriptors.Add(new TrackDataDescriptor
+ {
+ SessionNumber = currentSession,
+ POINT = 0xA1,
+ ADR = 1,
+ CONTROL = trackControl,
+ PMIN = endingTrackNumber
+ });
+
+ // Lead-out start
+ trackDescriptors.Add(new TrackDataDescriptor
+ {
+ SessionNumber = currentSession,
+ POINT = 0xA2,
+ ADR = 1,
+ CONTROL = trackControl,
+ PHOUR = 0,
+ PMIN = leadinPmsf.minute,
+ PSEC = leadinPmsf.second,
+ PFRAME = leadinPmsf.frame
+ });
+ }
+
+ (byte minute, byte second, byte frame) pmsf = LbaToMsf(track.TrackStartSector);
+
+ // Track
+ trackDescriptors.Add(new TrackDataDescriptor
+ {
+ SessionNumber = (byte)track.TrackSession,
+ POINT = (byte)track.TrackSequence,
+ ADR = 1,
+ CONTROL = trackControl,
+ PHOUR = 0,
+ PMIN = pmsf.minute,
+ PSEC = pmsf.second,
+ PFRAME = pmsf.frame
+ });
+ }
+
+ _toc.TrackDescriptors = trackDescriptors.ToArray();
+ return true;
+ }
+
+ ///
+ /// Convert the sector to LBA values
+ ///
+ /// Sector to convert
+ /// LBA values for the sector number
+ /// Copied from
+ private (byte minute, byte second, byte frame) LbaToMsf(ulong sector) =>
+ ((byte)((sector + 150) / 75 / 60), (byte)((sector + 150) / 75 % 60), (byte)((sector + 150) % 75));
+
+ ///
+ /// Load TOC for the current disc image
+ ///
+ /// True if the TOC could be loaded, false otherwise
+ private bool LoadTOC()
+ {
+ if(_image.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC) != true)
+ {
+ // Only generate the TOC if we have it set
+ if(!App.Settings.GenerateMissingTOC)
+ {
+ Console.WriteLine("Full TOC not found");
+ return false;
+ }
+
+ Console.WriteLine("Attempting to generate TOC");
+ if(GenerateTOC())
+ {
+ Console.WriteLine(Prettify(_toc));
+ return true;
+ }
+ else
+ {
+ Console.WriteLine("Full TOC not found or generated");
+ return false;
+ }
+ }
+
+ byte[] tocBytes = _image.ReadDiskTag(MediaTagType.CD_FullTOC);
+ if(tocBytes == null || tocBytes.Length == 0)
+ {
+ Console.WriteLine("Error reading TOC from disc image");
+ return false;
+ }
+
+ if(Swapping.Swap(BitConverter.ToUInt16(tocBytes, 0)) + 2 != tocBytes.Length)
+ {
+ byte[] tmp = new byte[tocBytes.Length + 2];
+ Array.Copy(tocBytes, 0, tmp, 2, tocBytes.Length);
+ tmp[0] = (byte)((tocBytes.Length & 0xFF00) >> 8);
+ tmp[1] = (byte)(tocBytes.Length & 0xFF);
+ tocBytes = tmp;
+ }
+
+ var nullableToc = Decode(tocBytes);
+ if(nullableToc == null)
+ {
+ Console.WriteLine("Error decoding TOC");
+ return false;
+ }
+
+ _toc = nullableToc.Value;
+ Console.WriteLine(Prettify(_toc));
+ 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
+ ///
+ /// Track object to read from
+ private void SetDefaultTrackFlags(Track track)
+ {
+ QuadChannel = false;
+ TrackType = track.TrackType;
+ CopyAllowed = false;
+ TrackHasEmphasis = false;
+ }
+
+ ///
+ /// Set track flags from the current track
+ ///
+ /// Track object to read from
+ private void SetTrackFlags(Track track)
+ {
+ try
+ {
+ // Get the track descriptor from the TOC
+ TrackDataDescriptor descriptor = _toc.TrackDescriptors.First(d => d.POINT == track.TrackSequence);
+
+ // Set the track flags from TOC data
+ byte flags = (byte)(descriptor.CONTROL & 0x0D);
+ TrackHasEmphasis = (flags & (byte)TocControl.TwoChanPreEmph) == (byte)TocControl.TwoChanPreEmph;
+ CopyAllowed = (flags & (byte)TocControl.CopyPermissionMask) == (byte)TocControl.CopyPermissionMask;
+ TrackType = (flags & (byte)TocControl.DataTrack) == (byte)TocControl.DataTrack ? TrackType.Data : TrackType.Audio;
+ QuadChannel = (flags & (byte)TocControl.FourChanNoPreEmph) == (byte)TocControl.FourChanNoPreEmph;
+
+ return;
+ }
+ catch(Exception)
+ {
+ SetDefaultTrackFlags(track);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RedBookPlayer/Player.cs b/RedBookPlayer/Player.cs
index 40f4f09..6ef92d4 100644
--- a/RedBookPlayer/Player.cs
+++ b/RedBookPlayer/Player.cs
@@ -1,15 +1,9 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Aaru.CommonTypes.Enums;
-using Aaru.CommonTypes.Structs;
-using Aaru.DiscImages;
-using Aaru.Helpers;
using CSCore.SoundOut;
using NWaves.Audio;
using NWaves.Filters.BiQuad;
-using static Aaru.Decoders.CD.FullTOC;
namespace RedBookPlayer
{
@@ -23,247 +17,23 @@ namespace RedBookPlayer
public bool Initialized { get; private set; } = false;
///
- /// Currently loaded disc image
+ /// Indicate if the disc is playing
///
- public AaruFormat Image { get; private set; }
-
- ///
- /// Current track number
- ///
- public int CurrentTrack
- {
- get => _currentTrack;
- private set
- {
- // Unset image means we can't do anything
- if(Image == null)
- return;
-
- // If the value is the same, don't do anything
- if(value == _currentTrack)
- return;
-
- // Check if we're incrementing or decrementing the track
- bool increment = value > _currentTrack;
-
- // Ensure that the value is valid, wrapping around if necessary
- if(value >= Image.Tracks.Count)
- _currentTrack = 0;
- else if(value < 0)
- _currentTrack = Image.Tracks.Count - 1;
- else
- _currentTrack = value;
-
- // Cache the current track for easy access
- Track track = Image.Tracks[CurrentTrack];
-
- // Set new track-specific data
- byte[] flagsData = Image.ReadSectorTag(track.TrackSequence, SectorTagType.CdTrackFlags);
- ApplyDeEmphasis = ((CdFlags)flagsData[0]).HasFlag(CdFlags.PreEmphasis);
-
- try
- {
- byte[] subchannel = Image.ReadSectorTag(track.TrackStartSector, SectorTagType.CdSectorSubchannel);
-
- if(!ApplyDeEmphasis)
- ApplyDeEmphasis = (subchannel[3] & 0b01000000) != 0;
-
- CopyAllowed = (subchannel[2] & 0b01000000) != 0;
- TrackType = (subchannel[1] & 0b01000000) != 0 ? Aaru.CommonTypes.Enums.TrackType.Data : Aaru.CommonTypes.Enums.TrackType.Audio;
- }
- catch(ArgumentException)
- {
- TrackType = track.TrackType;
- }
-
- TrackHasEmphasis = ApplyDeEmphasis;
-
- TotalIndexes = track.Indexes.Keys.Max();
- CurrentIndex = track.Indexes.Keys.Min();
-
- // If we're not playing data tracks, skip
- if(!App.Settings.PlayDataTracks && TrackType != Aaru.CommonTypes.Enums.TrackType.Audio)
- {
- if(increment)
- NextTrack();
- else
- PreviousTrack();
- }
- }
- }
-
- ///
- /// Current track index
- ///
- public ushort CurrentIndex
- {
- get => _currentIndex;
- private set
- {
- // Unset image means we can't do anything
- if(Image == null)
- return;
-
- // If the value is the same, don't do anything
- if(value == _currentIndex)
- return;
-
- // Cache the current track for easy access
- Track track = Image.Tracks[CurrentTrack];
-
- // Ensure that the value is valid, wrapping around if necessary
- if(value > track.Indexes.Keys.Max())
- _currentIndex = 0;
- else if(value < 0)
- _currentIndex = track.Indexes.Keys.Max();
- else
- _currentIndex = value;
-
- // Set new index-specific data
- SectionStartSector = (ulong)track.Indexes[CurrentIndex];
- TotalTime = track.TrackEndSector - track.TrackStartSector;
- }
- }
-
- ///
- /// Current sector number
- ///
- public ulong CurrentSector
- {
- get => _currentSector;
- private set
- {
- // Unset image means we can't do anything
- if(Image == null)
- return;
-
- // If the value is the same, don't do anything
- if(value == _currentSector)
- return;
-
- // Cache the current track for easy access
- Track track = Image.Tracks[CurrentTrack];
-
- _currentSector = value;
-
- if((CurrentTrack < Image.Tracks.Count - 1 && CurrentSector >= Image.Tracks[CurrentTrack + 1].TrackStartSector)
- || (CurrentTrack > 0 && CurrentSector < track.TrackStartSector))
- {
- foreach(Track trackData in Image.Tracks.ToArray().Reverse())
- {
- if(CurrentSector >= trackData.TrackStartSector)
- {
- CurrentTrack = (int)trackData.TrackSequence - 1;
- break;
- }
- }
- }
-
- foreach((ushort key, int i) in track.Indexes.Reverse())
- {
- if((int)CurrentSector >= i)
- {
- CurrentIndex = key;
- return;
- }
- }
-
- CurrentIndex = 0;
- }
- }
-
- ///
- /// Represents the pre-emphasis flag
- ///
- public bool TrackHasEmphasis { get; private set; } = false;
-
- ///
- /// Indicates if de-emphasis should be applied
- ///
- public bool ApplyDeEmphasis { get; private set; } = false;
-
- ///
- /// Represents the copy allowed flag
- ///
- public bool CopyAllowed { get; private set; } = false;
-
- ///
- /// Represents the track type
- ///
- public TrackType? TrackType { get; private set; }
-
- ///
- /// 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;
-
- ///
- /// 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;
-
- ///
- /// Represents the current play volume between 0 and 100
- ///
- public int Volume
- {
- get => _volume;
- set
- {
- if(value >= 0 &&
- value <= 100)
- _volume = value;
- }
- }
+ public bool Playing => _soundOut.PlaybackState == PlaybackState.Playing;
#endregion
#region Private State Variables
- ///
- /// Current track number
- ///
- private int _currentTrack = 0;
-
- ///
- /// Current track index
- ///
- private ushort _currentIndex = 0;
-
- ///
- /// Current sector number
- ///
- private ulong _currentSector = 0;
-
///
/// Current position in the sector
///
private int _currentSectorReadPosition = 0;
///
- /// Current play volume between 0 and 100
+ /// PlaybableDisc object
///
- private int _volume = 100;
-
- ///
- /// Current disc table of contents
- ///
- private CDFullTOC _toc;
+ private PlayableDisc _playableDisc;
///
/// Data provider for sound output
@@ -295,20 +65,16 @@ namespace RedBookPlayer
///
/// Initialize the player with a given image
///
- /// Aaruformat image to load for playback
+ /// Initialized disc image
/// True if playback should begin immediately, false otherwise
- public async void Init(AaruFormat image, bool autoPlay = false)
+ public void Init(PlayableDisc disc, bool autoPlay = false)
{
- // If the image is null, we can't do anything
- if(image == null)
+ // If the disc is not initalized, we can't do anything
+ if(!disc.Initialized)
return;
- // Set the current disc image
- Image = image;
-
- // Attempt to load the TOC
- if(!await LoadTOC())
- return;
+ // Set the internal reference to the disc
+ _playableDisc = disc;
// Setup the de-emphasis filters
SetupFilters();
@@ -316,24 +82,9 @@ namespace RedBookPlayer
// Setup the audio output
SetupAudio();
- // Load the first track
- CurrentTrack = 0;
- LoadTrack(0);
-
// Initialize playback, if necessary
if(autoPlay)
_soundOut.Play();
- else
- TotalIndexes = 0;
-
- // Set the internal disc state
- TotalTracks = image.Tracks.Count;
- TrackDataDescriptor firstTrack = _toc.TrackDescriptors.First(d => d.ADR == 1 && d.POINT == 1);
- TimeOffset = (ulong)((firstTrack.PMIN * 60 * 75) + (firstTrack.PSEC * 75) + firstTrack.PFRAME);
- TotalTime = TimeOffset + image.Tracks.Last().TrackEndSector;
-
- // Set the output volume from settings
- Volume = App.Settings.Volume;
// Mark the player as ready
Initialized = true;
@@ -352,7 +103,7 @@ namespace RedBookPlayer
public int ProviderRead(byte[] buffer, int offset, int count)
{
// Set the current volume
- _soundOut.Volume = (float)Volume / 100;
+ _soundOut.Volume = (float)App.Settings.Volume / 100;
// Determine how many sectors we can read
ulong sectorsToRead;
@@ -364,17 +115,17 @@ namespace RedBookPlayer
zeroSectorsAmount = 0;
// Avoid overreads by padding with 0-byte data at the end
- if(CurrentSector + sectorsToRead > Image.Info.Sectors)
+ if(_playableDisc.CurrentSector + sectorsToRead > _playableDisc.TotalSectors)
{
ulong oldSectorsToRead = sectorsToRead;
- sectorsToRead = Image.Info.Sectors - CurrentSector;
+ sectorsToRead = _playableDisc.TotalSectors - _playableDisc.CurrentSector;
zeroSectorsAmount = oldSectorsToRead - sectorsToRead;
}
// TODO: Figure out when this value could be negative
if(sectorsToRead <= 0)
{
- LoadTrack(0);
+ _playableDisc.LoadFirstTrack();
_currentSectorReadPosition = 0;
}
} while(sectorsToRead <= 0);
@@ -390,12 +141,12 @@ namespace RedBookPlayer
{
try
{
- return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray();
+ return _playableDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray();
}
catch(ArgumentOutOfRangeException)
{
- LoadTrack(0);
- return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray();
+ _playableDisc.LoadFirstTrack();
+ return _playableDisc.ReadSectors((uint)sectorsToRead).Concat(zeroSectors).ToArray();
}
}
});
@@ -416,7 +167,7 @@ namespace RedBookPlayer
Array.Copy(audioData, _currentSectorReadPosition, audioDataSegment, 0, Math.Min(count, audioData.Length - _currentSectorReadPosition));
// Apply de-emphasis filtering, only if enabled
- if(ApplyDeEmphasis)
+ if(_playableDisc.ApplyDeEmphasis)
{
float[][] floatAudioData = new float[2][];
floatAudioData[0] = new float[audioDataSegment.Length / 4];
@@ -439,25 +190,25 @@ namespace RedBookPlayer
_currentSectorReadPosition += count;
if(_currentSectorReadPosition >= 2352)
{
- CurrentSector += (ulong)_currentSectorReadPosition / 2352;
+ _playableDisc.CurrentSector += (ulong)_currentSectorReadPosition / 2352;
_currentSectorReadPosition %= 2352;
}
return count;
}
- #region Player Controls
+ #region Playback
///
/// Start audio playback
///
public void Play()
{
- if(Image == null)
+ if(!_playableDisc.Initialized)
return;
_soundOut.Play();
- TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max();
+ _playableDisc.SetTotalIndexes();
}
///
@@ -465,7 +216,7 @@ namespace RedBookPlayer
///
public void Pause()
{
- if(Image == null)
+ if(!_playableDisc.Initialized)
return;
_soundOut.Stop();
@@ -476,371 +227,17 @@ namespace RedBookPlayer
///
public void Stop()
{
- if(Image == null)
+ if(!_playableDisc.Initialized)
return;
_soundOut.Stop();
- LoadTrack(CurrentTrack);
+ _playableDisc.LoadFirstTrack();
}
- ///
- /// Try to move to the next track, wrapping around if necessary
- ///
- public void NextTrack()
- {
- if(Image == null)
- return;
-
- CurrentTrack++;
- LoadTrack(CurrentTrack);
- }
-
- ///
- /// Try to move to the previous track, wrapping around if necessary
- ///
- public void PreviousTrack()
- {
- if(Image == null)
- return;
-
- if(CurrentSector < (ulong)Image.Tracks[CurrentTrack].Indexes[1] + 75)
- {
- if(App.Settings.AllowSkipHiddenTrack && CurrentTrack == 0 && CurrentSector >= 75)
- CurrentSector = 0;
- else
- CurrentTrack--;
- }
-
- LoadTrack(CurrentTrack);
- }
-
- ///
- /// Try to move to the next track index
- ///
- /// True if index changes can trigger a track change, false otherwise
- public void NextIndex(bool changeTrack)
- {
- if(Image == null)
- return;
-
- if(CurrentIndex + 1 > Image.Tracks[CurrentTrack].Indexes.Keys.Max())
- {
- if(changeTrack)
- {
- NextTrack();
- CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Min();
- }
- }
- else
- {
- CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[++CurrentIndex];
- }
- }
-
- ///
- /// Try to move to the previous track index
- ///
- /// True if index changes can trigger a track change, false otherwise
- public void PreviousIndex(bool changeTrack)
- {
- if(Image == null)
- return;
-
- if(CurrentIndex - 1 < Image.Tracks[CurrentTrack].Indexes.Keys.Min())
- {
- if(changeTrack)
- {
- PreviousTrack();
- CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Max();
- }
- }
- else
- {
- CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[--CurrentIndex];
- }
- }
-
- ///
- /// 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
- ///
- /// Generate a CDFullTOC object from the current image
- ///
- /// CDFullTOC object, if possible
- /// Copied from
- private bool GenerateTOC()
- {
- // Invalid image means we can't generate anything
- if(Image == null)
- return false;
-
- _toc = new CDFullTOC();
- Dictionary _trackFlags = new Dictionary();
- Dictionary sessionEndingTrack = new Dictionary();
- _toc.FirstCompleteSession = byte.MaxValue;
- _toc.LastCompleteSession = byte.MinValue;
- List trackDescriptors = new List();
- byte currentTrack = 0;
-
- foreach(Track track in Image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
- {
- byte[] trackFlags = Image.ReadSectorTag(track.TrackStartSector + 1, SectorTagType.CdTrackFlags);
- if(trackFlags != null)
- _trackFlags.Add((byte)track.TrackStartSector, trackFlags[0]);
- }
-
- foreach(Track track in Image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
- {
- if(track.TrackSession < _toc.FirstCompleteSession)
- _toc.FirstCompleteSession = (byte)track.TrackSession;
-
- if(track.TrackSession <= _toc.LastCompleteSession)
- {
- currentTrack = (byte)track.TrackSequence;
-
- continue;
- }
-
- if(_toc.LastCompleteSession > 0)
- sessionEndingTrack.Add(_toc.LastCompleteSession, currentTrack);
-
- _toc.LastCompleteSession = (byte)track.TrackSession;
- }
-
- byte currentSession = 0;
-
- foreach(Track track in Image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence))
- {
- _trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl);
-
- if(trackControl == 0 &&
- track.TrackType != Aaru.CommonTypes.Enums.TrackType.Audio)
- trackControl = (byte)CdFlags.DataTrack;
-
- // Lead-Out
- if(track.TrackSession > currentSession &&
- currentSession != 0)
- {
- (byte minute, byte second, byte frame) leadoutAmsf = LbaToMsf(track.TrackStartSector - 150);
-
- (byte minute, byte second, byte frame) leadoutPmsf =
- LbaToMsf(Image.Tracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last().
- TrackStartSector);
-
- // Lead-out
- trackDescriptors.Add(new TrackDataDescriptor
- {
- SessionNumber = currentSession,
- POINT = 0xB0,
- ADR = 5,
- CONTROL = 0,
- HOUR = 0,
- Min = leadoutAmsf.minute,
- Sec = leadoutAmsf.second,
- Frame = leadoutAmsf.frame,
- PHOUR = 2,
- PMIN = leadoutPmsf.minute,
- PSEC = leadoutPmsf.second,
- PFRAME = leadoutPmsf.frame
- });
-
- // This seems to be constant? It should not exist on CD-ROM but CloneCD creates them anyway
- // Format seems like ATIP, but ATIP should not be as 0xC0 in TOC...
- //trackDescriptors.Add(new TrackDataDescriptor
- //{
- // SessionNumber = currentSession,
- // POINT = 0xC0,
- // ADR = 5,
- // CONTROL = 0,
- // Min = 128,
- // PMIN = 97,
- // PSEC = 25
- //});
- }
-
- // Lead-in
- if(track.TrackSession > currentSession)
- {
- currentSession = (byte)track.TrackSession;
- sessionEndingTrack.TryGetValue(currentSession, out byte endingTrackNumber);
-
- (byte minute, byte second, byte frame) leadinPmsf =
- LbaToMsf(Image.Tracks.FirstOrDefault(t => t.TrackSequence == endingTrackNumber)?.TrackEndSector ??
- 0 + 1);
-
- // Starting track
- trackDescriptors.Add(new TrackDataDescriptor
- {
- SessionNumber = currentSession,
- POINT = 0xA0,
- ADR = 1,
- CONTROL = trackControl,
- PMIN = (byte)track.TrackSequence
- });
-
- // Ending track
- trackDescriptors.Add(new TrackDataDescriptor
- {
- SessionNumber = currentSession,
- POINT = 0xA1,
- ADR = 1,
- CONTROL = trackControl,
- PMIN = endingTrackNumber
- });
-
- // Lead-out start
- trackDescriptors.Add(new TrackDataDescriptor
- {
- SessionNumber = currentSession,
- POINT = 0xA2,
- ADR = 1,
- CONTROL = trackControl,
- PHOUR = 0,
- PMIN = leadinPmsf.minute,
- PSEC = leadinPmsf.second,
- PFRAME = leadinPmsf.frame
- });
- }
-
- (byte minute, byte second, byte frame) pmsf = LbaToMsf(track.TrackStartSector);
-
- // Track
- trackDescriptors.Add(new TrackDataDescriptor
- {
- SessionNumber = (byte)track.TrackSession,
- POINT = (byte)track.TrackSequence,
- ADR = 1,
- CONTROL = trackControl,
- PHOUR = 0,
- PMIN = pmsf.minute,
- PSEC = pmsf.second,
- PFRAME = pmsf.frame
- });
- }
-
- _toc.TrackDescriptors = trackDescriptors.ToArray();
- return true;
- }
-
- ///
- /// Convert the sector to LBA values
- ///
- /// Sector to convert
- /// LBA values for the sector number
- /// Copied from
- private (byte minute, byte second, byte frame) LbaToMsf(ulong sector) =>
- ((byte)((sector + 150) / 75 / 60), (byte)((sector + 150) / 75 % 60), (byte)((sector + 150) % 75));
-
- ///
- /// Load TOC for the current disc image
- ///
- /// True if the TOC could be loaded, false otherwise
- private async Task LoadTOC()
- {
- if(await Task.Run(() => Image.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC)) != true)
- {
- // Only generate the TOC if we have it set
- if(!App.Settings.GenerateMissingTOC)
- {
- Console.WriteLine("Full TOC not found");
- return false;
- }
-
- Console.WriteLine("Attempting to generate TOC");
- if(GenerateTOC())
- {
- Console.WriteLine(Prettify(_toc));
- return true;
- }
- else
- {
- Console.WriteLine("Full TOC not found or generated");
- return false;
- }
- }
-
- byte[] tocBytes = await Task.Run(() => Image.ReadDiskTag(MediaTagType.CD_FullTOC));
- if(tocBytes == null || tocBytes.Length == 0)
- {
- Console.WriteLine("Error reading TOC from disc image");
- return false;
- }
-
- if(Swapping.Swap(BitConverter.ToUInt16(tocBytes, 0)) + 2 != tocBytes.Length)
- {
- byte[] tmp = new byte[tocBytes.Length + 2];
- Array.Copy(tocBytes, 0, tmp, 2, tocBytes.Length);
- tmp[0] = (byte)((tocBytes.Length & 0xFF00) >> 8);
- tmp[1] = (byte)(tocBytes.Length & 0xFF);
- tocBytes = tmp;
- }
-
- var nullableToc = await Task.Run(() => Decode(tocBytes));
- if(nullableToc == null)
- {
- Console.WriteLine("Error decoding TOC");
- return false;
- }
-
- _toc = nullableToc.Value;
- Console.WriteLine(Prettify(_toc));
- return true;
- }
-
- ///
- /// Load the track for a given track number, if possible
- ///
- /// Track number to load
- private void LoadTrack(int index)
- {
- // Save if audio is currently playing
- bool oldRun = _source.Run;
-
- // Stop playback if necessary
- _source.Stop();
-
- // If it is a valid index, seek to the first, non-negative sectored index for the track
- if(index >= 0 && index < Image.Tracks.Count)
- {
- ushort firstIndex = Image.Tracks[index].Indexes.Keys.Min();
- int firstSector = Image.Tracks[index].Indexes[firstIndex];
- CurrentSector = (ulong)(firstSector >= 0 ? firstSector : Image.Tracks[index].Indexes[1]);
- }
-
- // Reset the playing state
- _source.Run = oldRun;
- }
-
///
/// Sets or resets the de-emphasis filters
///
diff --git a/RedBookPlayer/PlayerView.xaml b/RedBookPlayer/PlayerView.xaml
index 7b0f528..2213eba 100644
--- a/RedBookPlayer/PlayerView.xaml
+++ b/RedBookPlayer/PlayerView.xaml
@@ -94,6 +94,8 @@
EMPHASIS
COPY
COPY
+ 4CH
+ 4CH
HIDDEN
HIDDEN
diff --git a/RedBookPlayer/PlayerView.xaml.cs b/RedBookPlayer/PlayerView.xaml.cs
index 9e48786..72dc4b3 100644
--- a/RedBookPlayer/PlayerView.xaml.cs
+++ b/RedBookPlayer/PlayerView.xaml.cs
@@ -5,6 +5,7 @@ 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;
@@ -20,10 +21,15 @@ namespace RedBookPlayer
public class PlayerView : UserControl
{
///
- /// Player representing the internal state and loaded image
+ /// Player representing the internal state
///
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
///
@@ -66,32 +72,32 @@ namespace RedBookPlayer
/// String representing the digits for the player
private string GenerateDigitString()
{
- // If the player isn't initialized, return all '-' characters
- if (!Player.Initialized)
+ // 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 = Player.CurrentSector;
- if (Player.SectionStartSector != 0)
- sectorTime -= Player.SectionStartSector;
+ ulong sectorTime = PlayableDisc.CurrentSector;
+ if (PlayableDisc.SectionStartSector != 0)
+ sectorTime -= PlayableDisc.SectionStartSector;
else
- sectorTime += Player.TimeOffset;
+ sectorTime += PlayableDisc.TimeOffset;
int[] numbers = new int[]
{
- Player.CurrentTrack + 1,
- Player.CurrentIndex,
+ PlayableDisc.CurrentTrackNumber + 1,
+ PlayableDisc.CurrentTrackIndex,
(int)(sectorTime / (75 * 60)),
(int)(sectorTime / 75 % 60),
(int)(sectorTime % 75),
- Player.TotalTracks,
- Player.TotalIndexes,
+ PlayableDisc.TotalTracks,
+ PlayableDisc.TotalIndexes,
- (int)(Player.TotalTime / (75 * 60)),
- (int)(Player.TotalTime / 75 % 60),
- (int)(Player.TotalTime % 75),
+ (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)));
@@ -194,16 +200,12 @@ namespace RedBookPlayer
///
/// Aaruformat image file
/// True if the image is playble, false otherwise
- private bool IsPlayableImage(AaruFormat image)
+ private bool IsPlayableImage(IOpticalMediaImage image)
{
// Invalid images can't be played
if (image == null)
return false;
- // Tape images are not supported
- if (image.IsTape)
- 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);
@@ -215,6 +217,43 @@ namespace RedBookPlayer
};
}
+ ///
+ /// Load an image from the path
+ ///
+ /// Path to the image to load
+ private async void LoadImage(string path)
+ {
+ 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;
+ });
+
+ if(result)
+ {
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last();
+ });
+ }
+ }
+
///
/// Update the UI with the most recent information from the Player
///
@@ -232,12 +271,13 @@ namespace RedBookPlayer
if (Player.Initialized)
{
PlayerViewModel dataContext = (PlayerViewModel)DataContext;
- dataContext.HiddenTrack = Player.TimeOffset > 150;
- dataContext.ApplyDeEmphasis = Player.ApplyDeEmphasis;
- dataContext.TrackHasEmphasis = Player.TrackHasEmphasis;
- dataContext.CopyAllowed = Player.CopyAllowed;
- dataContext.IsAudioTrack = Player.TrackType == TrackType.Audio;
- dataContext.IsDataTrack = Player.TrackType != TrackType.Audio;
+ 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;
}
});
}
@@ -252,29 +292,7 @@ namespace RedBookPlayer
if (path == null)
return;
- bool result = await Task.Run(() =>
- {
- var image = new AaruFormat();
- var filter = new ZZZNoFilter();
- filter.Open(path);
- image.Open(filter);
-
- if (IsPlayableImage(image))
- {
- Player.Init(image, App.Settings.AutoPlay);
- return true;
- }
- else
- return false;
- });
-
- if (result)
- {
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last();
- });
- }
+ LoadImage(path);
}
public void PlayButton_Click(object sender, RoutedEventArgs e) => Player.Play();
@@ -283,21 +301,45 @@ namespace RedBookPlayer
public void StopButton_Click(object sender, RoutedEventArgs e) => Player.Stop();
- public void NextTrackButton_Click(object sender, RoutedEventArgs e) => Player.NextTrack();
+ public void NextTrackButton_Click(object sender, RoutedEventArgs e)
+ {
+ bool wasPlaying = Player.Playing;
+ if(wasPlaying) Player.Pause();
+ PlayableDisc.NextTrack();
+ if(wasPlaying) Player.Play();
+ }
- public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => Player.PreviousTrack();
+ public void PreviousTrackButton_Click(object sender, RoutedEventArgs e)
+ {
+ bool wasPlaying = Player.Playing;
+ if(wasPlaying) Player.Pause();
+ PlayableDisc.PreviousTrack();
+ if(wasPlaying) Player.Play();
+ }
- public void NextIndexButton_Click(object sender, RoutedEventArgs e) => Player.NextIndex(App.Settings.IndexButtonChangeTrack);
+ 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 PreviousIndexButton_Click(object sender, RoutedEventArgs e) => Player.PreviousIndex(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 FastForwardButton_Click(object sender, RoutedEventArgs e) => Player.FastForward();
+ public void FastForwardButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.FastForward();
- public void RewindButton_Click(object sender, RoutedEventArgs e) => Player.Rewind();
+ public void RewindButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.Rewind();
- public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.ToggleDeEmphasis(true);
+ public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.ToggleDeEmphasis(true);
- public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.ToggleDeEmphasis(false);
+ public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => PlayableDisc.ToggleDeEmphasis(false);
#endregion
}
diff --git a/RedBookPlayer/PlayerViewModel.cs b/RedBookPlayer/PlayerViewModel.cs
index a44dcc4..17e058f 100644
--- a/RedBookPlayer/PlayerViewModel.cs
+++ b/RedBookPlayer/PlayerViewModel.cs
@@ -32,6 +32,13 @@ namespace RedBookPlayer
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
{
diff --git a/RedBookPlayer/SettingsWindow.xaml.cs b/RedBookPlayer/SettingsWindow.xaml.cs
index abc880f..ae39c0f 100644
--- a/RedBookPlayer/SettingsWindow.xaml.cs
+++ b/RedBookPlayer/SettingsWindow.xaml.cs
@@ -36,8 +36,6 @@ namespace RedBookPlayer
MainWindow.ApplyTheme(_selectedTheme);
}
- PlayerView.Player.Volume = _settings.Volume;
-
_settings.Save();
}