forked from swharden/Spectrogram
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWavFile.cs
More file actions
121 lines (106 loc) · 5.41 KB
/
WavFile.cs
File metadata and controls
121 lines (106 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
using System;
using System.Diagnostics;
using System.IO;
namespace Spectrogram
{
[Obsolete("Use a library like NAudio to extract data from WAV files (see spectrogram quickstart for examples)", true)]
public static class WavFile
{
private static (string id, uint length) ChunkInfo(BinaryReader br, long position)
{
br.BaseStream.Seek(position, SeekOrigin.Begin);
string chunkID = new string(br.ReadChars(4));
uint chunkBytes = br.ReadUInt32();
return (chunkID, chunkBytes);
}
public static (int sampleRate, double[] L) ReadMono(string filePath)
{
(int sampleRate, double[] L, _) = ReadStereo(filePath);
return (sampleRate, L);
}
public static (int sampleRate, double[] L, double[] R) ReadStereo(string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
// The first chunk is RIFF section
// Length should be the number of bytes in the file minus 4
var (id, length) = ChunkInfo(br, 0);
Console.WriteLine($"First chunk '{id}' indicates {length:N0} bytes");
if (id != "RIFF")
throw new InvalidOperationException($"Unsupported WAV format (first chunk ID was '{id}', not 'RIFF')");
// The second chunk is FORMAT section
var fmtChunk = ChunkInfo(br, 12);
Console.WriteLine($"Format chunk '{fmtChunk.id}' indicates {fmtChunk.length:N0} bytes");
if (fmtChunk.id != "fmt ")
throw new InvalidOperationException($"Unsupported WAV format (first chunk ID was '{fmtChunk.id}', not 'fmt ')");
if (fmtChunk.length != 16)
throw new InvalidOperationException($"Unsupported WAV format (expect 16 byte 'fmt' chunk, got {fmtChunk.length} bytes)");
// By now we verified this is probably a valid FORMAT section, so read its values.
int audioFormat = br.ReadUInt16();
Console.WriteLine($"audio format: {audioFormat}");
if (audioFormat != 1)
throw new NotImplementedException("Unsupported WAV format (audio format must be 1, indicating uncompressed PCM data)");
int channelCount = br.ReadUInt16();
Console.WriteLine($"channel count: {channelCount}");
if (channelCount < 0 || channelCount > 2)
throw new NotImplementedException($"Unsupported WAV format (must be 1 or 2 channel, file has {channelCount})");
int sampleRate = (int)br.ReadUInt32();
Console.WriteLine($"sample rate: {sampleRate} Hz");
int byteRate = (int)br.ReadUInt32();
Console.WriteLine($"byteRate: {byteRate}");
ushort blockSize = br.ReadUInt16();
Console.WriteLine($"block size: {blockSize} bytes per sample");
ushort bitsPerSample = br.ReadUInt16();
Console.WriteLine($"resolution: {bitsPerSample}-bit");
if (bitsPerSample != 16)
throw new NotImplementedException("Only 16-bit WAV files are supported");
// Cycle custom chunks until we get to the DATA chunk
// Various chunks may exist until the data chunk appears
long nextChunkPosition = 36;
int maximumChunkNumber = 42;
long firstDataByte = 0;
long dataByteCount = 0;
for (int i = 0; i < maximumChunkNumber; i++)
{
var chunk = ChunkInfo(br, nextChunkPosition);
Console.WriteLine($"Chunk at {nextChunkPosition} ('{chunk.id}') indicates {chunk.length:N0} bytes");
if (chunk.id == "data")
{
firstDataByte = nextChunkPosition + 8;
dataByteCount = chunk.length;
break;
}
nextChunkPosition += chunk.length + 8;
}
if (firstDataByte == 0 || dataByteCount == 0)
throw new InvalidOperationException("Unsupported WAV format (no 'data' chunk found)");
Console.WriteLine($"PCM data starts at {firstDataByte} and contains {dataByteCount} bytes");
// Now read PCM data values into an array and return it
long sampleCount = dataByteCount / blockSize;
Debug.WriteLine($"Samples in file: {sampleCount}");
double[] L = null;
double[] R = null;
if (channelCount == 1)
{
L = new double[sampleCount];
for (int i = 0; i < sampleCount; i++)
{
L[i] = br.ReadInt16();
}
}
else if (channelCount == 2)
{
L = new double[sampleCount];
R = new double[sampleCount];
for (int i = 0; i < sampleCount; i++)
{
L[i] = br.ReadInt16();
R[i] = br.ReadInt16();
}
}
return (sampleRate, L, R);
}
}
}
}