2021-03-19 17:07:27 -03:00
|
|
|
using System;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Aaru.CommonTypes.Enums;
|
|
|
|
|
using Aaru.Decoders.CD;
|
|
|
|
|
using Aaru.DiscImages;
|
|
|
|
|
using Aaru.Helpers;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using CSCore.SoundOut;
|
|
|
|
|
using CSCore;
|
|
|
|
|
using NWaves.Audio;
|
|
|
|
|
using NWaves.Filters.BiQuad;
|
2021-03-29 09:17:50 -03:00
|
|
|
using static Aaru.Decoders.CD.FullTOC;
|
2021-04-14 20:36:34 -03:00
|
|
|
using Aaru.CommonTypes.Structs;
|
2021-03-19 17:07:27 -03:00
|
|
|
|
|
|
|
|
namespace RedBookPlayer
|
|
|
|
|
{
|
|
|
|
|
public class Player
|
|
|
|
|
{
|
2021-04-14 20:36:34 -03:00
|
|
|
public enum TrackType
|
|
|
|
|
{
|
|
|
|
|
Audio,
|
|
|
|
|
Data
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-19 17:07:27 -03:00
|
|
|
public bool Initialized = false;
|
|
|
|
|
private int currentTrack = 0;
|
|
|
|
|
public int CurrentTrack
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return currentTrack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (Image != null)
|
|
|
|
|
{
|
|
|
|
|
if (value >= Image.Tracks.Count)
|
|
|
|
|
{
|
|
|
|
|
currentTrack = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (value < 0)
|
|
|
|
|
{
|
|
|
|
|
currentTrack = Image.Tracks.Count - 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currentTrack = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] flagsData = Image.ReadSectorTag(Image.Tracks[CurrentTrack].TrackSequence, SectorTagType.CdTrackFlags);
|
2021-04-12 18:12:51 -03:00
|
|
|
ApplyDeEmphasis = ((CdFlags)flagsData[0]).HasFlag(CdFlags.PreEmphasis);
|
2021-03-19 17:07:27 -03:00
|
|
|
|
2021-04-14 20:36:34 -03:00
|
|
|
byte[] subchannel = Image.ReadSectorTag(
|
|
|
|
|
Image.Tracks[CurrentTrack].TrackStartSector,
|
|
|
|
|
SectorTagType.CdSectorSubchannel
|
|
|
|
|
);
|
|
|
|
|
|
2021-04-12 18:12:51 -03:00
|
|
|
if (!ApplyDeEmphasis)
|
2021-03-29 20:12:44 -03:00
|
|
|
{
|
2021-04-12 18:12:51 -03:00
|
|
|
ApplyDeEmphasis = (subchannel[3] & 0b01000000) != 0;
|
2021-03-29 20:12:44 -03:00
|
|
|
}
|
|
|
|
|
|
2021-04-14 20:36:34 -03:00
|
|
|
CopyAllowed = (subchannel[2] & 0b01000000) != 0;
|
|
|
|
|
TrackType_ = (subchannel[1] & 0b01000000) != 0 ? TrackType.Data : TrackType.Audio;
|
|
|
|
|
|
2021-04-12 18:12:51 -03:00
|
|
|
TrackHasEmphasis = ApplyDeEmphasis;
|
|
|
|
|
|
2021-04-13 15:58:44 -03:00
|
|
|
TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max();
|
2021-06-05 10:57:20 -07:00
|
|
|
CurrentIndex = Image.Tracks[CurrentTrack].Indexes.Keys.Min();
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-15 19:24:04 -03:00
|
|
|
ushort currentIndex = 1;
|
|
|
|
|
public ushort CurrentIndex
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return currentIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
currentIndex = value;
|
|
|
|
|
|
|
|
|
|
SectionStartSector = (ulong)Image.Tracks[CurrentTrack].Indexes[CurrentIndex];
|
2021-04-15 19:26:57 -03:00
|
|
|
TotalTime = Image.Tracks[CurrentTrack].TrackEndSector - Image.Tracks[CurrentTrack].TrackStartSector;
|
2021-04-15 19:24:04 -03:00
|
|
|
}
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
private ulong currentSector = 0;
|
|
|
|
|
private int currentSectorReadPosition = 0;
|
|
|
|
|
public ulong CurrentSector
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return currentSector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
currentSector = value;
|
|
|
|
|
|
|
|
|
|
if (Image != null)
|
|
|
|
|
{
|
2021-04-13 18:56:01 -03:00
|
|
|
if (CurrentTrack < Image.Tracks.Count - 1 && CurrentSector >= Image.Tracks[CurrentTrack + 1].TrackStartSector ||
|
|
|
|
|
CurrentTrack > 0 && CurrentSector < Image.Tracks[CurrentTrack].TrackStartSector)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
2021-04-13 18:56:01 -03:00
|
|
|
foreach (Track track in Image.Tracks)
|
|
|
|
|
{
|
|
|
|
|
if (track.TrackStartSector >= CurrentSector)
|
|
|
|
|
{
|
|
|
|
|
CurrentTrack = (int)track.TrackSequence - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var item in Image.Tracks[CurrentTrack].Indexes.Reverse())
|
|
|
|
|
{
|
|
|
|
|
if ((int)CurrentSector >= item.Value)
|
|
|
|
|
{
|
|
|
|
|
CurrentIndex = item.Key;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CurrentIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-12 18:12:51 -03:00
|
|
|
public bool TrackHasEmphasis { get; private set; } = false;
|
|
|
|
|
public bool ApplyDeEmphasis { get; private set; } = false;
|
2021-04-14 20:36:34 -03:00
|
|
|
public bool CopyAllowed { get; private set; } = false;
|
|
|
|
|
public TrackType? TrackType_ { get; private set; }
|
2021-04-15 19:24:04 -03:00
|
|
|
public ulong SectionStartSector { get; private set; }
|
2021-03-26 09:57:05 -03:00
|
|
|
public int TotalTracks { get; private set; } = 0;
|
|
|
|
|
public int TotalIndexes { get; private set; } = 0;
|
2021-03-29 09:17:50 -03:00
|
|
|
public ulong TimeOffset { get; private set; } = 0;
|
2021-04-13 15:58:44 -03:00
|
|
|
public ulong TotalTime { get; private set; } = 0;
|
2021-03-29 19:40:22 -03:00
|
|
|
int volume = 100;
|
|
|
|
|
public int Volume
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return volume;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (volume >= 0 && volume <= 100)
|
|
|
|
|
{
|
|
|
|
|
volume = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
public AaruFormat Image { get; private set; }
|
|
|
|
|
FullTOC.CDFullTOC toc;
|
|
|
|
|
PlayerSource source;
|
|
|
|
|
ALSoundOut soundOut;
|
|
|
|
|
BiQuadFilter deEmphasisFilterLeft;
|
|
|
|
|
BiQuadFilter deEmphasisFilterRight;
|
2021-06-05 10:57:20 -07:00
|
|
|
object readingImage = new object();
|
2021-03-19 17:07:27 -03:00
|
|
|
|
2021-03-29 10:59:47 -03:00
|
|
|
public async void Init(AaruFormat image, bool autoPlay = false)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
|
|
|
|
this.Image = image;
|
|
|
|
|
|
|
|
|
|
if (await Task.Run(() => image.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC)) != true)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Full TOC not found");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] tocBytes = await Task.Run(() => image.ReadDiskTag(MediaTagType.CD_FullTOC));
|
|
|
|
|
|
|
|
|
|
if ((tocBytes?.Length ?? 0) == 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error reading TOC from disc image");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FullTOC.CDFullTOC? nullableToc = await Task.Run(() => FullTOC.Decode(tocBytes));
|
|
|
|
|
|
|
|
|
|
if (nullableToc == null)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error decoding TOC");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toc = nullableToc.Value;
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(FullTOC.Prettify(toc));
|
|
|
|
|
|
2021-04-08 21:41:31 -03:00
|
|
|
if (deEmphasisFilterLeft == null)
|
|
|
|
|
{
|
|
|
|
|
deEmphasisFilterLeft = new DeEmphasisFilter();
|
|
|
|
|
deEmphasisFilterRight = new DeEmphasisFilter();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
deEmphasisFilterLeft.Reset();
|
|
|
|
|
deEmphasisFilterRight.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (source == null)
|
|
|
|
|
{
|
|
|
|
|
source = new PlayerSource(ProviderRead);
|
|
|
|
|
|
|
|
|
|
soundOut = new ALSoundOut(100);
|
|
|
|
|
soundOut.Initialize(source);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
soundOut.Stop();
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
|
2021-04-13 15:58:44 -03:00
|
|
|
CurrentTrack = 0;
|
|
|
|
|
LoadTrack(0);
|
|
|
|
|
|
2021-03-29 10:59:47 -03:00
|
|
|
if (autoPlay)
|
|
|
|
|
{
|
|
|
|
|
soundOut.Play();
|
|
|
|
|
}
|
2021-04-13 15:58:44 -03:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TotalIndexes = 0;
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
|
2021-03-26 09:57:05 -03:00
|
|
|
TotalTracks = image.Tracks.Count;
|
2021-03-29 09:17:50 -03:00
|
|
|
TrackDataDescriptor firstTrack = toc.TrackDescriptors.First(d => d.ADR == 1 && d.POINT == 1);
|
|
|
|
|
TimeOffset = (ulong)(firstTrack.PMIN * 60 * 75 + firstTrack.PSEC * 75 + firstTrack.PFRAME);
|
2021-04-13 15:58:44 -03:00
|
|
|
TotalTime = TimeOffset + image.Tracks.Last().TrackEndSector;
|
2021-03-29 09:17:50 -03:00
|
|
|
|
2021-04-15 19:16:34 -03:00
|
|
|
Volume = App.Settings.Volume;
|
|
|
|
|
|
2021-03-19 17:07:27 -03:00
|
|
|
Initialized = true;
|
|
|
|
|
|
|
|
|
|
source.Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ProviderRead(byte[] buffer, int offset, int count)
|
|
|
|
|
{
|
2021-03-30 20:25:44 -03:00
|
|
|
soundOut.Volume = (float)Volume / 100;
|
|
|
|
|
|
2021-03-19 17:07:27 -03:00
|
|
|
ulong sectorsToRead;
|
|
|
|
|
ulong zeroSectorsAmount;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
sectorsToRead = (ulong)count / 2352 + 2;
|
|
|
|
|
zeroSectorsAmount = 0;
|
|
|
|
|
|
|
|
|
|
if (CurrentSector + sectorsToRead > Image.Info.Sectors)
|
|
|
|
|
{
|
|
|
|
|
ulong oldSectorsToRead = sectorsToRead;
|
|
|
|
|
sectorsToRead = Image.Info.Sectors - CurrentSector;
|
|
|
|
|
zeroSectorsAmount = oldSectorsToRead - sectorsToRead;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sectorsToRead <= 0)
|
|
|
|
|
{
|
2021-04-07 19:05:46 -03:00
|
|
|
LoadTrack(0);
|
2021-03-19 17:07:27 -03:00
|
|
|
currentSectorReadPosition = 0;
|
|
|
|
|
}
|
|
|
|
|
} while (sectorsToRead <= 0);
|
|
|
|
|
|
|
|
|
|
byte[] zeroSectors = new Byte[zeroSectorsAmount * 2352];
|
|
|
|
|
Array.Clear(zeroSectors, 0, zeroSectors.Length);
|
|
|
|
|
byte[] audioData;
|
|
|
|
|
|
|
|
|
|
Task<byte[]> task = Task.Run(() =>
|
|
|
|
|
{
|
2021-06-05 10:57:20 -07:00
|
|
|
lock (readingImage)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
2021-06-05 10:57:20 -07:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray();
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception)
|
|
|
|
|
{
|
|
|
|
|
LoadTrack(0);
|
|
|
|
|
return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray();
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (task.Wait(TimeSpan.FromMilliseconds(100)))
|
|
|
|
|
{
|
|
|
|
|
audioData = task.Result;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Array.Clear(buffer, offset, count);
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
2021-06-05 10:57:20 -07:00
|
|
|
lock (readingImage)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
|
|
|
|
Image.ReadSector(CurrentSector + 375);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
byte[] audioDataSegment = new byte[count];
|
|
|
|
|
Array.Copy(audioData, currentSectorReadPosition, audioDataSegment, 0, count);
|
|
|
|
|
|
2021-04-12 18:12:51 -03:00
|
|
|
if (ApplyDeEmphasis)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
|
|
|
|
float[][] floatAudioData = new float[2][];
|
|
|
|
|
floatAudioData[0] = new float[audioDataSegment.Length / 4];
|
|
|
|
|
floatAudioData[1] = new float[audioDataSegment.Length / 4];
|
|
|
|
|
ByteConverter.ToFloats16Bit(audioDataSegment, floatAudioData);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < floatAudioData[0].Length; i++)
|
|
|
|
|
{
|
|
|
|
|
floatAudioData[0][i] = deEmphasisFilterLeft.Process(floatAudioData[0][i]);
|
|
|
|
|
floatAudioData[1][i] = deEmphasisFilterRight.Process(floatAudioData[1][i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ByteConverter.FromFloats16Bit(floatAudioData, audioDataSegment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Array.Copy(audioDataSegment, 0, buffer, offset, count);
|
|
|
|
|
|
|
|
|
|
currentSectorReadPosition += count;
|
|
|
|
|
if (currentSectorReadPosition >= 2352)
|
|
|
|
|
{
|
|
|
|
|
CurrentSector += (ulong)currentSectorReadPosition / 2352;
|
|
|
|
|
currentSectorReadPosition %= 2352;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadTrack(int index)
|
|
|
|
|
{
|
|
|
|
|
bool oldRun = source.Run;
|
|
|
|
|
source.Stop();
|
|
|
|
|
|
2021-04-07 19:05:46 -03:00
|
|
|
CurrentSector = (ulong)Image.Tracks[index].Indexes[1];
|
2021-03-19 17:07:27 -03:00
|
|
|
|
|
|
|
|
source.Run = oldRun;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Play()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
soundOut.Play();
|
2021-04-13 15:58:44 -03:00
|
|
|
TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max();
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Pause()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
soundOut.Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
soundOut.Stop();
|
|
|
|
|
LoadTrack(CurrentTrack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void NextTrack()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (++CurrentTrack >= Image.Tracks.Count)
|
|
|
|
|
{
|
|
|
|
|
CurrentTrack = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoadTrack(CurrentTrack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PreviousTrack()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 21:04:22 -03:00
|
|
|
if (CurrentSector < (ulong)Image.Tracks[CurrentTrack].Indexes[1] + 75)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
2021-04-08 21:39:39 -03:00
|
|
|
if (App.Settings.AllowSkipHiddenTrack && CurrentTrack == 0 && CurrentSector >= 75)
|
2021-04-07 21:04:22 -03:00
|
|
|
{
|
2021-04-08 21:39:39 -03:00
|
|
|
CurrentSector = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (--CurrentTrack < 0)
|
|
|
|
|
{
|
|
|
|
|
CurrentTrack = Image.Tracks.Count - 1;
|
|
|
|
|
}
|
2021-04-07 21:04:22 -03:00
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoadTrack(CurrentTrack);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 12:49:56 -03:00
|
|
|
public void NextIndex(bool changeTrack)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-05 11:35:52 -07:00
|
|
|
if (CurrentIndex + 1 > Image.Tracks[CurrentTrack].Indexes.Keys.Max())
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
2021-03-29 12:49:56 -03:00
|
|
|
if (changeTrack)
|
|
|
|
|
{
|
|
|
|
|
NextTrack();
|
2021-06-05 11:35:52 -07:00
|
|
|
CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Min();
|
2021-03-29 12:49:56 -03:00
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-06-05 11:35:52 -07:00
|
|
|
CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[++CurrentIndex];
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 12:49:56 -03:00
|
|
|
public void PreviousIndex(bool changeTrack)
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-05 11:35:52 -07:00
|
|
|
if (CurrentIndex - 1 < Image.Tracks[CurrentTrack].Indexes.Keys.Min())
|
2021-03-19 17:07:27 -03:00
|
|
|
{
|
2021-03-29 12:49:56 -03:00
|
|
|
if (changeTrack)
|
|
|
|
|
{
|
|
|
|
|
PreviousTrack();
|
|
|
|
|
CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Max();
|
|
|
|
|
}
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-06-05 11:35:52 -07:00
|
|
|
CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[--CurrentIndex];
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void FastForward()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CurrentSector = Math.Min(Image.Info.Sectors - 1, CurrentSector + 75);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Rewind()
|
|
|
|
|
{
|
|
|
|
|
if (Image == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CurrentSector >= 75)
|
|
|
|
|
CurrentSector -= 75;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void EnableDeEmphasis()
|
|
|
|
|
{
|
2021-04-12 18:12:51 -03:00
|
|
|
ApplyDeEmphasis = true;
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DisableDeEmphasis()
|
|
|
|
|
{
|
2021-04-12 18:12:51 -03:00
|
|
|
ApplyDeEmphasis = false;
|
2021-03-19 17:07:27 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class PlayerSource : IWaveSource
|
|
|
|
|
{
|
|
|
|
|
public CSCore.WaveFormat WaveFormat => new CSCore.WaveFormat();
|
|
|
|
|
bool IAudioSource.CanSeek => throw new NotImplementedException();
|
|
|
|
|
public long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
|
|
|
|
public long Length => throw new NotImplementedException();
|
|
|
|
|
|
|
|
|
|
public bool Run = true;
|
|
|
|
|
private ReadFunction read;
|
|
|
|
|
|
|
|
|
|
public delegate int ReadFunction(byte[] buffer, int offset, int count);
|
|
|
|
|
|
|
|
|
|
public PlayerSource(ReadFunction read)
|
|
|
|
|
{
|
|
|
|
|
this.read = read;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Read(byte[] buffer, int offset, int count)
|
|
|
|
|
{
|
|
|
|
|
if (!Run)
|
|
|
|
|
{
|
|
|
|
|
Array.Clear(buffer, offset, count);
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return read(buffer, offset, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
|
|
|
|
Run = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
Run = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|