diff --git a/RedBookPlayer/App.xaml b/RedBookPlayer/App.xaml index 911230d..f598a25 100644 --- a/RedBookPlayer/App.xaml +++ b/RedBookPlayer/App.xaml @@ -1,8 +1,7 @@ - - - + + - + \ No newline at end of file diff --git a/RedBookPlayer/App.xaml.cs b/RedBookPlayer/App.xaml.cs index 6a3a6a9..43a5898 100644 --- a/RedBookPlayer/App.xaml.cs +++ b/RedBookPlayer/App.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using Avalonia; using Avalonia.Controls; @@ -11,10 +12,8 @@ namespace RedBookPlayer { public static Settings Settings; - static App() - { - Directory.SetCurrentDirectory(Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName)); - } + static App() => + Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); public override void Initialize() { @@ -28,9 +27,9 @@ namespace RedBookPlayer public override void OnFrameworkInitializationCompleted() { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow(); + desktop.MainWindow = new MainWindow(); desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; Settings = Settings.Load("settings.json"); diff --git a/RedBookPlayer/DeEmphasisFilter.cs b/RedBookPlayer/DeEmphasisFilter.cs index fd5c8bd..e9122ab 100644 --- a/RedBookPlayer/DeEmphasisFilter.cs +++ b/RedBookPlayer/DeEmphasisFilter.cs @@ -5,38 +5,41 @@ namespace RedBookPlayer { public class DeEmphasisFilter : BiQuadFilter { - static double B0, B1, B2, A0, A1, A2; + static readonly double B0; + static readonly double B1; + static readonly double B2; + static readonly double A0; + static readonly double A1; + static readonly double A2; static DeEmphasisFilter() { - double fc = 5277; + double fc = 5277; double slope = 0.4850; - double gain = -9.465; + double gain = -9.465; - double w0 = 2 * Math.PI * fc / 44100; - double A = Math.Exp(gain / 40 * Math.Log(10)); - double alpha = Math.Sin(w0) / 2 * Math.Sqrt((A + 1 / A) * (1 / slope - 1) + 2); + double w0 = 2 * Math.PI * fc / 44100; + double A = Math.Exp(gain / 40 * Math.Log(10)); + double alpha = Math.Sin(w0) / 2 * Math.Sqrt(((A + (1 / A)) * ((1 / slope) - 1)) + 2); double cs = Math.Cos(w0); - double v = 2 * Math.Sqrt(A) * alpha; + double v = 2 * Math.Sqrt(A) * alpha; - B0 = A * ((A + 1) + (A - 1) * cs + v); - B1 = -2 * A * ((A - 1) + (A + 1) * cs); - B2 = A * ((A + 1) + (A - 1) * cs - v); - A0 = (A + 1) - (A - 1) * cs + v; - A1 = 2 * ((A - 1) - (A + 1) * cs); - A2 = (A + 1) - (A - 1) * cs - v; + B0 = A * (A + 1 + ((A - 1) * cs) + v); + B1 = -2 * A * (A - 1 + ((A + 1) * cs)); + B2 = A * (A + 1 + ((A - 1) * cs) - v); + A0 = A + 1 - ((A - 1) * cs) + v; + A1 = 2 * (A - 1 - ((A + 1) * cs)); + A2 = A + 1 - ((A - 1) * cs) - v; B2 /= A0; B1 /= A0; B0 /= A0; A2 /= A0; A1 /= A0; - A0 = 1; + A0 = 1; } - public DeEmphasisFilter() : base(B0, B1, B2, A0, A1, A2) - { - } + public DeEmphasisFilter() : base(B0, B1, B2, A0, A1, A2) {} } } \ No newline at end of file diff --git a/RedBookPlayer/HiResTimer.cs b/RedBookPlayer/HiResTimer.cs index ea05e59..1c2a5ef 100644 --- a/RedBookPlayer/HiResTimer.cs +++ b/RedBookPlayer/HiResTimer.cs @@ -2,34 +2,35 @@ using System; using System.Diagnostics; using System.Threading; -namespace RedBookPlayer { +namespace RedBookPlayer +{ public class HiResTimer { - private static readonly float tickFrequency = 1000f / Stopwatch.Frequency; + static readonly float tickFrequency = 1000f / Stopwatch.Frequency; - public event EventHandler Elapsed; + volatile float interval; + volatile bool isRunning; - private volatile float interval; - private volatile bool isRunning; - - public HiResTimer() : this(1f) - { - } + public HiResTimer() : this(1f) {} public HiResTimer(float interval) { - if (interval < 0f || Single.IsNaN(interval)) + if(interval < 0f || + float.IsNaN(interval)) throw new ArgumentOutOfRangeException(nameof(interval)); + this.interval = interval; } public float Interval { - get { return interval; } + get => interval; set { - if (value < 0f || Single.IsNaN(value)) + if(value < 0f || + float.IsNaN(value)) throw new ArgumentOutOfRangeException(nameof(value)); + interval = value; } } @@ -38,89 +39,82 @@ namespace RedBookPlayer { { set { - if (value) + if(value) Start(); else Stop(); } - get { return isRunning; } + get => isRunning; } + public event EventHandler Elapsed; + public void Start() { - if (isRunning) + if(isRunning) return; isRunning = true; - Thread thread = new Thread(ExecuteTimer); + var thread = new Thread(ExecuteTimer); thread.Priority = ThreadPriority.Highest; thread.Start(); } - public void Stop() - { - isRunning = false; - } + public void Stop() => isRunning = false; - private void ExecuteTimer() + void ExecuteTimer() { float nextTrigger = 0f; - Stopwatch stopwatch = new Stopwatch(); + var stopwatch = new Stopwatch(); stopwatch.Start(); - while (isRunning) + while(isRunning) { nextTrigger += interval; float elapsed; - while (true) + while(true) { elapsed = ElapsedHiRes(stopwatch); float diff = nextTrigger - elapsed; - if (diff <= 0f) + + if(diff <= 0f) break; - if (diff < 1f) + if(diff < 1f) Thread.SpinWait(10); - else if (diff < 5f) + else if(diff < 5f) Thread.SpinWait(100); - else if (diff < 15f) + else if(diff < 15f) Thread.Sleep(1); else Thread.Sleep(10); - if (!isRunning) + if(!isRunning) return; } - float delay = elapsed - nextTrigger; Elapsed?.Invoke(this, new HiResTimerElapsedEventArgs(delay)); - if (stopwatch.Elapsed.TotalHours >= 1d) - { - stopwatch.Restart(); - nextTrigger = 0f; - } + if(!(stopwatch.Elapsed.TotalHours >= 1d)) + continue; + + stopwatch.Restart(); + nextTrigger = 0f; } stopwatch.Stop(); } - private static float ElapsedHiRes(Stopwatch stopwatch) - { - return stopwatch.ElapsedTicks * tickFrequency; - } + static float ElapsedHiRes(Stopwatch stopwatch) => stopwatch.ElapsedTicks * tickFrequency; } public class HiResTimerElapsedEventArgs : EventArgs { - public float Delay { get; } + internal HiResTimerElapsedEventArgs(float delay) => Delay = delay; - internal HiResTimerElapsedEventArgs(float delay) - { - Delay = delay; - } + public float Delay { get; } } } \ No newline at end of file diff --git a/RedBookPlayer/MainWindow.xaml b/RedBookPlayer/MainWindow.xaml index 9b7f264..eeab898 100644 --- a/RedBookPlayer/MainWindow.xaml +++ b/RedBookPlayer/MainWindow.xaml @@ -1,10 +1,6 @@ - - - + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" + x:Class="RedBookPlayer.MainWindow" Title="RedBookPlayer" SizeToContent="WidthAndHeight"> + + \ No newline at end of file diff --git a/RedBookPlayer/MainWindow.xaml.cs b/RedBookPlayer/MainWindow.xaml.cs index ccafdd4..14b37c8 100644 --- a/RedBookPlayer/MainWindow.xaml.cs +++ b/RedBookPlayer/MainWindow.xaml.cs @@ -1,16 +1,17 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Avalonia.Input; using System; using System.IO; +using System.Xml; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; namespace RedBookPlayer { public class MainWindow : Window { - public static MainWindow Instance; - public ContentControl ContentControl; - public Window settingsWindow; + public static MainWindow Instance; + public ContentControl ContentControl; + public Window settingsWindow; public MainWindow() { @@ -20,74 +21,76 @@ namespace RedBookPlayer public static void ApplyTheme(string theme) { - if ((theme ?? "") == "") + if((theme ?? "") == "") { return; } - if (theme == "default") + if(theme == "default") { - MainWindow.Instance.ContentControl.Content = new PlayerView(); + Instance.ContentControl.Content = new PlayerView(); } else { string themeDirectory = Directory.GetCurrentDirectory() + "/themes/" + theme; - string xamlPath = themeDirectory + "/view.xaml"; + string xamlPath = themeDirectory + "/view.xaml"; - if (!File.Exists(xamlPath)) + if(!File.Exists(xamlPath)) { - Console.WriteLine($"Warning: specified theme doesn't exist, reverting to default"); + Console.WriteLine("Warning: specified theme doesn't exist, reverting to default"); + return; } try { - MainWindow.Instance.ContentControl.Content = new PlayerView( - File.ReadAllText(xamlPath).Replace("Source=\"", $"Source=\"file://{themeDirectory}/") - ); + Instance.ContentControl.Content = + new PlayerView(File.ReadAllText(xamlPath). + Replace("Source=\"", $"Source=\"file://{themeDirectory}/")); } - catch (System.Xml.XmlException ex) + catch(XmlException ex) { Console.WriteLine($"Error: invalid theme XAML ({ex.Message}), reverting to default"); - MainWindow.Instance.ContentControl.Content = new PlayerView(); + Instance.ContentControl.Content = new PlayerView(); } } - MainWindow.Instance.Width = ((PlayerView)MainWindow.Instance.ContentControl.Content).Width; - MainWindow.Instance.Height = ((PlayerView)MainWindow.Instance.ContentControl.Content).Height; + Instance.Width = ((PlayerView)Instance.ContentControl.Content).Width; + Instance.Height = ((PlayerView)Instance.ContentControl.Content).Height; } public void OnKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.F1) + if(e.Key == Key.F1) { settingsWindow = new SettingsWindow(App.Settings); settingsWindow.Show(); } } - private void InitializeComponent() + void InitializeComponent() { AvaloniaXamlLoader.Load(this); - ContentControl = this.FindControl("Content"); + ContentControl = this.FindControl("Content"); ContentControl.Content = new PlayerView(); - MainWindow.Instance.MaxWidth = ((PlayerView)MainWindow.Instance.ContentControl.Content).Width; - MainWindow.Instance.MaxHeight = ((PlayerView)MainWindow.Instance.ContentControl.Content).Height; + Instance.MaxWidth = ((PlayerView)Instance.ContentControl.Content).Width; + Instance.MaxHeight = ((PlayerView)Instance.ContentControl.Content).Height; ContentControl.Content = new PlayerView(); - this.CanResize = false; + CanResize = false; - this.KeyDown += OnKeyDown; - this.Closing += (s, e) => + KeyDown += OnKeyDown; + + Closing += (s, e) => { settingsWindow?.Close(); settingsWindow = null; }; - this.Closing += (e, f) => + Closing += (e, f) => { PlayerView.Player.Stop(); }; diff --git a/RedBookPlayer/Player.cs b/RedBookPlayer/Player.cs index 4b2c71f..564f035 100644 --- a/RedBookPlayer/Player.cs +++ b/RedBookPlayer/Player.cs @@ -1,17 +1,16 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; -using Aaru.Decoders.CD; -using static Aaru.Decoders.CD.FullTOC; using Aaru.DiscImages; using Aaru.Helpers; -using CSCore.SoundOut; using CSCore; +using CSCore.SoundOut; using NWaves.Audio; using NWaves.Filters.BiQuad; +using static Aaru.Decoders.CD.FullTOC; +using WaveFormat = CSCore.WaveFormat; namespace RedBookPlayer { @@ -19,66 +18,63 @@ namespace RedBookPlayer { public enum TrackType { - Audio, - Data + Audio, Data } - public bool Initialized = false; - private int currentTrack = 0; + readonly object readingImage = new object(); + + ushort currentIndex = 1; + ulong currentSector; + int currentSectorReadPosition; + int currentTrack; + BiQuadFilter deEmphasisFilterLeft; + BiQuadFilter deEmphasisFilterRight; + public bool Initialized; + ALSoundOut soundOut; + PlayerSource source; + CDFullTOC toc; + int volume = 100; + public int CurrentTrack { - get - { - return currentTrack; - } + get => 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; - } + if(Image == null) + return; - byte[] flagsData = Image.ReadSectorTag(Image.Tracks[CurrentTrack].TrackSequence, SectorTagType.CdTrackFlags); - ApplyDeEmphasis = ((CdFlags)flagsData[0]).HasFlag(CdFlags.PreEmphasis); + if(value >= Image.Tracks.Count) + currentTrack = 0; + else if(value < 0) + currentTrack = Image.Tracks.Count - 1; + else + currentTrack = value; - byte[] subchannel = Image.ReadSectorTag( - Image.Tracks[CurrentTrack].TrackStartSector, - SectorTagType.CdSectorSubchannel - ); + byte[] flagsData = + Image.ReadSectorTag(Image.Tracks[CurrentTrack].TrackSequence, SectorTagType.CdTrackFlags); - if (!ApplyDeEmphasis) - { - ApplyDeEmphasis = (subchannel[3] & 0b01000000) != 0; - } + ApplyDeEmphasis = ((CdFlags)flagsData[0]).HasFlag(CdFlags.PreEmphasis); - CopyAllowed = (subchannel[2] & 0b01000000) != 0; - TrackType_ = (subchannel[1] & 0b01000000) != 0 ? TrackType.Data : TrackType.Audio; + byte[] subchannel = Image.ReadSectorTag(Image.Tracks[CurrentTrack].TrackStartSector, + SectorTagType.CdSectorSubchannel); - TrackHasEmphasis = ApplyDeEmphasis; + if(!ApplyDeEmphasis) + ApplyDeEmphasis = (subchannel[3] & 0b01000000) != 0; - TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max(); - CurrentIndex = Image.Tracks[CurrentTrack].Indexes.Keys.Min(); - } + 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(); } } - ushort currentIndex = 1; + public ushort CurrentIndex { - get - { - return currentIndex; - } + get => currentIndex; private set { @@ -88,121 +84,116 @@ namespace RedBookPlayer TotalTime = Image.Tracks[CurrentTrack].TrackEndSector - Image.Tracks[CurrentTrack].TrackStartSector; } } - private ulong currentSector = 0; - private int currentSectorReadPosition = 0; + public ulong CurrentSector { - get - { - return currentSector; - } + get => 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)) - { - foreach (Track track in Image.Tracks.ToArray().Reverse()) - { - if (CurrentSector >= track.TrackStartSector) - { - CurrentTrack = (int)track.TrackSequence - 1; - break; - } - } - } + if(Image == null) + return; - foreach (var item in Image.Tracks[CurrentTrack].Indexes.Reverse()) + if((CurrentTrack < Image.Tracks.Count - 1 && + CurrentSector >= Image.Tracks[CurrentTrack + 1].TrackStartSector) || + (CurrentTrack > 0 && CurrentSector < Image.Tracks[CurrentTrack].TrackStartSector)) + { + foreach(Track track in Image.Tracks.ToArray().Reverse()) { - if ((int)CurrentSector >= item.Value) - { - CurrentIndex = item.Key; - return; - } + if(CurrentSector < track.TrackStartSector) + continue; + + CurrentTrack = (int)track.TrackSequence - 1; + + break; } - CurrentIndex = 0; } + + foreach((ushort key, int i) in Image.Tracks[CurrentTrack].Indexes.Reverse()) + { + if((int)CurrentSector < i) + continue; + + CurrentIndex = key; + + return; + } + + CurrentIndex = 0; } } - public bool TrackHasEmphasis { get; private set; } = false; - public bool ApplyDeEmphasis { get; private set; } = false; - public bool CopyAllowed { get; private set; } = false; - public TrackType? TrackType_ { get; private set; } - public ulong SectionStartSector { get; private set; } - public int TotalTracks { get; private set; } = 0; - public int TotalIndexes { get; private set; } = 0; - public ulong TimeOffset { get; private set; } = 0; - public ulong TotalTime { get; private set; } = 0; - int volume = 100; + + public bool TrackHasEmphasis { get; private set; } + public bool ApplyDeEmphasis { get; private set; } + public bool CopyAllowed { get; private set; } + public TrackType? TrackType_ { get; private set; } + public ulong SectionStartSector { get; private set; } + public int TotalTracks { get; private set; } + public int TotalIndexes { get; private set; } + public ulong TimeOffset { get; private set; } + public ulong TotalTime { get; private set; } + public int Volume { - get - { - return volume; - } + get => volume; set { - if (volume >= 0 && volume <= 100) - { + if(volume >= 0 && + volume <= 100) volume = value; - } } } + public AaruFormat Image { get; private set; } - FullTOC.CDFullTOC toc; - PlayerSource source; - ALSoundOut soundOut; - BiQuadFilter deEmphasisFilterLeft; - BiQuadFilter deEmphasisFilterRight; - object readingImage = new object(); public async void Init(AaruFormat image, bool autoPlay = false) { - this.Image = image; + Image = image; - if (await Task.Run(() => image.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC)) != true) + 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) + if((tocBytes?.Length ?? 0) == 0) { Console.WriteLine("Error reading TOC from disc image"); + return; } - if (Swapping.Swap(BitConverter.ToUInt16(tocBytes, 0)) + 2 != tocBytes.Length) + 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); + tmp[0] = (byte)((tocBytes.Length & 0xFF00) >> 8); + tmp[1] = (byte)(tocBytes.Length & 0xFF); tocBytes = tmp; } - FullTOC.CDFullTOC? nullableToc = await Task.Run(() => FullTOC.Decode(tocBytes)); + CDFullTOC? nullableToc = await Task.Run(() => Decode(tocBytes)); - if (nullableToc == null) + if(nullableToc == null) { Console.WriteLine("Error decoding TOC"); + return; } toc = nullableToc.Value; - Console.WriteLine(FullTOC.Prettify(toc)); + Console.WriteLine(Prettify(toc)); - if (deEmphasisFilterLeft == null) + if(deEmphasisFilterLeft == null) { - deEmphasisFilterLeft = new DeEmphasisFilter(); + deEmphasisFilterLeft = new DeEmphasisFilter(); deEmphasisFilterRight = new DeEmphasisFilter(); } else @@ -211,7 +202,7 @@ namespace RedBookPlayer deEmphasisFilterRight.Reset(); } - if (source == null) + if(source == null) { source = new PlayerSource(ProviderRead); @@ -219,26 +210,20 @@ namespace RedBookPlayer soundOut.Initialize(source); } else - { soundOut.Stop(); - } CurrentTrack = 0; LoadTrack(0); - if (autoPlay) - { + if(autoPlay) soundOut.Play(); - } else - { TotalIndexes = 0; - } 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; + TimeOffset = (ulong)((firstTrack.PMIN * 60 * 75) + (firstTrack.PSEC * 75) + firstTrack.PFRAME); + TotalTime = TimeOffset + image.Tracks.Last().TrackEndSector; Volume = App.Settings.Volume; @@ -256,56 +241,58 @@ namespace RedBookPlayer do { - sectorsToRead = (ulong)count / 2352 + 2; + sectorsToRead = ((ulong)count / 2352) + 2; zeroSectorsAmount = 0; - if (CurrentSector + sectorsToRead > Image.Info.Sectors) + if(CurrentSector + sectorsToRead > Image.Info.Sectors) { ulong oldSectorsToRead = sectorsToRead; - sectorsToRead = Image.Info.Sectors - CurrentSector; - zeroSectorsAmount = oldSectorsToRead - sectorsToRead; + sectorsToRead = Image.Info.Sectors - CurrentSector; + zeroSectorsAmount = oldSectorsToRead - sectorsToRead; } - if (sectorsToRead <= 0) - { - LoadTrack(0); - currentSectorReadPosition = 0; - } - } while (sectorsToRead <= 0); + if(sectorsToRead > 0) + continue; - byte[] zeroSectors = new Byte[zeroSectorsAmount * 2352]; + LoadTrack(0); + currentSectorReadPosition = 0; + } while(sectorsToRead <= 0); + + byte[] zeroSectors = new byte[zeroSectorsAmount * 2352]; Array.Clear(zeroSectors, 0, zeroSectors.Length); byte[] audioData; Task task = Task.Run(() => { - lock (readingImage) + lock(readingImage) { try { return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray(); } - catch (System.ArgumentOutOfRangeException) + catch(ArgumentOutOfRangeException) { LoadTrack(0); + return Image.ReadSectors(CurrentSector, (uint)sectorsToRead).Concat(zeroSectors).ToArray(); } } }); - if (task.Wait(TimeSpan.FromMilliseconds(100))) + if(task.Wait(TimeSpan.FromMilliseconds(100))) { audioData = task.Result; } else { Array.Clear(buffer, offset, count); + return count; } Task.Run(() => { - lock (readingImage) + lock(readingImage) { Image.ReadSector(CurrentSector + 375); } @@ -314,14 +301,14 @@ namespace RedBookPlayer byte[] audioDataSegment = new byte[count]; Array.Copy(audioData, currentSectorReadPosition, audioDataSegment, 0, count); - if (ApplyDeEmphasis) + if(ApplyDeEmphasis) { 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++) + 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]); @@ -333,11 +320,12 @@ namespace RedBookPlayer Array.Copy(audioDataSegment, 0, buffer, offset, count); currentSectorReadPosition += count; - if (currentSectorReadPosition >= 2352) - { - CurrentSector += (ulong)currentSectorReadPosition / 2352; - currentSectorReadPosition %= 2352; - } + + if(currentSectorReadPosition < 2352) + return count; + + CurrentSector += (ulong)currentSectorReadPosition / 2352; + currentSectorReadPosition %= 2352; return count; } @@ -354,10 +342,8 @@ namespace RedBookPlayer public void Play() { - if (Image == null) - { + if(Image == null) return; - } soundOut.Play(); TotalIndexes = Image.Tracks[CurrentTrack].Indexes.Keys.Max(); @@ -365,20 +351,16 @@ namespace RedBookPlayer public void Pause() { - if (Image == null) - { + if(Image == null) return; - } soundOut.Stop(); } public void Stop() { - if (Image == null) - { + if(Image == null) return; - } soundOut.Stop(); LoadTrack(CurrentTrack); @@ -386,46 +368,34 @@ namespace RedBookPlayer public void NextTrack() { - if (Image == null) - { + if(Image == null) return; - } - if (CurrentTrack + 1 >= Image.Tracks.Count) - { + if(CurrentTrack + 1 >= Image.Tracks.Count) CurrentTrack = 0; - } else - { CurrentTrack++; - } LoadTrack(CurrentTrack); } public void PreviousTrack() { - if (Image == null) - { + if(Image == null) return; - } - if (CurrentSector < (ulong)Image.Tracks[CurrentTrack].Indexes[1] + 75) + if(CurrentSector < (ulong)Image.Tracks[CurrentTrack].Indexes[1] + 75) { - if (App.Settings.AllowSkipHiddenTrack && CurrentTrack == 0 && CurrentSector >= 75) - { + if(App.Settings.AllowSkipHiddenTrack && + CurrentTrack == 0 && + CurrentSector >= 75) CurrentSector = 0; - } else { - if (CurrentTrack - 1 < 0) - { + if(CurrentTrack - 1 < 0) CurrentTrack = Image.Tracks.Count - 1; - } else - { CurrentTrack--; - } } } @@ -434,120 +404,95 @@ namespace RedBookPlayer public void NextIndex(bool changeTrack) { - if (Image == null) - { + if(Image == null) return; - } - if (CurrentIndex + 1 > Image.Tracks[CurrentTrack].Indexes.Keys.Max()) + if(CurrentIndex + 1 > Image.Tracks[CurrentTrack].Indexes.Keys.Max()) { - if (changeTrack) - { - NextTrack(); - CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Min(); - } + if(!changeTrack) + return; + + NextTrack(); + CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Min(); } else - { CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[++CurrentIndex]; - } } public void PreviousIndex(bool changeTrack) { - if (Image == null) - { + if(Image == null) return; - } - if (CurrentIndex - 1 < Image.Tracks[CurrentTrack].Indexes.Keys.Min()) + if(CurrentIndex - 1 < Image.Tracks[CurrentTrack].Indexes.Keys.Min()) { - if (changeTrack) - { - PreviousTrack(); - CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Max(); - } + if(!changeTrack) + return; + + PreviousTrack(); + CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes.Values.Max(); } else - { CurrentSector = (ulong)Image.Tracks[CurrentTrack].Indexes[--CurrentIndex]; - } } public void FastForward() { - if (Image == null) - { + if(Image == null) return; - } CurrentSector = Math.Min(Image.Info.Sectors - 1, CurrentSector + 75); } public void Rewind() { - if (Image == null) - { + if(Image == null) return; - } - if (CurrentSector >= 75) + if(CurrentSector >= 75) CurrentSector -= 75; } - public void EnableDeEmphasis() - { - ApplyDeEmphasis = true; - } + public void EnableDeEmphasis() => ApplyDeEmphasis = true; - public void DisableDeEmphasis() - { - ApplyDeEmphasis = false; - } + public void DisableDeEmphasis() => ApplyDeEmphasis = false; } 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) + readonly ReadFunction read; + + public bool Run = true; + + public PlayerSource(ReadFunction read) => this.read = read; + + public WaveFormat WaveFormat => new WaveFormat(); + bool IAudioSource.CanSeek => throw new NotImplementedException(); + + public long Position { - this.read = read; + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); } + public long Length => throw new NotImplementedException(); + public int Read(byte[] buffer, int offset, int count) { - if (!Run) - { - Array.Clear(buffer, offset, count); - return count; - } - else - { + if(Run) return read(buffer, offset, count); - } + + Array.Clear(buffer, offset, count); + + return count; } - public void Start() - { - Run = true; - } + public void Dispose() {} - public void Stop() - { - Run = false; - } + public void Start() => Run = true; - public void Dispose() - { - } + public void Stop() => Run = false; } -} +} \ No newline at end of file diff --git a/RedBookPlayer/PlayerView.xaml b/RedBookPlayer/PlayerView.xaml index 5119186..7b0f528 100644 --- a/RedBookPlayer/PlayerView.xaml +++ b/RedBookPlayer/PlayerView.xaml @@ -1,10 +1,7 @@ - + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" + x:Class="RedBookPlayer.PlayerView" Width="900" Height="400"> @@ -24,28 +21,28 @@ TRACK - - + + INDEX - - + + TIME - - - - - - - - + + + + + + + + @@ -53,34 +50,40 @@ TRACKS - - + + INDEXES - - + + TOTAL - - - - - - - - + + + + + + + + - - + + AUDIO @@ -95,4 +98,4 @@ HIDDEN - + \ No newline at end of file diff --git a/RedBookPlayer/PlayerView.xaml.cs b/RedBookPlayer/PlayerView.xaml.cs index 083c209..3a23296 100644 --- a/RedBookPlayer/PlayerView.xaml.cs +++ b/RedBookPlayer/PlayerView.xaml.cs @@ -20,33 +20,27 @@ namespace RedBookPlayer { public class PlayerView : UserControl { - public PlayerView() - { - InitializeComponent(null); - } - - public PlayerView(string xaml) - { - InitializeComponent(xaml); - } - public static Player Player = new Player(); - TextBlock currentTrack; - Image[] digits; - Timer updateTimer; + TextBlock currentTrack; + Image[] digits; + Timer updateTimer; + + public PlayerView() => InitializeComponent(null); + + public PlayerView(string xaml) => InitializeComponent(xaml); public async void LoadButton_Click(object sender, RoutedEventArgs e) { string path = await GetPath(); - if (path == null) + if(path == null) { return; } await Task.Run(() => { - AaruFormat image = new AaruFormat(); + var image = new AaruFormat(); IFilter filter = new ZZZNoFilter(); filter.Open(path); image.Open(filter); @@ -57,87 +51,56 @@ namespace RedBookPlayer await Dispatcher.UIThread.InvokeAsync(() => { MainWindow.Instance.Title = "RedBookPlayer - " + path.Split('/').Last().Split('\\').Last(); - } - ); + }); } public async Task GetPath() { - OpenFileDialog dialog = new OpenFileDialog(); + var dialog = new OpenFileDialog(); dialog.AllowMultiple = false; - List knownExtensions = (new Aaru.DiscImages.AaruFormat()).KnownExtensions.ToList(); - dialog.Filters.Add(new FileDialogFilter() + List knownExtensions = new AaruFormat().KnownExtensions.ToList(); + + dialog.Filters.Add(new FileDialogFilter { - Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", + Name = "Aaru Image Format (*" + string.Join(", *", knownExtensions) + ")", Extensions = knownExtensions.ConvertAll(e => e.Substring(1)) - } - ); + }); - return (await dialog.ShowAsync((Window)this.Parent.Parent))?.FirstOrDefault(); + return (await dialog.ShowAsync((Window)Parent.Parent))?.FirstOrDefault(); } - public void PlayButton_Click(object sender, RoutedEventArgs e) - { - Player.Play(); - } + public void PlayButton_Click(object sender, RoutedEventArgs e) => Player.Play(); - public void PauseButton_Click(object sender, RoutedEventArgs e) - { - Player.Pause(); - } + public void PauseButton_Click(object sender, RoutedEventArgs e) => Player.Pause(); - public void StopButton_Click(object sender, RoutedEventArgs e) - { - Player.Stop(); - } + 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) => Player.NextTrack(); - public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) - { - Player.PreviousTrack(); - } + public void PreviousTrackButton_Click(object sender, RoutedEventArgs e) => Player.PreviousTrack(); - public void NextIndexButton_Click(object sender, RoutedEventArgs e) - { + public void NextIndexButton_Click(object sender, RoutedEventArgs e) => Player.NextIndex(App.Settings.IndexButtonChangeTrack); - } - public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) - { + public void PreviousIndexButton_Click(object sender, RoutedEventArgs e) => Player.PreviousIndex(App.Settings.IndexButtonChangeTrack); - } - public void FastForwardButton_Click(object sender, RoutedEventArgs e) - { - Player.FastForward(); - } + public void FastForwardButton_Click(object sender, RoutedEventArgs e) => Player.FastForward(); - public void RewindButton_Click(object sender, RoutedEventArgs e) - { - Player.Rewind(); - } + public void RewindButton_Click(object sender, RoutedEventArgs e) => Player.Rewind(); - public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) - { - Player.EnableDeEmphasis(); - } + public void EnableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.EnableDeEmphasis(); - public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) - { - Player.DisableDeEmphasis(); - } + public void DisableDeEmphasisButton_Click(object sender, RoutedEventArgs e) => Player.DisableDeEmphasis(); - private void UpdateView(object sender, ElapsedEventArgs e) + void UpdateView(object sender, ElapsedEventArgs e) { - if (Player.Initialized) + if(Player.Initialized) { ulong sectorTime = Player.CurrentSector; - if (Player.SectionStartSector != 0) + + if(Player.SectionStartSector != 0) { sectorTime -= Player.SectionStartSector; } @@ -146,47 +109,41 @@ namespace RedBookPlayer sectorTime += Player.TimeOffset; } - int[] numbers = new int[]{ - Player.CurrentTrack + 1, - Player.CurrentIndex, - (int)(sectorTime / (75 * 60)), - (int)((sectorTime / 75) % 60), - (int)(sectorTime % 75), - Player.TotalTracks, - Player.TotalIndexes, - (int)(Player.TotalTime / (75 * 60)), - (int)((Player.TotalTime / 75) % 60), - (int)(Player.TotalTime % 75), + int[] numbers = + { + Player.CurrentTrack + 1, Player.CurrentIndex, (int)(sectorTime / (75 * 60)), + (int)(sectorTime / 75 % 60), (int)(sectorTime % 75), Player.TotalTracks, Player.TotalIndexes, + (int)(Player.TotalTime / (75 * 60)), (int)(Player.TotalTime / 75 % 60), (int)(Player.TotalTime % 75) }; - string digitString = String.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); + string digitString = string.Join("", numbers.Select(i => i.ToString().PadLeft(2, '0').Substring(0, 2))); Dispatcher.UIThread.InvokeAsync(() => { - for (int i = 0; i < digits.Length; i++) + for(int i = 0; i < digits.Length; i++) { - if (digits[i] != null) + if(digits[i] != null) { digits[i].Source = GetBitmap(digitString[i]); } } - PlayerViewModel dataContext = (PlayerViewModel)DataContext; - dataContext.HiddenTrack = Player.TimeOffset > 150; - dataContext.ApplyDeEmphasis = Player.ApplyDeEmphasis; + var dataContext = (PlayerViewModel)DataContext; + dataContext.HiddenTrack = Player.TimeOffset > 150; + dataContext.ApplyDeEmphasis = Player.ApplyDeEmphasis; dataContext.TrackHasEmphasis = Player.TrackHasEmphasis; - dataContext.CopyAllowed = Player.CopyAllowed; - dataContext.IsAudioTrack = Player.TrackType_ == Player.TrackType.Audio; - dataContext.IsDataTrack = Player.TrackType_ == Player.TrackType.Data; + dataContext.CopyAllowed = Player.CopyAllowed; + dataContext.IsAudioTrack = Player.TrackType_ == Player.TrackType.Audio; + dataContext.IsDataTrack = Player.TrackType_ == Player.TrackType.Data; }); } else { Dispatcher.UIThread.InvokeAsync(() => { - foreach (Image digit in digits) + foreach(Image digit in digits) { - if (digit != null) + if(digit != null) { digit.Source = GetBitmap('-'); } @@ -195,23 +152,24 @@ namespace RedBookPlayer } } - private Bitmap GetBitmap(char character) + Bitmap GetBitmap(char character) { - if (App.Settings.SelectedTheme == "default") + if(App.Settings.SelectedTheme == "default") { IAssetLoader assets = AvaloniaLocator.Current.GetService(); + return new Bitmap(assets.Open(new Uri($"avares://RedBookPlayer/Assets/{character}.png"))); } - else + + string themeDirectory = Directory.GetCurrentDirectory() + "/themes/" + App.Settings.SelectedTheme; + Bitmap bitmap; + + using(FileStream stream = File.Open(themeDirectory + $"/{character}.png", FileMode.Open)) { - string themeDirectory = Directory.GetCurrentDirectory() + "/themes/" + App.Settings.SelectedTheme; - Bitmap bitmap; - using (FileStream stream = File.Open(themeDirectory + $"/{character}.png", FileMode.Open)) - { - bitmap = new Bitmap(stream); - } - return bitmap; + bitmap = new Bitmap(stream); } + + return bitmap; } public void Initialize() @@ -247,11 +205,11 @@ namespace RedBookPlayer currentTrack = this.FindControl("CurrentTrack"); } - private void InitializeComponent(string xaml) + void InitializeComponent(string xaml) { DataContext = new PlayerViewModel(); - if (xaml != null) + if(xaml != null) { new AvaloniaXamlLoader().Load(xaml, null, this); } @@ -263,17 +221,19 @@ namespace RedBookPlayer Initialize(); updateTimer = new Timer(1000 / 60); + updateTimer.Elapsed += (sender, e) => { try { UpdateView(sender, e); } - catch (Exception ex) + catch(Exception ex) { Console.WriteLine(ex); } }; + updateTimer.AutoReset = true; updateTimer.Start(); } @@ -281,37 +241,43 @@ namespace RedBookPlayer public class PlayerViewModel : ReactiveObject { - private bool applyDeEmphasis; + bool applyDeEmphasis; + bool copyAllowed; + bool hiddenTrack; + bool isAudioTrack; + bool isDataTrack; + bool trackHasEmphasis; + public bool ApplyDeEmphasis { get => applyDeEmphasis; set => this.RaiseAndSetIfChanged(ref applyDeEmphasis, value); } - private bool trackHasEmphasis; + public bool TrackHasEmphasis { get => trackHasEmphasis; set => this.RaiseAndSetIfChanged(ref trackHasEmphasis, value); } - private bool hiddenTrack; + public bool HiddenTrack { get => hiddenTrack; set => this.RaiseAndSetIfChanged(ref hiddenTrack, value); } - private bool copyAllowed; + public bool CopyAllowed { get => copyAllowed; set => this.RaiseAndSetIfChanged(ref copyAllowed, value); } - private bool isAudioTrack; + public bool IsAudioTrack { get => isAudioTrack; set => this.RaiseAndSetIfChanged(ref isAudioTrack, value); } - private bool isDataTrack; + public bool IsDataTrack { get => isDataTrack; diff --git a/RedBookPlayer/Program.cs b/RedBookPlayer/Program.cs index 30db9dc..64edf12 100644 --- a/RedBookPlayer/Program.cs +++ b/RedBookPlayer/Program.cs @@ -1,28 +1,24 @@ -using System.Runtime.InteropServices; -using Avalonia; +using Avalonia; using Avalonia.Logging.Serilog; namespace RedBookPlayer { - class Program + internal class Program { public static void Main(string[] args) { -#if Windows + #if Windows AllocConsole(); -#endif + #endif BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } -#if Windows + #if Windows [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AllocConsole(); -#endif + #endif - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .LogToDebug(); + public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure().UsePlatformDetect().LogToDebug(); } -} +} \ No newline at end of file diff --git a/RedBookPlayer/Settings.cs b/RedBookPlayer/Settings.cs index 5d2a212..0411c1d 100644 --- a/RedBookPlayer/Settings.cs +++ b/RedBookPlayer/Settings.cs @@ -6,23 +6,21 @@ namespace RedBookPlayer { public class Settings { - public bool AutoPlay { get; set; } = false; - public bool IndexButtonChangeTrack { get; set; } = false; - public bool AllowSkipHiddenTrack { get; set; } = false; - public int Volume { get; set; } = 100; - public string SelectedTheme { get; set; } = "default"; string filePath; - public Settings() { } + public Settings() {} - public Settings(string filePath) - { - this.filePath = filePath; - } + public Settings(string filePath) => this.filePath = filePath; + + public bool AutoPlay { get; set; } + public bool IndexButtonChangeTrack { get; set; } + public bool AllowSkipHiddenTrack { get; set; } + public int Volume { get; set; } = 100; + public string SelectedTheme { get; set; } = "default"; public static Settings Load(string filePath) { - if (File.Exists(filePath)) + if(File.Exists(filePath)) { try { @@ -33,21 +31,20 @@ namespace RedBookPlayer return settings; } - catch (JsonException) + catch(JsonException) { Console.WriteLine("Couldn't parse settings, reverting to default"); + return new Settings(filePath); } } - else - { - return new Settings(filePath); - } + + return new Settings(filePath); } public void Save() { - JsonSerializerOptions options = new JsonSerializerOptions() + var options = new JsonSerializerOptions { WriteIndented = true }; diff --git a/RedBookPlayer/SettingsWindow.xaml b/RedBookPlayer/SettingsWindow.xaml index 77bd752..0760065 100644 --- a/RedBookPlayer/SettingsWindow.xaml +++ b/RedBookPlayer/SettingsWindow.xaml @@ -1,34 +1,32 @@ - + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" + d:DesignHeight="450" x:Class="RedBookPlayer.SettingsWindow" Title="Settings" Width="450" Height="600"> Themes - + Auto-play CD on load - + Index navigation can change track - + Treat index 0 of track 1 as track 0 (hidden track) Volume - - - + + + - + - + \ No newline at end of file diff --git a/RedBookPlayer/SettingsWindow.xaml.cs b/RedBookPlayer/SettingsWindow.xaml.cs index 3b954bf..4873089 100644 --- a/RedBookPlayer/SettingsWindow.xaml.cs +++ b/RedBookPlayer/SettingsWindow.xaml.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using Avalonia.Controls; @@ -9,21 +8,21 @@ namespace RedBookPlayer { public class SettingsWindow : Window { - Settings settings; - ListBox themeList; - string selectedTheme; + readonly Settings settings; + string selectedTheme; + ListBox themeList; - public SettingsWindow() { } + public SettingsWindow() {} public SettingsWindow(Settings settings) { - this.DataContext = this.settings = settings; + DataContext = this.settings = settings; InitializeComponent(); } public void ThemeList_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (e.AddedItems.Count == 0) + if(e.AddedItems.Count == 0) { return; } @@ -33,7 +32,7 @@ namespace RedBookPlayer public void ApplySettings(object sender, RoutedEventArgs e) { - if ((selectedTheme ?? "") != "") + if((selectedTheme ?? "") != "") { settings.SelectedTheme = selectedTheme; MainWindow.ApplyTheme(selectedTheme); @@ -44,28 +43,25 @@ namespace RedBookPlayer settings.Save(); } - public void UpdateView() - { - this.FindControl("VolumeLabel").Text = settings.Volume.ToString(); - } + public void UpdateView() => this.FindControl("VolumeLabel").Text = settings.Volume.ToString(); - private void InitializeComponent() + void InitializeComponent() { AvaloniaXamlLoader.Load(this); - themeList = this.FindControl("ThemeList"); + themeList = this.FindControl("ThemeList"); themeList.SelectionChanged += ThemeList_SelectionChanged; - List items = new List(); + List items = new List(); items.Add("default"); - if (Directory.Exists("themes/")) + if(Directory.Exists("themes/")) { - foreach (string dir in Directory.EnumerateDirectories("themes/")) + foreach(string dir in Directory.EnumerateDirectories("themes/")) { string themeName = dir.Split('/')[1]; - if (!File.Exists($"themes/{themeName}/view.xaml")) + if(!File.Exists($"themes/{themeName}/view.xaml")) { continue; } @@ -76,7 +72,7 @@ namespace RedBookPlayer themeList.Items = items; - this.FindControl