1 /**
2 Supports Microsoft WAV audio file format.
3 
4 Copyright: Guillaume Piolat 2015-2020.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module audioformats.wav;
8 
9 import dplug.core.nogc;
10 import audioformats.io;
11 
12 
13 version(decodeWAV)
14 {
15     /// Use both for scanning and decoding
16     final class WAVDecoder
17     {
18     public:
19     @nogc:
20         this(IOCallbacks* io, void* userData) nothrow
21         {
22             _io = io;
23             _userData = userData;
24         }
25 
26         // After scan, we know _sampleRate, _lengthInFrames, and _channels, and can call `readSamples`
27         void scan()
28         {
29             // check RIFF header
30             {
31                 uint chunkId, chunkSize;
32                 _io.readRIFFChunkHeader(_userData, chunkId, chunkSize);
33                 if (chunkId != RIFFChunkId!"RIFF")
34                     throw mallocNew!Exception("Expected RIFF chunk.");
35 
36                 if (chunkSize < 4)
37                     throw mallocNew!Exception("RIFF chunk is too small to contain a format.");
38 
39                 if (_io.read_uint_BE(_userData) !=  RIFFChunkId!"WAVE")
40                     throw mallocNew!Exception("Expected WAVE format.");
41             }
42 
43             bool foundFmt = false;
44             bool foundData = false;
45 
46             int byteRate;
47             int blockAlign;
48             int bitsPerSample;
49 
50             while (!_io.nothingToReadAnymore(_userData))
51             {
52                 // Some corrupted WAV files in the wild finish with one
53                 // extra 0 byte after an AFAn chunk, very odd
54                 if (_io.remainingBytesToRead(_userData) == 1)
55                 {
56                     if (_io.peek_ubyte(_userData) == 0)
57                         break;
58                 }
59 
60                 // Question: is there any reason to parse the whole WAV file? This prevents streaming.
61 
62                 uint chunkId, chunkSize;
63                 _io.readRIFFChunkHeader(_userData, chunkId, chunkSize); 
64                 if (chunkId == RIFFChunkId!"fmt ")
65                 {
66                     if (foundFmt)
67                         throw mallocNew!Exception("Found several 'fmt ' chunks in RIFF file.");
68 
69                     foundFmt = true;
70 
71                     if (chunkSize < 16)
72                         throw mallocNew!Exception("Expected at least 16 bytes in 'fmt ' chunk."); // found in real-world for the moment: 16 or 40 bytes
73 
74                     _audioFormat = _io.read_ushort_LE(_userData);
75                     if (_audioFormat == WAVE_FORMAT_EXTENSIBLE)
76                         throw mallocNew!Exception("No support for format WAVE_FORMAT_EXTENSIBLE yet."); // Reference: http://msdn.microsoft.com/en-us/windows/hardware/gg463006.aspx
77 
78                     if (_audioFormat != LinearPCM && _audioFormat != FloatingPointIEEE)
79                         throw mallocNew!Exception("Unsupported audio format, only PCM and IEEE float are supported.");
80 
81                     _channels = _io.read_ushort_LE(_userData);
82 
83                     _sampleRate = _io.read_uint_LE(_userData);
84                     if (_sampleRate <= 0)
85                         throw mallocNew!Exception("Unsupported sample-rate."); // we do not support sample-rate higher than 2^31hz
86 
87                     uint bytesPerSec = _io.read_uint_LE(_userData);
88                     int bytesPerFrame = _io.read_ushort_LE(_userData);
89                     bitsPerSample = _io.read_ushort_LE(_userData);
90 
91                     if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) 
92                         throw mallocNew!Exception("Unsupported bitdepth");
93 
94                     if (bytesPerFrame != (bitsPerSample / 8) * _channels)
95                         throw mallocNew!Exception("Invalid bytes-per-second, data might be corrupted.");
96 
97                     _io.skip(chunkSize - 16, _userData);
98                 }
99                 else if (chunkId == RIFFChunkId!"data")
100                 {
101                     if (foundData)
102                         throw mallocNew!Exception("Found several 'data' chunks in RIFF file.");
103 
104                     if (!foundFmt)
105                         throw mallocNew!Exception("'fmt ' chunk expected before the 'data' chunk.");
106 
107                     _bytePerSample = bitsPerSample / 8;
108                     uint frameSize = _channels * _bytePerSample;
109                     if (chunkSize % frameSize != 0)
110                         throw mallocNew!Exception("Remaining bytes in 'data' chunk, inconsistent with audio data type.");
111 
112                     uint numFrames = chunkSize / frameSize;
113                     _lengthInFrames = numFrames;
114 
115                     _samplesOffsetInFile = _io.tell(_userData);
116 
117                     _io.skip(chunkSize, _userData); // skip, will read later
118                     foundData = true;
119                 }
120                 else
121                 {
122                     // ignore unknown chunks
123                     _io.skip(chunkSize, _userData);
124                 }
125             }
126 
127             if (!foundFmt)
128                 throw mallocNew!Exception("'fmt ' chunk not found.");
129 
130             if (!foundData)
131                 throw mallocNew!Exception("'data' chunk not found.");
132 
133             // Get ready to decode
134             _io.seek(_samplesOffsetInFile, false, _userData);
135             _framePosition = 0; // seek to start
136         }
137 
138         // read interleaved samples
139         // `outData` should have enough room for frames * _channels
140         // Returs: Frames actually read.
141         int readSamples(float* outData, int maxFrames) nothrow
142         {
143             assert(_framePosition <= _lengthInFrames);
144             int available = _lengthInFrames - _framePosition;
145 
146             // How much frames can we decode?
147             int frames = maxFrames;
148             if (frames > available)
149                 frames = available;
150             _framePosition += frames;
151 
152             int numSamples = frames * _channels;
153 
154             uint n = 0;
155 
156             try
157             {
158                 if (_audioFormat == FloatingPointIEEE)
159                 {
160                     if (_bytePerSample == 4)
161                     {
162                         for (n = 0; n < numSamples; ++n)
163                             outData[n] = _io.read_float_LE(_userData);
164                     }
165                     else if (_bytePerSample == 8)
166                     {
167                         for (n = 0; n < numSamples; ++n)
168                             outData[n] = _io.read_double_LE(_userData);
169                     }
170                     else
171                         throw mallocNew!Exception("Unsupported bit-depth for floating point data, should be 32 or 64.");
172                 }
173                 else if (_audioFormat == LinearPCM)
174                 {
175                     if (_bytePerSample == 1)
176                     {
177                         for (n = 0; n < numSamples; ++n)
178                         {
179                             ubyte b = _io.read_ubyte(_userData);
180                             outData[n] = (b - 128) / 127.0;
181                         }
182                     }
183                     else if (_bytePerSample == 2)
184                     {
185                         for (n = 0; n < numSamples; ++n)
186                         {
187                             short s = _io.read_ushort_LE(_userData);
188                             outData[n] = s / 32767.0;
189                         }
190                     }
191                     else if (_bytePerSample == 3)
192                     {
193                         for (n = 0; n < numSamples; ++n)
194                         {
195                             int s = _io.read_24bits_LE(_userData);
196                             // duplicate sign bit
197                             s = (s << 8) >> 8;
198                             outData[n] = s / 8388607.0;
199                         }
200                     }
201                     else if (_bytePerSample == 4)
202                     {
203                         for (n = 0; n < numSamples; ++n)
204                         {
205                             int s = _io.read_uint_LE(_userData);
206                             outData[n] = s / 2147483648.0;
207                         }
208                     }
209                     else
210                         throw mallocNew!Exception("Unsupported bit-depth for integer PCM data, should be 8, 16, 24 or 32 bits.");
211                 }
212                 else
213                     assert(false); // should have been handled earlier, crash
214             }
215             catch(Exception e)
216             {
217                 destroyFree(e); // well this is really unexpected, since no read should fail in this loop
218                 return 0;
219             }
220 
221             // Return number of integer samples read
222             return frames;
223         }
224 
225     package:
226         int _sampleRate;
227         int _channels;
228         int _audioFormat;
229         int _bytePerSample;
230         long _samplesOffsetInFile;
231         uint _lengthInFrames;
232         uint _framePosition;
233 
234     private:
235         void* _userData;
236         IOCallbacks* _io;
237     }
238 }
239 
240 
241 version(encodeWAV)
242 {
243     /// Use both for scanning and decoding
244     final class WAVEncoder
245     {
246     public:
247     @nogc:
248         this(IOCallbacks* io, void* userData, int sampleRate, int numChannels)
249         {
250             _io = io;
251             _userData = userData;
252             _channels = numChannels;
253 
254             // Avoids a number of edge cases.
255             if (_channels < 0 || _channels > 1024)
256                 throw mallocNew!Exception("Can't save a WAV with this numnber of channels.");
257 
258             // RIFF header
259             // its size will be overwritten at finalizing
260             _riffLengthOffset = _io.tell(_userData) + 4;
261             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"RIFF", 0);
262             _io.write_uint_BE(_userData, RIFFChunkId!"WAVE");
263 
264             // 'fmt ' sub-chunk
265             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"fmt ", 0x10);
266             _io.write_ushort_LE(_userData, FloatingPointIEEE);
267             _io.write_ushort_LE(_userData, cast(ushort)(_channels));
268             _io.write_uint_LE(_userData, sampleRate);
269 
270             size_t bytesPerSec = sampleRate * _channels * float.sizeof;
271             _io.write_uint_LE(_userData,  cast(uint)(bytesPerSec));
272 
273             int bytesPerFrame = cast(int)(_channels * float.sizeof);
274             _io.write_ushort_LE(_userData, cast(ushort)bytesPerFrame);
275 
276             _io.write_ushort_LE(_userData, 32);
277 
278             // data sub-chunk
279             _dataLengthOffset = _io.tell(_userData) + 4;
280             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"data", 0); // write 0 but temporarily, this will be overwritten at finalizing
281             _writtenFrames = 0;
282         }
283 
284         // read interleaved samples
285         // `inSamples` should have enough room for frames * _channels
286         int writeSamples(float* inSamples, int frames) nothrow
287         {
288             int n = 0;
289             try
290             {
291                 int samples = frames * _channels;
292                 for ( ; n < samples; ++n)
293                 {
294                     _io.write_float_LE(_userData, inSamples[n]);
295                 }
296                 _writtenFrames += frames;
297             }
298             catch(Exception e)
299             {
300                 destroyFree(e);
301             }
302             return n;
303         }
304 
305         void finalizeEncoding() 
306         {
307             size_t bytesOfData = float.sizeof * _channels * _writtenFrames;
308 
309             // write final number of samples for the 'RIFF' chunk
310             {
311                 uint riffLength = cast(uint)( 4 + (4 + 4 + 16) + (4 + 4 + bytesOfData) );
312                 _io.seek(_riffLengthOffset, false, _userData);
313                 _io.write_uint_LE(_userData, riffLength);
314             }
315 
316             // write final number of samples for the 'data' chunk
317             {
318                 _io.seek(_dataLengthOffset, false, _userData);
319                 _io.write_uint_LE(_userData, cast(uint)bytesOfData );
320             }
321         }
322 
323     private:
324         void* _userData;
325         IOCallbacks* _io;
326         int _channels;
327         int _writtenFrames;
328         long _riffLengthOffset, _dataLengthOffset;
329     }
330 }
331 
332 
333 private:
334 
335 // wFormatTag
336 immutable int LinearPCM = 0x0001;
337 immutable int FloatingPointIEEE = 0x0003;
338 immutable int WAVE_FORMAT_EXTENSIBLE = 0xFFFE;