|
@@ -4,148 +4,171 @@ using System.IO;
|
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
-namespace SherpaNcnn {
|
|
|
-
|
|
|
-[StructLayout(LayoutKind.Sequential)]
|
|
|
-public struct WaveHeader {
|
|
|
- public Int32 ChunkID;
|
|
|
- public Int32 ChunkSize;
|
|
|
- public Int32 Format;
|
|
|
- public Int32 SubChunk1ID;
|
|
|
- public Int32 SubChunk1Size;
|
|
|
- public Int16 AudioFormat;
|
|
|
- public Int16 NumChannels;
|
|
|
- public Int32 SampleRate;
|
|
|
- public Int32 ByteRate;
|
|
|
- public Int16 BlockAlign;
|
|
|
- public Int16 BitsPerSample;
|
|
|
- public Int32 SubChunk2ID;
|
|
|
- public Int32 SubChunk2Size;
|
|
|
-
|
|
|
- public bool Validate() {
|
|
|
- if (ChunkID != 0x46464952) {
|
|
|
- Console.WriteLine($"Invalid chunk ID: 0x{ChunkID:X}. Expect 0x46464952");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // E V A W
|
|
|
- if (Format != 0x45564157) {
|
|
|
- Console.WriteLine($"Invalid format: 0x{Format:X}. Expect 0x45564157");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // t m f
|
|
|
- if (SubChunk1ID != 0x20746d66) {
|
|
|
- Console.WriteLine($"Invalid SubChunk1ID: 0x{SubChunk1ID:X}. Expect 0x20746d66");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (SubChunk1Size != 16) {
|
|
|
- Console.WriteLine($"Invalid SubChunk1Size: {SubChunk1Size}. Expect 16");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (AudioFormat != 1) {
|
|
|
- Console.WriteLine($"Invalid AudioFormat: {AudioFormat}. Expect 1");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (NumChannels != 1) {
|
|
|
- Console.WriteLine($"Invalid NumChannels: {NumChannels}. Expect 1");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (ByteRate != (SampleRate * NumChannels * BitsPerSample / 8)) {
|
|
|
- Console.WriteLine($"Invalid byte rate: {ByteRate}.");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (BlockAlign != (NumChannels * BitsPerSample / 8)) {
|
|
|
- Console.WriteLine($"Invalid block align: {ByteRate}.");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (BitsPerSample != 16) { // we support only 16 bits per sample
|
|
|
- Console.WriteLine($"Invalid bits per sample: {BitsPerSample}. Expect 16");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// It supports only 16-bit, single channel WAVE format.
|
|
|
-// The sample rate can be any value.
|
|
|
-public class WaveReader {
|
|
|
- public WaveReader(String fileName) {
|
|
|
- if (!File.Exists(fileName)) {
|
|
|
- throw new ApplicationException($"{fileName} does not exist!");
|
|
|
+namespace SherpaNcnn
|
|
|
+{
|
|
|
+
|
|
|
+ [StructLayout(LayoutKind.Sequential)]
|
|
|
+ public struct WaveHeader
|
|
|
+ {
|
|
|
+ public Int32 ChunkID;
|
|
|
+ public Int32 ChunkSize;
|
|
|
+ public Int32 Format;
|
|
|
+ public Int32 SubChunk1ID;
|
|
|
+ public Int32 SubChunk1Size;
|
|
|
+ public Int16 AudioFormat;
|
|
|
+ public Int16 NumChannels;
|
|
|
+ public Int32 SampleRate;
|
|
|
+ public Int32 ByteRate;
|
|
|
+ public Int16 BlockAlign;
|
|
|
+ public Int16 BitsPerSample;
|
|
|
+ public Int32 SubChunk2ID;
|
|
|
+ public Int32 SubChunk2Size;
|
|
|
+
|
|
|
+ public bool Validate()
|
|
|
+ {
|
|
|
+ if (ChunkID != 0x46464952)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid chunk ID: 0x{ChunkID:X}. Expect 0x46464952");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // E V A W
|
|
|
+ if (Format != 0x45564157)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid format: 0x{Format:X}. Expect 0x45564157");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // t m f
|
|
|
+ if (SubChunk1ID != 0x20746d66)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid SubChunk1ID: 0x{SubChunk1ID:X}. Expect 0x20746d66");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (SubChunk1Size != 16)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid SubChunk1Size: {SubChunk1Size}. Expect 16");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (AudioFormat != 1)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid AudioFormat: {AudioFormat}. Expect 1");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NumChannels != 1)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid NumChannels: {NumChannels}. Expect 1");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ByteRate != (SampleRate * NumChannels * BitsPerSample / 8))
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid byte rate: {ByteRate}.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (BlockAlign != (NumChannels * BitsPerSample / 8))
|
|
|
+ {
|
|
|
+ Console.WriteLine($"Invalid block align: {ByteRate}.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (BitsPerSample != 16)
|
|
|
+ { // we support only 16 bits per sample
|
|
|
+ Console.WriteLine($"Invalid bits per sample: {BitsPerSample}. Expect 16");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- using (var stream = File.Open(fileName, FileMode.Open)) {
|
|
|
- using (var reader = new BinaryReader(stream)) {
|
|
|
- _header = ReadHeader(reader);
|
|
|
-
|
|
|
- if(!_header.Validate()) {
|
|
|
- throw new ApplicationException($"Invalid wave file ${fileName}");
|
|
|
+ // It supports only 16-bit, single channel WAVE format.
|
|
|
+ // The sample rate can be any value.
|
|
|
+ public class WaveReader
|
|
|
+ {
|
|
|
+ public WaveReader(String fileName)
|
|
|
+ {
|
|
|
+ if (!File.Exists(fileName))
|
|
|
+ {
|
|
|
+ throw new ApplicationException($"{fileName} does not exist!");
|
|
|
+ }
|
|
|
+
|
|
|
+ using (var stream = File.Open(fileName, FileMode.Open))
|
|
|
+ {
|
|
|
+ using (var reader = new BinaryReader(stream))
|
|
|
+ {
|
|
|
+ _header = ReadHeader(reader);
|
|
|
+
|
|
|
+ if (!_header.Validate())
|
|
|
+ {
|
|
|
+ throw new ApplicationException($"Invalid wave file ${fileName}");
|
|
|
+ }
|
|
|
+
|
|
|
+ SkipMetaData(reader);
|
|
|
+
|
|
|
+ // now read samples
|
|
|
+ // _header.SubChunk2Size contains number of bytes in total.
|
|
|
+ // we assume each sample is of type int16
|
|
|
+ byte[] buffer = reader.ReadBytes(_header.SubChunk2Size);
|
|
|
+ short[] samples_int16 = new short[_header.SubChunk2Size / 2];
|
|
|
+ Buffer.BlockCopy(buffer, 0, samples_int16, 0, buffer.Length);
|
|
|
+
|
|
|
+ _samples = new float[samples_int16.Length];
|
|
|
+
|
|
|
+ for (var i = 0; i < samples_int16.Length; ++i)
|
|
|
+ {
|
|
|
+ _samples[i] = samples_int16[i] / 32768.0F;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- SkipMetaData(reader);
|
|
|
+ private static WaveHeader ReadHeader(BinaryReader reader)
|
|
|
+ {
|
|
|
+ byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(WaveHeader)));
|
|
|
|
|
|
- // now read samples
|
|
|
- // _header.SubChunk2Size contains number of bytes in total.
|
|
|
- // we assume each sample is of type int16
|
|
|
- byte[] buffer = reader.ReadBytes(_header.SubChunk2Size);
|
|
|
- short[] samples_int16 = new short[_header.SubChunk2Size/2];
|
|
|
- Buffer.BlockCopy(buffer, 0, samples_int16, 0, buffer.Length);
|
|
|
+ GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
|
|
+ WaveHeader header = (WaveHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(WaveHeader));
|
|
|
+ handle.Free();
|
|
|
|
|
|
- _samples = new float[samples_int16.Length];
|
|
|
-
|
|
|
- for(var i = 0; i < samples_int16.Length; ++i) {
|
|
|
- _samples[i] = samples_int16[i] / 32768.0F;
|
|
|
+ return header;
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- private static WaveHeader ReadHeader(BinaryReader reader) {
|
|
|
- byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(WaveHeader)));
|
|
|
-
|
|
|
- GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
|
|
- WaveHeader header = (WaveHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(WaveHeader));
|
|
|
- handle.Free();
|
|
|
+ private void SkipMetaData(BinaryReader reader)
|
|
|
+ {
|
|
|
+ var bs = reader.BaseStream;
|
|
|
+
|
|
|
+ Int32 subChunk2ID = _header.SubChunk2ID;
|
|
|
+ Int32 subChunk2Size = _header.SubChunk2Size;
|
|
|
+
|
|
|
+ while (bs.Position != bs.Length && subChunk2ID != 0x61746164)
|
|
|
+ {
|
|
|
+ bs.Seek(subChunk2Size, SeekOrigin.Current);
|
|
|
+ subChunk2ID = reader.ReadInt32();
|
|
|
+ subChunk2Size = reader.ReadInt32();
|
|
|
+ }
|
|
|
+ _header.SubChunk2ID = subChunk2ID;
|
|
|
+ _header.SubChunk2Size = subChunk2Size;
|
|
|
+ }
|
|
|
|
|
|
- return header;
|
|
|
- }
|
|
|
+ private WaveHeader _header;
|
|
|
|
|
|
- private void SkipMetaData(BinaryReader reader) {
|
|
|
- var bs = reader.BaseStream;
|
|
|
+ // Samples are normalized to the range [-1, 1]
|
|
|
+ private float[] _samples;
|
|
|
|
|
|
- Int32 subChunk2ID = _header.SubChunk2ID;
|
|
|
- Int32 subChunk2Size = _header.SubChunk2Size;
|
|
|
+ public int SampleRate => _header.SampleRate;
|
|
|
+ public float[] Samples => _samples;
|
|
|
|
|
|
- while (bs.Position != bs.Length && subChunk2ID != 0x61746164) {
|
|
|
- bs.Seek(subChunk2Size, SeekOrigin.Current);
|
|
|
- subChunk2ID = reader.ReadInt32();
|
|
|
- subChunk2Size = reader.ReadInt32();
|
|
|
+ public static void Test(String fileName)
|
|
|
+ {
|
|
|
+ WaveReader reader = new WaveReader(fileName);
|
|
|
+ Console.WriteLine($"samples length: {reader.Samples.Length}");
|
|
|
+ Console.WriteLine($"samples rate: {reader.SampleRate}");
|
|
|
+ }
|
|
|
}
|
|
|
- _header.SubChunk2ID = subChunk2ID;
|
|
|
- _header.SubChunk2Size = subChunk2Size;
|
|
|
- }
|
|
|
-
|
|
|
- private WaveHeader _header;
|
|
|
-
|
|
|
- // Samples are normalized to the range [-1, 1]
|
|
|
- private float[] _samples;
|
|
|
-
|
|
|
- public int SampleRate => _header.SampleRate;
|
|
|
- public float [] Samples => _samples;
|
|
|
-
|
|
|
- public static void Test(String fileName) {
|
|
|
- WaveReader reader = new WaveReader(fileName);
|
|
|
- Console.WriteLine($"samples length: {reader.Samples.Length}");
|
|
|
- Console.WriteLine($"samples rate: {reader.SampleRate}");
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
}
|