Files
RedBookPlayer/RedBookPlayer/Player.cs

545 lines
16 KiB
C#
Raw Normal View History

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);
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
);
if (!ApplyDeEmphasis)
{
ApplyDeEmphasis = (subchannel[3] & 0b01000000) != 0;
}
2021-04-14 20:36:34 -03:00
CopyAllowed = (subchannel[2] & 0b01000000) != 0;
TrackType_ = (subchannel[1] & 0b01000000) != 0 ? TrackType.Data : TrackType.Audio;
TrackHasEmphasis = ApplyDeEmphasis;
TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max();
CurrentIndex = Image.Tracks[CurrentTrack].Indexes.Keys.Min();
2021-03-19 17:07:27 -03:00
}
}
}
ushort currentIndex = 1;
public ushort CurrentIndex
{
get
{
return currentIndex;
}
private set
{
currentIndex = value;
SectionStartSector = (ulong)Image.Tracks[CurrentTrack].Indexes[CurrentIndex];
TotalTime = Image.Tracks[CurrentTrack].TrackEndSector - Image.Tracks[CurrentTrack].TrackStartSector;
}
}
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)
{
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
{
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;
}
}
}
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; }
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;
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;
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));
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
CurrentTrack = 0;
LoadTrack(0);
2021-03-29 10:59:47 -03:00
if (autoPlay)
{
soundOut.Play();
}
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);
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)
{
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(() =>
{
lock (readingImage)
2021-03-19 17:07:27 -03: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(() =>
{
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);
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();
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;
}
if (CurrentSector < (ulong)Image.Tracks[CurrentTrack].Indexes[1] + 75)
2021-03-19 17:07:27 -03:00
{
if (App.Settings.AllowSkipHiddenTrack && CurrentTrack == 0 && CurrentSector >= 75)
{
CurrentSector = 0;
}
else
{
if (--CurrentTrack < 0)
{
CurrentTrack = Image.Tracks.Count - 1;
}
}
2021-03-19 17:07:27 -03:00
}
LoadTrack(CurrentTrack);
}
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
{
if (changeTrack)
{
NextTrack();
2021-06-05 11:35:52 -07:00
CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Min();
}
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 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
{
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()
{
ApplyDeEmphasis = true;
2021-03-19 17:07:27 -03:00
}
public void DisableDeEmphasis()
{
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()
{
}
}
}