ソースを参照

WIP DFF Reading

Coder 8 ヶ月 前
コミット
b40b3175a3

+ 115 - 0
Bmp.Core/Common/Utility/MarshalExtension.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bmp.Core.Common.Utility
+{
+    public static class MarshalExtension
+    {
+        public static byte[] ReadBytes(this Stream stream, int count)
+        {
+            var buffer = new byte[count];
+            var bytesRead = 0;
+            while (bytesRead < buffer.Length)
+            {
+                var bytesToRead = buffer.Length - bytesRead;
+                var n = stream.Read(buffer, bytesRead, bytesToRead);
+                if (n == 0) break;
+                bytesRead += n;
+            }
+            return buffer;
+        }
+
+        public static T ReadStruct<T>(this Stream stream)
+        {
+            var buffer = stream.ReadBytes(Marshal.SizeOf(typeof(T)));
+ 
+            var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+            var result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T))!;
+            handle.Free();
+
+            return result;
+        }
+
+        public static void ReadStruct<T>(this Stream stream, ref T result)
+        {
+            var buffer = stream.ReadBytes(Marshal.SizeOf(typeof(T)));
+          
+            var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+            result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T))!;
+            handle.Free();
+        }
+
+        public static long NetworkOrderToHostOrder(this long value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToInt64(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+        public static ulong NetworkOrderToHostOrder(this ulong value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToUInt64(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+
+        public static int NetworkOrderToHostOrder(this int value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToInt32(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+        public static uint NetworkOrderToHostOrder(this uint value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToUInt32(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+
+        public static short NetworkOrderToHostOrder(this short value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToInt16(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+        public static ushort NetworkOrderToHostOrder(this ushort value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                var bytes = BitConverter.GetBytes(value);
+                Array.Reverse(bytes); // 反转字节数组以转换为本机端序
+                return BitConverter.ToUInt16(bytes, 0);
+            }
+
+            return value; // 本机端序和大端序相同,无需转换
+        }
+    }
+}

+ 135 - 46
Bmp.Core/Playback/Inputs/DffSourceStream.cs

@@ -1,68 +1,157 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.IO;
 using System.Runtime.InteropServices;
 using System.Text;
-using System.Threading.Tasks;
-using Bmp.Core.FFMpeg.CsCorePorts;
-using NAudio.Wave;
+using Bmp.Core.Common.Utility;
+using static System.Net.WebRequestMethods;
 using WaveFormat = NAudio.Wave.WaveFormat;
 
-namespace Bmp.Core.Playback.Inputs
+namespace Bmp.Core.Playback.Inputs;
+
+public class DffSourceStream : DsdAbstractSourceStream
 {
-    internal class DffSourceStream : WaveStream
-    {
+    private const string ID_FRM8 = "FRM8";
+    private const string ID_DSD = "DSD ";
+    private const string ID_SND = "SND ";
+    private const string ID_FS = "FS  ";
+    private const string ID_CHNL = "CHNL";
+    private const string ID_CMPR = "CMPR";
+    private const string ID_DST = "DST ";
+    private const string ID_LSCO = "LSCO";
 
-        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-        public struct ID
-        {
-            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
-            public string ckID;
-        }
 
-        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-        public struct Chunk
-        {
-            public ID id;
-            public ulong ckDataSize;
-        }
+    private const int FILE_VERSION = 0x01050000;
 
-        public static bool IsDffFile(Stream underlyingStream)
+    public static bool IsDffFile(Stream stream)
+    {
+        if (stream.Length > 4)
         {
-            //TODO: DffSourceStream::IsDffFile
-            return false;
+            using var binaryReader = new BinaryReader(stream, Encoding.UTF8, true);
+            var signatureDSD = Encoding.ASCII.GetString(binaryReader.ReadBytes(4));
+            if (signatureDSD == ID_FRM8) return true;
         }
+        return false;
+    }
 
+    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
+    public struct ID
+    {
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
+        public string Value;
+    }
 
-        private readonly Stream _underlyingStream;
-        private readonly bool _disposeStream;
+    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
+    public struct Chunk
+    {
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
+        public string Id;
+        private long chunkDataSize;
+        public long DataSize => chunkDataSize.NetworkOrderToHostOrder();
+    }
 
-        public DffSourceStream(string urlOrPath) : this(InputSourceProvider.ReadContentAsSeekableStream(urlOrPath), true)
-        {
-        }
+    private readonly Stream _underlyingStream;
+    private readonly bool _disposeStream;
+    private readonly int _fileVersion;
+    private readonly int _sampleRate;
+    private readonly short _channelCount;
+    private readonly IReadOnlyCollection<string> _channelIds;
+    private readonly bool _isDstEncoded;
 
-        public DffSourceStream(Stream underlyingStream, bool disposeStream)
-        {
-            _underlyingStream = underlyingStream;
-            _disposeStream = disposeStream;
-        }
+    public DffSourceStream(string urlOrPath) : this(InputSourceProvider.ReadContentAsSeekableStream(urlOrPath), true)
+    {
+    }
 
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-            if (_disposeStream) _underlyingStream.Dispose();
-        }
+    public DffSourceStream(Stream underlyingStream, bool disposeStream)
+    {
+        _underlyingStream = underlyingStream;
+        _disposeStream = disposeStream;
 
+        var ck = new Chunk();
+        var sizeOfCk = Marshal.SizeOf<Chunk>();
+        var sizeOfId = Marshal.SizeOf<ID>();
 
-    
+        if (underlyingStream.Length < sizeOfCk) throw new InvalidDataException("too small as dff file");
+        underlyingStream.ReadStruct(ref ck);
+        if (ck.Id != ID_FRM8) throw new InvalidDataException("invalid FRM8 signature");
 
-        public override int Read(byte[] buffer, int offset, int count)
+        var frm8Size = ck.DataSize;
+        var id = underlyingStream.ReadStruct<ID>();
+        if (id.Value != ID_DSD) throw new InvalidDataException("invalid DSD signature");
+
+        while (_underlyingStream.Position < (frm8Size + sizeOfCk))
         {
-            throw new NotImplementedException();
+            underlyingStream.ReadStruct(ref ck);
+            switch ((ck.Id, ck.DataSize))
+            {
+                case ("FVER", 4):
+                    _fileVersion = _underlyingStream.ReadStruct<int>().NetworkOrderToHostOrder();
+                    if (_fileVersion != FILE_VERSION) throw new InvalidDataException("invalid File version");
+                    continue;
+            }
+
+            if (ck.Id == "PROP")
+            {
+                underlyingStream.ReadStruct(ref id);
+                if (id.Value != ID_SND) throw new InvalidDataException("invalid SND signature");
+                var id_prop_end = _underlyingStream.Position - sizeOfId + ck.DataSize;
+                while (_underlyingStream.Position < id_prop_end)
+                {
+                    underlyingStream.ReadStruct(ref ck);
+                    switch (ck.Id, ck.DataSize)
+                    {
+                        case (ID_FS, 4):
+                            _sampleRate = _underlyingStream.ReadStruct<int>().NetworkOrderToHostOrder();
+                            break;
+                        case (ID_LSCO, 2):
+                            //_loudspeakerConfig
+                            break;
+                        default:
+
+                            switch (ck.Id)
+                            {
+                                case ID_CHNL:
+                                    _channelCount = _underlyingStream.ReadStruct<short>().NetworkOrderToHostOrder();
+                                    _channelIds = Enumerable.Range(0, _channelCount).Select(_ => _underlyingStream.ReadStruct<ID>().Value).ToArray();
+                                    break;
+                                case ID_CMPR:
+                                    var compressionId = _underlyingStream.ReadStruct<ID>().Value;
+                                    _isDstEncoded = compressionId == ID_DST;
+                                    var len = _underlyingStream.ReadStruct<byte>();
+                                    var text = Encoding.UTF8.GetString(_underlyingStream.ReadBytes(len));
+                                    break;
+                                default:
+                                    _underlyingStream.Seek(ck.DataSize, SeekOrigin.Current);
+                                    break;
+                            }
+                            break;
+
+                    }
+
+                    //pad byte char with value 0, used for making chunks an even length. 
+                    _underlyingStream.Seek(_underlyingStream.Position % 2, SeekOrigin.Current);
+
+                }
+
+
+                continue;
+            }
+
+            //skip unknown chunk
+            underlyingStream.Seek(ck.DataSize, SeekOrigin.Current);
         }
+    }
 
-        public override WaveFormat WaveFormat { get; }
-        public override long Length { get; }
-        public override long Position { get; set; }
+    protected override void Dispose(bool disposing)
+    {
+        base.Dispose(disposing);
+        if (_disposeStream) _underlyingStream.Dispose();
     }
-}
+
+    public override int Read(byte[] buffer, int offset, int count)
+    {
+        throw new NotImplementedException();
+    }
+
+    public override WaveFormat WaveFormat { get; }
+    public override long Length { get; }
+    public override long Position { get; set; }
+}

+ 5 - 0
Bmp.Core/Playback/Inputs/DsdAbstractSourceStream.cs

@@ -0,0 +1,5 @@
+using NAudio.Wave;
+
+namespace Bmp.Core.Playback.Inputs;
+
+public abstract class DsdAbstractSourceStream : WaveStream { }

+ 174 - 182
Bmp.Core/Playback/Inputs/DsfSourceStream.cs

@@ -1,235 +1,227 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Bmp.Core.NAudioExt;
+using System.Text;
 using NAudio.Wave;
 
-namespace Bmp.Core.Playback.Inputs
-{
-    public class DsfSourceStream : WaveStream
-    {
-        public const string SIGNATURE_DSD = "DSD ";
-        private const string SIGNATURE_FMT = "fmt ";
-        private const string SIGNATURE_DATA = "data";
-
+namespace Bmp.Core.Playback.Inputs;
 
+public class DsfSourceStream : DsdAbstractSourceStream
+{
+    public const string SIGNATURE_DSD = "DSD ";
+    private const string SIGNATURE_FMT = "fmt ";
+    private const string SIGNATURE_DATA = "data";
 
-        private readonly Stream _underlyingSeekableStream;
-        private readonly bool _disposeStream;
+    private readonly Stream _underlyingStream;
+    private readonly bool _disposeStream;
 
-        private readonly long _dataChunkBeginOffset;
-        private readonly long _dataChunkEndOffset;
-        private readonly long _dataSize;
-        private readonly long _pointerToMetadataChunk;
+    private readonly long _dataChunkBeginOffset;
+    private readonly long _dataChunkEndOffset;
+    private readonly long _dataSize;
+    private readonly long _pointerToMetadataChunk;
 
-        public int ChannelNum { get; }
-        public int SampleRate { get; }
-        public int BlockSizePerChannel { get; }
-        public int BitPerSample { get; }
+    public int ChannelNum { get; }
+    public int SampleRate { get; }
+    public int BlockSizePerChannel { get; }
+    public int BitPerSample { get; }
 
-        public override WaveFormat WaveFormat { get; }
+    public override WaveFormat WaveFormat { get; }
 
-        private readonly byte[] _blockBuf;
-        private int _blockBufSize;
-        private int _blockPtrByChannel;
+    private readonly byte[] _blockBuf;
+    private int _blockBufSize;
+    private int _blockPtrByChannel;
 
-        public static bool IsDsfFile(Stream stream)
+    public static bool IsDsfFile(Stream stream)
+    {
+        if (stream.Length > 4)
         {
-            if (stream.Length > 4)
-            {
-                using var binaryReader = new BinaryReader(stream, Encoding.UTF8, true);
-                var signatureDSD = Encoding.ASCII.GetString(binaryReader.ReadBytes(4));
-                if (signatureDSD == SIGNATURE_DSD) return true;
-            }
-            return false;
+            using var binaryReader = new BinaryReader(stream, Encoding.UTF8, true);
+            var signatureDSD = Encoding.ASCII.GetString(binaryReader.ReadBytes(4));
+            if (signatureDSD == SIGNATURE_DSD) return true;
         }
+        return false;
+    }
 
-        public DsfSourceStream(string urlOrPath) : this(InputSourceProvider.ReadContentAsSeekableStream(urlOrPath), true)
-        {
-        }
+    public DsfSourceStream(string urlOrPath) : this(InputSourceProvider.ReadContentAsSeekableStream(urlOrPath), true)
+    {
+    }
 
-        public DsfSourceStream(Stream underlyingSeekableStream, bool disposeStream)
-        {
-            if (underlyingSeekableStream.CanSeek == false) throw new ArgumentException("stream not seekable");
-            _underlyingSeekableStream = underlyingSeekableStream;
-            _disposeStream = disposeStream;
-
-            if (_underlyingSeekableStream.Length < 4) throw new InvalidDataException("too small as dsf file");
-
-            _underlyingSeekableStream.Position = 0;
-
-            using var reader = new BinaryReader(_underlyingSeekableStream, Encoding.UTF8, true);
-            var dsdSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
-            if (SIGNATURE_DSD != dsdSignature) throw new InvalidDataException("invalid DSD signature");
-            var dsdSizeOfThisChunk = reader.ReadInt64();
-            if (dsdSizeOfThisChunk != 28) throw new InvalidDataException("invalid dsd chunk size");
-            var totalFileSize = reader.ReadInt64();
-            if (totalFileSize != _underlyingSeekableStream.Length) throw new InvalidDataException("invalid total file size");
-            _pointerToMetadataChunk = reader.ReadInt64();
-
-            var fmtSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
-            if (SIGNATURE_FMT != fmtSignature) throw new InvalidDataException("invalid FMT signature");
-            var fmtSizeOfThisChunk = reader.ReadInt64();
-            if (fmtSizeOfThisChunk != 52) throw new NotSupportedException("not supported fmt chunk size");
-            var formatVersion = reader.ReadInt32();
-            var formatId = reader.ReadInt32();
-            if (formatId != 0) throw new NotSupportedException("only DSD raw supported");
-
-            var channelType = reader.ReadInt32();
-            ChannelNum = reader.ReadInt32();
-            SampleRate = reader.ReadInt32();
-            BitPerSample = reader.ReadInt32();
-
-            var sampleCount = reader.ReadInt64();
-            BlockSizePerChannel = reader.ReadInt32();
-            var rsv0000 = reader.ReadInt32();
-
-            var dataSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
-            if (SIGNATURE_DATA != dataSignature) throw new InvalidDataException("invalid data signature");
-            var dataSizeOfThisChunk = reader.ReadInt64();
-
-            _dataSize = dataSizeOfThisChunk - 12;
-            _dataChunkBeginOffset = _underlyingSeekableStream.Position;
-            _dataChunkEndOffset = _dataChunkBeginOffset + _dataSize;
-
-            var align = BlockSizePerChannel * ChannelNum;
-            WaveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Unknown, SampleRate, ChannelNum, SampleRate * channelType / 8, align, 1);
-
-            _blockBuf = new byte[align];
-        }
+    public DsfSourceStream(Stream underlyingStream, bool disposeStream)
+    {
+        if (underlyingStream.CanSeek == false) throw new ArgumentException("stream not seekable");
+        _underlyingStream = underlyingStream;
+        _disposeStream = disposeStream;
+
+        if (_underlyingStream.Length < 4) throw new InvalidDataException("too small as dsf file");
+
+        _underlyingStream.Position = 0;
+
+        using var reader = new BinaryReader(_underlyingStream, Encoding.UTF8, true);
+        var dsdSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
+        if (SIGNATURE_DSD != dsdSignature) throw new InvalidDataException("invalid DSD signature");
+        var dsdSizeOfThisChunk = reader.ReadInt64();
+        if (dsdSizeOfThisChunk != 28) throw new InvalidDataException("invalid dsd chunk size");
+        var totalFileSize = reader.ReadInt64();
+        if (totalFileSize != _underlyingStream.Length) throw new InvalidDataException("invalid total file size");
+        _pointerToMetadataChunk = reader.ReadInt64();
+
+        var fmtSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
+        if (SIGNATURE_FMT != fmtSignature) throw new InvalidDataException("invalid FMT signature");
+        var fmtSizeOfThisChunk = reader.ReadInt64();
+        if (fmtSizeOfThisChunk != 52) throw new NotSupportedException("not supported fmt chunk size");
+        var formatVersion = reader.ReadInt32();
+        var formatId = reader.ReadInt32();
+        if (formatId != 0) throw new NotSupportedException("only DSD raw supported");
+
+        var channelType = reader.ReadInt32();
+        ChannelNum = reader.ReadInt32();
+        SampleRate = reader.ReadInt32();
+        BitPerSample = reader.ReadInt32();
+
+        var sampleCount = reader.ReadInt64();
+        BlockSizePerChannel = reader.ReadInt32();
+        var rsv0000 = reader.ReadInt32();
+
+        var dataSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
+        if (SIGNATURE_DATA != dataSignature) throw new InvalidDataException("invalid data signature");
+        var dataSizeOfThisChunk = reader.ReadInt64();
+
+        _dataSize = dataSizeOfThisChunk - 12;
+        _dataChunkBeginOffset = _underlyingStream.Position;
+        _dataChunkEndOffset = _dataChunkBeginOffset + _dataSize;
+
+        var align = BlockSizePerChannel * ChannelNum;
+        WaveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Unknown, SampleRate, ChannelNum, SampleRate * channelType / 8, align, 1);
+
+        _blockBuf = new byte[align];
+    }
 
-        private bool ReadBlock()
-        {
-            //TODO: Rename to  ReadChunk
-            //  block[ChannelNum]
-            //  blockPtr
-            //  blockSize = Chunk/ChannelNum
+    private bool ReadBlock()
+    {
+        //TODO: Rename to  ReadChunk
+        //  block[ChannelNum]
+        //  blockPtr
+        //  blockSize = Chunk/ChannelNum
 
-            var readCount = 0;
-            do
-            {
-                var safeCount = Math.Min(_blockBuf.Length - readCount, _dataChunkEndOffset - _underlyingSeekableStream.Position);
-                if (safeCount <= 0) break;
+        var readCount = 0;
+        do
+        {
+            var safeCount = Math.Min(_blockBuf.Length - readCount, _dataChunkEndOffset - _underlyingStream.Position);
+            if (safeCount <= 0) break;
 
-                var read = _underlyingSeekableStream.Read(_blockBuf, readCount, (int)safeCount);
-                if (read == 0) break;
+            var read = _underlyingStream.Read(_blockBuf, readCount, (int)safeCount);
+            if (read == 0) break;
 
-                readCount += read;
-            } while (readCount < _blockBuf.Length);
+            readCount += read;
+        } while (readCount < _blockBuf.Length);
 
-            _blockBufSize = readCount;
-            _blockPtrByChannel = 0;
+        _blockBufSize = readCount;
+        _blockPtrByChannel = 0;
 
-            return readCount != 0;
-        }
+        return readCount != 0;
+    }
 
-        public override int Read(byte[] buffer, int offset, int countRequired)
-        {
-            if (countRequired == 0) return 0;
+    public override int Read(byte[] buffer, int offset, int countRequired)
+    {
+        if (countRequired == 0) return 0;
 
-            if (countRequired < ChannelNum) throw new ArgumentOutOfRangeException(nameof(countRequired));
-            var mod = countRequired % ChannelNum;
-            var count = countRequired;
-            if (mod != 0) count -= mod;
+        if (countRequired < ChannelNum) throw new ArgumentOutOfRangeException(nameof(countRequired));
+        var mod = countRequired % ChannelNum;
+        var count = countRequired;
+        if (mod != 0) count -= mod;
 
-            //对齐 BlockSizePerChannel 声道交替字节 (FAKE BIT PER SAMPLE 8bit) or ( 16 SHORT/ 32 INT/ 64 LONG)
+        //对齐 BlockSizePerChannel 声道交替字节 (FAKE BIT PER SAMPLE 8bit) or ( 16 SHORT/ 32 INT/ 64 LONG)
 
-            //Source L[BlockSizePerChannel] R[BlockSizePerChannel] L[BlockSizePerChannel] R[BlockSizePerChannel] -> Transfer LR LR LR LR
+        //Source L[BlockSizePerChannel] R[BlockSizePerChannel] L[BlockSizePerChannel] R[BlockSizePerChannel] -> Transfer LR LR LR LR
 
-            if (_blockBufSize == 0)
-            {
-                if (ReadBlock() == false) return 0;
-            }
+        if (_blockBufSize == 0)
+        {
+            if (ReadBlock() == false) return 0;
+        }
 
-            var bytes = count / ChannelNum;
-            var dstPtr = 0;
+        var bytes = count / ChannelNum;
+        var dstPtr = 0;
 
-            //TODO: optimization Unsafe moving prt
+        //TODO: optimization Unsafe moving prt
 
-            for (var s = 0; s < bytes; s++)
+        for (var s = 0; s < bytes; s++)
+        {
+            for (var ch = 0; ch < ChannelNum; ch++)
             {
-                for (var ch = 0; ch < ChannelNum; ch++)
-                {
-                    var srcPtr = ch * BlockSizePerChannel + _blockPtrByChannel;
+                var srcPtr = ch * BlockSizePerChannel + _blockPtrByChannel;
 
-                    buffer[dstPtr] = _blockBuf[srcPtr];
+                buffer[dstPtr] = _blockBuf[srcPtr];
 
-                    ++dstPtr;
-                }
+                ++dstPtr;
+            }
 
-                _blockPtrByChannel++;
+            _blockPtrByChannel++;
 
-                if (_blockPtrByChannel * ChannelNum == _blockBufSize)
-                {
-                    if (!ReadBlock()) break;
-                }
+            if (_blockPtrByChannel * ChannelNum == _blockBufSize)
+            {
+                if (!ReadBlock()) break;
             }
-
-            return dstPtr;
         }
 
-        public override long Seek(long offset, SeekOrigin origin)
-        {
-            var align = offset % (ChannelNum * BlockSizePerChannel);
-            if (align != 0) offset -= align;
+        return dstPtr;
+    }
 
-            var seekPosition = _underlyingSeekableStream.Position;
+    public override long Seek(long offset, SeekOrigin origin)
+    {
+        var align = offset % (ChannelNum * BlockSizePerChannel);
+        if (align != 0) offset -= align;
 
-            switch (origin)
-            {
-                case SeekOrigin.Begin:
-                    seekPosition = _dataChunkBeginOffset + offset;
-                    break;
+        var seekPosition = _underlyingStream.Position;
 
-                case SeekOrigin.Current:
-                    seekPosition += offset;
-                    break;
+        switch (origin)
+        {
+            case SeekOrigin.Begin:
+                seekPosition = _dataChunkBeginOffset + offset;
+                break;
 
-                case SeekOrigin.End:
-                    seekPosition += _dataChunkEndOffset + offset;
-                    break;
+            case SeekOrigin.Current:
+                seekPosition += offset;
+                break;
 
-                default:
-                    throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
-            }
+            case SeekOrigin.End:
+                seekPosition += _dataChunkEndOffset + offset;
+                break;
 
-            if (seekPosition < _dataChunkBeginOffset) seekPosition = _dataChunkBeginOffset;
-            if (seekPosition > _dataChunkEndOffset) seekPosition = _dataChunkEndOffset;
+            default:
+                throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
+        }
 
-            _underlyingSeekableStream.Position = seekPosition;
+        if (seekPosition < _dataChunkBeginOffset) seekPosition = _dataChunkBeginOffset;
+        if (seekPosition > _dataChunkEndOffset) seekPosition = _dataChunkEndOffset;
 
-            //ReadBlock();
+        _underlyingStream.Position = seekPosition;
 
-            return seekPosition;
-        }
+        //ReadBlock();
 
-        public override long Length => _dataSize;
+        return seekPosition;
+    }
 
-        public override long Position
-        {
-            get => _underlyingSeekableStream.Position - _dataChunkBeginOffset;
-            set => Seek(value, SeekOrigin.Begin);
-        }
+    public override long Length => _dataSize;
 
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-            if (_disposeStream) _underlyingSeekableStream.Dispose();
-        }
+    public override long Position
+    {
+        get => _underlyingStream.Position - _dataChunkBeginOffset;
+        set => Seek(value, SeekOrigin.Begin);
+    }
+
+    protected override void Dispose(bool disposing)
+    {
+        base.Dispose(disposing);
+        if (_disposeStream) _underlyingStream.Dispose();
+    }
 
-        public override bool CanRead => true;
-        public override bool CanSeek => true;
+    public override bool CanRead => true;
+    public override bool CanSeek => true;
 
-        // -------------- unused stubs --------------
+    // -------------- unused stubs --------------
 
-        public override void SetLength(long value) => throw new NotSupportedException();
+    public override void SetLength(long value) => throw new NotSupportedException();
 
-        public override void Flush() => throw new NotSupportedException();
+    public override void Flush() => throw new NotSupportedException();
 
-        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
+    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
 
-        public override bool CanWrite => false;
-    }
-}
+    public override bool CanWrite => false;
+}

+ 3 - 3
Bmp.Core/Playback/Inputs/NAudioFFMPEGAudioReader.cs

@@ -6,7 +6,7 @@ using WaveFormat = NAudio.Wave.WaveFormat;
 
 namespace Bmp.Core.Playback.Inputs;
 
-public class NAudioFFMPEGAudioReader : WaveStream, IHaveBitPerRawSample
+public class FFMPEGAudioReader : WaveStream, IHaveBitPerRawSample
 {
     public int? BitPerRawSample => _ffmpegDecoder?.BitPerRawSample;
 
@@ -26,7 +26,7 @@ public class NAudioFFMPEGAudioReader : WaveStream, IHaveBitPerRawSample
         }
     }
 
-    public NAudioFFMPEGAudioReader(string filename)
+    public FFMPEGAudioReader(string filename)
     {
         _ffmpegDecoder = new FfmpegDecoder(filename);
 
@@ -41,7 +41,7 @@ public class NAudioFFMPEGAudioReader : WaveStream, IHaveBitPerRawSample
             WaveFormat = new WaveFormat(sampleRate, bitsPerSample, channels);
     }
 
-    public NAudioFFMPEGAudioReader(Stream stream)
+    public FFMPEGAudioReader(Stream stream)
     {
         _ffmpegDecoder = new FfmpegDecoder(stream);
 

+ 1 - 1
Bmp.Core/Playback/Inputs/InputSourceProvider.cs

@@ -116,7 +116,7 @@ namespace Bmp.Core.Playback.Inputs
             {
                 if (url.IsFile) urlOrPath = url.LocalPath;
             }
-            return new NAudioFFMPEGAudioReader(urlOrPath);
+            return new FFMPEGAudioReader(urlOrPath);
         }
 
         public static (bool exists, long? size) FileCheckSize(string urlOrPath)

+ 14 - 18
Bmp.Core/Playback/Inputs/MetaData.cs

@@ -1,21 +1,17 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+
+namespace Bmp.Core.Playback.Inputs;
 
-namespace Bmp.Core.Playback.Inputs
+public class MetaData
 {
-    public class MetaData
-    {
-        public IReadOnlyDictionary<string, string> RawTags { get; set; }
-        public string? Title => RawTags.TryGetValue("TITLE", out var text) ? text : null;
-        public string? Artist => RawTags.TryGetValue("ARTIST", out var text) ? text : null;
-        public string? Album => RawTags.TryGetValue("ALBUM", out var text) ? text : null;
+    public IReadOnlyDictionary<string, string>? RawTags { get; init; }
+    public TimeSpan Duration { get; init; }
 
-        //TODO: track xx/yy
-        public int? TrackNo => RawTags.TryGetValue("track", out var num) ? int.TryParse(num, out var track) ? track : null : null;
-        public TimeSpan Duration { get; set; }
-        public Exception? Error { get; set; }
-    }
-}
+    public string? Title => RawTags?.TryGetValue("TITLE", out var text) == true ? text : null;
+    public string? Artist => RawTags?.TryGetValue("ARTIST", out var text) == true ? text : null;
+    public string? Album => RawTags?.TryGetValue("ALBUM", out var text) == true ? text : null;
+
+    //TODO: track xx/yy
+    public int? TrackNo => RawTags?.TryGetValue("track", out var num) == true ? int.TryParse(num, out var track) ? track : null : null;
+
+    public Exception? Error { get; set; }
+}

+ 0 - 35
Bmp.Core/Playback/Outputs/BASS_ASIO/ASIOFormat.cs

@@ -1,35 +0,0 @@
-namespace Bmp.Core.Playback.Outputs.BASS_ASIO;
-
-/// <summary>
-/// BassAsio sample formats to be used with <see cref="T:Un4seen.BassAsio.BASS_ASIO_CHANNELINFO" /> and <see cref="M:Un4seen.BassAsio.BassAsio.BASS_ASIO_ChannelGetInfo(System.Boolean,System.Int32,Un4seen.BassAsio.BASS_ASIO_CHANNELINFO)" />.
-/// </summary>
-public enum ASIOFormat
-{
-    /// <summary>Unknown format. Error.</summary>
-    _UNKNOWN = -1, // 0xFFFFFFFF
-
-    /// <summary>16-bit integer.</summary>
-    _16BIT = 16, // 0x00000010
-
-    /// <summary>24-bit integer.</summary>
-    _24BIT = 17, // 0x00000011
-
-    /// <summary>32-bit integer.</summary>
-    _32BIT = 18, // 0x00000012
-
-    /// <summary>32-bit floating-point.</summary>
-    _FLOAT = 19, // 0x00000013
-
-    /// <summary>DSD (LSB 1st)</summary>
-    _DSD_LSB = 32, // 0x00000020
-
-    /// <summary>DSD (MSB 1st)</summary>
-    _DSD_MSB = 33, // 0x00000021
-
-    /// <summary>
-    /// Flag: apply dither (TPDF) when converting from floating-point to integer.
-    /// <para>Dither is optional rather than automatic because it destroys bit-perfect output; you probably don't want to enable it unless you're applying DSP (including volume changes).
-    /// When the output is floating-point (eg. in shared mode), the flag will have no effect.</para>
-    /// </summary>
-    _DITHER = 256, // 0x00000100
-}

+ 1 - 1
Bmp.WinForms/MainForm.cs

@@ -177,7 +177,7 @@ namespace Bmp.WinForms
 
             try
             {
-                if (_inputSource is DsfSourceStream dsfSourceStream)
+                if (_inputSource is DsdAbstractSourceStream)
                 {
                     _nativeDsd = true;
                     //TODO: 匹配原生DSD设备