DsfAudioStream.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System.Text;
  2. using Bmp.Core.Lite.Playback.Outputs.NAudioASIO.Originals;
  3. using NAudio.Wave;
  4. namespace Bmp.Core.Lite.Playback.Inputs;
  5. public class DsfAudioStream : DsdAudioStream, IHaveDecoderInfo, IHaveAsioSampleFormat
  6. {
  7. public const string SIGNATURE_DSD = "DSD ";
  8. private const string SIGNATURE_FMT = "fmt ";
  9. private const string SIGNATURE_DATA = "data";
  10. string IHaveDecoderInfo.DecoderName => "DSF";
  11. string? IHaveDecoderInfo.FileFormat => $"V{_formatVersion}";
  12. public AsioSampleType? AsioSampleFormat => AsioSampleType.DSDInt8LSB1;
  13. private readonly Stream _underlyingStream;
  14. private readonly bool _disposeStream;
  15. private readonly long _dataChunkBeginOffset;
  16. private readonly long _dataChunkEndOffset;
  17. private readonly long _dataSize;
  18. private readonly long _pointerToMetadataChunk;
  19. public int ChannelNum { get; }
  20. public int SampleRate { get; }
  21. public int ClusterSizePerChannel { get; }
  22. public int BitPerSample { get; }
  23. public override WaveFormat WaveFormat { get; }
  24. private readonly byte[] _clusterBuf;
  25. private int _clusterBufSize;
  26. private int _clusterPtrByChannel;
  27. private readonly int _formatVersion;
  28. public static bool IsDsfFile(Stream stream)
  29. {
  30. if (stream.Length > 4)
  31. {
  32. using var binaryReader = new BinaryReader(stream, Encoding.UTF8, true);
  33. var signatureDSD = Encoding.ASCII.GetString(binaryReader.ReadBytes(4));
  34. if (signatureDSD == SIGNATURE_DSD) return true;
  35. }
  36. return false;
  37. }
  38. public DsfAudioStream(string urlOrPath) : this(InputSourceProviderLite.ReadContentAsSeekableStream(urlOrPath), true)
  39. {
  40. }
  41. public DsfAudioStream(Stream underlyingStream, bool disposeStream)
  42. {
  43. if (underlyingStream.CanSeek == false) throw new ArgumentException("stream not seekable");
  44. _underlyingStream = underlyingStream;
  45. _disposeStream = disposeStream;
  46. if (_underlyingStream.Length < 4) throw new InvalidDataException("too small as dsf file");
  47. _underlyingStream.Position = 0;
  48. using var reader = new BinaryReader(_underlyingStream, Encoding.UTF8, true);
  49. var dsdSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
  50. if (SIGNATURE_DSD != dsdSignature) throw new InvalidDataException("invalid DSD signature");
  51. var dsdSizeOfThisChunk = reader.ReadInt64();
  52. if (dsdSizeOfThisChunk != 28) throw new InvalidDataException("invalid dsd chunk size");
  53. var totalFileSize = reader.ReadInt64();
  54. if (totalFileSize != _underlyingStream.Length) throw new InvalidDataException("invalid total file size");
  55. _pointerToMetadataChunk = reader.ReadInt64();
  56. var fmtSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
  57. if (SIGNATURE_FMT != fmtSignature) throw new InvalidDataException("invalid FMT signature");
  58. var fmtSizeOfThisChunk = reader.ReadInt64();
  59. if (fmtSizeOfThisChunk != 52) throw new NotSupportedException("not supported fmt chunk size");
  60. _formatVersion = reader.ReadInt32();
  61. var formatId = reader.ReadInt32();
  62. if (formatId != 0) throw new NotSupportedException("only DSD raw supported");
  63. var channelType = reader.ReadInt32();
  64. ChannelNum = reader.ReadInt32();
  65. SampleRate = reader.ReadInt32();
  66. BitPerSample = reader.ReadInt32();
  67. var sampleCount = reader.ReadInt64();
  68. ClusterSizePerChannel = reader.ReadInt32();
  69. var rsv0000 = reader.ReadInt32();
  70. var dataSignature = Encoding.ASCII.GetString(reader.ReadBytes(4));
  71. if (SIGNATURE_DATA != dataSignature) throw new InvalidDataException("invalid data signature");
  72. var dataSizeOfThisChunk = reader.ReadInt64();
  73. _dataSize = dataSizeOfThisChunk - 12;
  74. _dataChunkBeginOffset = _underlyingStream.Position;
  75. _dataChunkEndOffset = _dataChunkBeginOffset + _dataSize;
  76. var align = ClusterSizePerChannel * ChannelNum;
  77. WaveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Unknown, SampleRate, ChannelNum, SampleRate * channelType / 8, align, 1);
  78. _clusterBuf = new byte[align];
  79. }
  80. private bool ReadCluster()
  81. {
  82. // block[ChannelNum]
  83. // blockPtr
  84. // blockSize = Chunk/ChannelNum
  85. var readCount = 0;
  86. do
  87. {
  88. var safeCount = Math.Min(_clusterBuf.Length - readCount, _dataChunkEndOffset - _underlyingStream.Position);
  89. if (safeCount <= 0) break;
  90. var read = _underlyingStream.Read(_clusterBuf, readCount, (int)safeCount);
  91. if (read == 0) break;
  92. readCount += read;
  93. } while (readCount < _clusterBuf.Length);
  94. _clusterBufSize = readCount;
  95. _clusterPtrByChannel = 0;
  96. return readCount != 0;
  97. }
  98. public override int Read(byte[] buffer, int offset, int countRequired)
  99. {
  100. if (countRequired == 0) return 0;
  101. if (countRequired < ChannelNum) throw new ArgumentOutOfRangeException(nameof(countRequired));
  102. var mod = countRequired % ChannelNum;
  103. var count = countRequired;
  104. if (mod != 0) count -= mod;
  105. //对齐 BlockSizePerChannel 声道交替字节 (FAKE BIT PER SAMPLE 8bit) or ( 16 SHORT/ 32 INT/ 64 LONG)
  106. //Source L[BlockSizePerChannel] R[BlockSizePerChannel] L[BlockSizePerChannel] R[BlockSizePerChannel] -> Transfer LR LR LR LR
  107. if (_clusterBufSize == 0)
  108. {
  109. if (ReadCluster() == false) return 0;
  110. }
  111. var bytes = count / ChannelNum;
  112. var dstPtr = 0;
  113. //TODO: DsfAudioStream optimization Unsafe moving ptr
  114. for (var s = 0; s < bytes; s++)
  115. {
  116. for (var ch = 0; ch < ChannelNum; ch++)
  117. {
  118. var srcPtr = ch * ClusterSizePerChannel + _clusterPtrByChannel;
  119. buffer[dstPtr] = _clusterBuf[srcPtr];
  120. ++dstPtr;
  121. }
  122. _clusterPtrByChannel++;
  123. if (_clusterPtrByChannel * ChannelNum == _clusterBufSize)
  124. {
  125. if (!ReadCluster()) break;
  126. }
  127. }
  128. return dstPtr;
  129. }
  130. public override long Length => _dataSize;
  131. public override long Position
  132. {
  133. get => _underlyingStream.Position - _dataChunkBeginOffset;
  134. set => _underlyingStream.Position = _dataChunkBeginOffset + value - value % (ClusterSizePerChannel * ChannelNum);
  135. }
  136. protected override void Dispose(bool disposing)
  137. {
  138. base.Dispose(disposing);
  139. if (_disposeStream) _underlyingStream.Dispose();
  140. }
  141. }