123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- using System.Text;
- using Bmp.Core.Lite.Playback.Outputs.NAudioASIO.Originals;
- using NAudio.Wave;
- namespace Bmp.Core.Lite.Playback.Inputs;
- public class DsfAudioStream : DsdAudioStream, IHaveDecoderInfo, IHaveAsioSampleFormat
- {
- public const string SIGNATURE_DSD = "DSD ";
- private const string SIGNATURE_FMT = "fmt ";
- private const string SIGNATURE_DATA = "data";
- string IHaveDecoderInfo.DecoderName => "DSF";
- string? IHaveDecoderInfo.FileFormat => $"V{_formatVersion}";
- public AsioSampleType? AsioSampleFormat => AsioSampleType.DSDInt8LSB1;
- private readonly Stream _underlyingStream;
- private readonly bool _disposeStream;
- 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 ClusterSizePerChannel { get; }
- public int BitPerSample { get; }
- public override WaveFormat WaveFormat { get; }
- private readonly byte[] _clusterBuf;
- private int _clusterBufSize;
- private int _clusterPtrByChannel;
- private readonly int _formatVersion;
- public static bool IsDsfFile(Stream stream)
- {
- 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;
- }
- public DsfAudioStream(string urlOrPath) : this(InputSourceProviderLite.ReadContentAsSeekableStream(urlOrPath), true)
- {
- }
- public DsfAudioStream(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");
- _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();
- ClusterSizePerChannel = 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 = ClusterSizePerChannel * ChannelNum;
- WaveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Unknown, SampleRate, ChannelNum, SampleRate * channelType / 8, align, 1);
- _clusterBuf = new byte[align];
- }
- private bool ReadCluster()
- {
- // block[ChannelNum]
- // blockPtr
- // blockSize = Chunk/ChannelNum
- var readCount = 0;
- do
- {
- var safeCount = Math.Min(_clusterBuf.Length - readCount, _dataChunkEndOffset - _underlyingStream.Position);
- if (safeCount <= 0) break;
- var read = _underlyingStream.Read(_clusterBuf, readCount, (int)safeCount);
- if (read == 0) break;
- readCount += read;
- } while (readCount < _clusterBuf.Length);
- _clusterBufSize = readCount;
- _clusterPtrByChannel = 0;
- return readCount != 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;
- //对齐 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
- if (_clusterBufSize == 0)
- {
- if (ReadCluster() == false) return 0;
- }
- var bytes = count / ChannelNum;
- var dstPtr = 0;
- //TODO: DsfAudioStream optimization Unsafe moving ptr
- for (var s = 0; s < bytes; s++)
- {
- for (var ch = 0; ch < ChannelNum; ch++)
- {
- var srcPtr = ch * ClusterSizePerChannel + _clusterPtrByChannel;
- buffer[dstPtr] = _clusterBuf[srcPtr];
- ++dstPtr;
- }
- _clusterPtrByChannel++;
- if (_clusterPtrByChannel * ChannelNum == _clusterBufSize)
- {
- if (!ReadCluster()) break;
- }
- }
- return dstPtr;
- }
- public override long Length => _dataSize;
- public override long Position
- {
- get => _underlyingStream.Position - _dataChunkBeginOffset;
- set => _underlyingStream.Position = _dataChunkBeginOffset + value - value % (ClusterSizePerChannel * ChannelNum);
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (_disposeStream) _underlyingStream.Dispose();
- }
- }
|