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 
21         static immutable ubyte[16] KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 
22         [3, 0, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113];
23 
24         this(IOCallbacks* io, void* userData) nothrow
25         {
26             _io = io;
27             _userData = userData;
28         }
29 
30         // After scan, we know _sampleRate, _lengthInFrames, and _channels, and can call `readSamples`
31         void scan()
32         {
33             // check RIFF header
34             {
35                 uint chunkId, chunkSize;
36                 _io.readRIFFChunkHeader(_userData, chunkId, chunkSize);
37                 if (chunkId != RIFFChunkId!"RIFF")
38                     throw mallocNew!Exception("Expected RIFF chunk.");
39 
40                 if (chunkSize < 4)
41                     throw mallocNew!Exception("RIFF chunk is too small to contain a format.");
42 
43                 if (_io.read_uint_BE(_userData) !=  RIFFChunkId!"WAVE")
44                     throw mallocNew!Exception("Expected WAVE format.");
45             }
46 
47             bool foundFmt = false;
48             bool foundData = false;
49 
50             int byteRate;
51             int blockAlign;
52             int bitsPerSample;
53 
54             while (!_io.nothingToReadAnymore(_userData))
55             {
56                 // Some corrupted WAV files in the wild finish with one
57                 // extra 0 byte after an AFAn chunk, very odd
58                 if (_io.remainingBytesToRead(_userData) == 1)
59                 {
60                     if (_io.peek_ubyte(_userData) == 0)
61                         break;
62                 }
63 
64                 // Question: is there any reason to parse the whole WAV file? This prevents streaming.
65 
66                 uint chunkId, chunkSize;
67                 _io.readRIFFChunkHeader(_userData, chunkId, chunkSize); 
68                 if (chunkId == RIFFChunkId!"fmt ")
69                 {
70                     if (foundFmt)
71                         throw mallocNew!Exception("Found several 'fmt ' chunks in RIFF file.");
72 
73                     foundFmt = true;
74 
75                     if (chunkSize < 16)
76                         throw mallocNew!Exception("Expected at least 16 bytes in 'fmt ' chunk."); // found in real-world for the moment: 16 or 40 bytes
77 
78                     _audioFormat = _io.read_ushort_LE(_userData);
79                     bool isWFE = _audioFormat == WAVE_FORMAT_EXTENSIBLE;
80 
81                     if (_audioFormat != LinearPCM && _audioFormat != FloatingPointIEEE && !isWFE)
82                         throw mallocNew!Exception("Unsupported audio format, only PCM and IEEE float and WAVE_FORMAT_EXTENSIBLE are supported.");
83 
84                     _channels = _io.read_ushort_LE(_userData);
85 
86                     _sampleRate = _io.read_uint_LE(_userData);
87                     if (_sampleRate <= 0)
88                         throw mallocNew!Exception("Unsupported sample-rate."); // we do not support sample-rate higher than 2^31hz
89 
90                     uint bytesPerSec = _io.read_uint_LE(_userData);
91                     int bytesPerFrame = _io.read_ushort_LE(_userData);
92                     bitsPerSample = _io.read_ushort_LE(_userData);
93 
94                     if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) 
95                         throw mallocNew!Exception("Unsupported bitdepth");
96 
97                     if (bytesPerFrame != (bitsPerSample / 8) * _channels)
98                         throw mallocNew!Exception("Invalid bytes-per-second, data might be corrupted.");
99 
100                     // Sometimes there is no cbSize
101                     if (chunkSize >= 18)
102                     {
103                         ushort cbSize = _io.read_ushort_LE(_userData);
104 
105                         if (isWFE)
106                         {
107                             if (cbSize >= 22)
108                             {
109                                 ushort wReserved = _io.read_ushort_LE(_userData);
110                                 uint dwChannelMask = _io.read_uint_LE(_userData);
111                                 ubyte[16] SubFormat = _io.read_guid(_userData);
112 
113                                 if (SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
114                                 {
115                                     _audioFormat = FloatingPointIEEE;
116                                 }
117                                 else
118                                     throw mallocNew!Exception("Unsupported GUID in WAVE_FORMAT_EXTENSIBLE.");
119                             }
120                             else
121                                 throw mallocNew!Exception("Unsupported WAVE_FORMAT_EXTENSIBLE.");
122 
123                             _io.skip(chunkSize - (18 + 2 + 4 + 16), _userData);
124                         }
125                         else
126                         {
127                             _io.skip(chunkSize - 18, _userData);
128                         }
129                     }
130                     else
131                     {
132                         _io.skip(chunkSize - 16, _userData);
133                     }
134 
135                 }
136                 else if (chunkId == RIFFChunkId!"data")
137                 {
138                     if (foundData)
139                         throw mallocNew!Exception("Found several 'data' chunks in RIFF file.");
140 
141                     if (!foundFmt)
142                         throw mallocNew!Exception("'fmt ' chunk expected before the 'data' chunk.");
143 
144                     _bytePerSample = bitsPerSample / 8;
145                     uint frameSize = _channels * _bytePerSample;
146                     if (chunkSize % frameSize != 0)
147                         throw mallocNew!Exception("Remaining bytes in 'data' chunk, inconsistent with audio data type.");
148 
149                     uint numFrames = chunkSize / frameSize;
150                     _lengthInFrames = numFrames;
151 
152                     _samplesOffsetInFile = _io.tell(_userData);
153 
154                     _io.skip(chunkSize, _userData); // skip, will read later
155                     foundData = true;
156                 }
157                 else
158                 {
159                     // ignore unknown chunks
160                     _io.skip(chunkSize, _userData);
161                 }
162             }
163 
164             if (!foundFmt)
165                 throw mallocNew!Exception("'fmt ' chunk not found.");
166 
167             if (!foundData)
168                 throw mallocNew!Exception("'data' chunk not found.");
169 
170             // Get ready to decode
171             _io.seek(_samplesOffsetInFile, false, _userData);
172             _framePosition = 0; // seek to start
173         }
174 
175         /// Returns: false in case of failure.
176         bool seekPosition(int absoluteFrame)
177         {
178             if (absoluteFrame < 0)
179                 return false;
180             if (absoluteFrame > _lengthInFrames)
181                 return false;
182             uint frameSize = _channels * _bytePerSample;
183             long pos = _samplesOffsetInFile + absoluteFrame * frameSize;
184             _io.seek(pos, false, _userData);
185             _framePosition = absoluteFrame;
186             return true;
187         }
188 
189         /// Returns: position in absolute number of frames since beginning.
190         int tellPosition()
191         {
192             return _framePosition;
193         }
194 
195         // read interleaved samples
196         // `outData` should have enough room for frames * _channels
197         // Returs: Frames actually read.
198         int readSamples(T)(T* outData, int maxFrames) nothrow
199         {
200             assert(_framePosition <= _lengthInFrames);
201             int available = _lengthInFrames - _framePosition;
202 
203             // How much frames can we decode?
204             int frames = maxFrames;
205             if (frames > available)
206                 frames = available;
207             _framePosition += frames;
208 
209             int numSamples = frames * _channels;
210 
211             uint n = 0;
212 
213             try
214             {
215                 if (_audioFormat == FloatingPointIEEE)
216                 {
217                     if (_bytePerSample == 4)
218                     {
219                         for (n = 0; n < numSamples; ++n)
220                             outData[n] = _io.read_float_LE(_userData);
221                     }
222                     else if (_bytePerSample == 8)
223                     {
224                         for (n = 0; n < numSamples; ++n)
225                             outData[n] = _io.read_double_LE(_userData);
226                     }
227                     else
228                         throw mallocNew!Exception("Unsupported bit-depth for floating point data, should be 32 or 64.");
229                 }
230                 else if (_audioFormat == LinearPCM)
231                 {
232                     if (_bytePerSample == 1)
233                     {
234                         for (n = 0; n < numSamples; ++n)
235                         {
236                             ubyte b = _io.read_ubyte(_userData);
237                             outData[n] = (b - 128) / 127.0;
238                         }
239                     }
240                     else if (_bytePerSample == 2)
241                     {
242                         for (n = 0; n < numSamples; ++n)
243                         {
244                             short s = _io.read_ushort_LE(_userData);
245                             outData[n] = s / 32767.0;
246                         }
247                     }
248                     else if (_bytePerSample == 3)
249                     {
250                         for (n = 0; n < numSamples; ++n)
251                         {
252                             int s = _io.read_24bits_LE(_userData);
253                             // duplicate sign bit
254                             s = (s << 8) >> 8;
255                             outData[n] = s / 8388607.0;
256                         }
257                     }
258                     else if (_bytePerSample == 4)
259                     {
260                         for (n = 0; n < numSamples; ++n)
261                         {
262                             int s = _io.read_uint_LE(_userData);
263                             outData[n] = s / 2147483648.0;
264                         }
265                     }
266                     else
267                         throw mallocNew!Exception("Unsupported bit-depth for integer PCM data, should be 8, 16, 24 or 32 bits.");
268                 }
269                 else
270                     assert(false); // should have been handled earlier, crash
271             }
272             catch(Exception e)
273             {
274                 destroyFree(e); // well this is really unexpected, since no read should fail in this loop
275                 return 0;
276             }
277 
278             // Return number of integer samples read
279             return frames;
280         }
281 
282     package:
283         int _sampleRate;
284         int _channels;
285         int _audioFormat;
286         int _bytePerSample;
287         long _samplesOffsetInFile;
288         uint _lengthInFrames;
289         uint _framePosition;
290 
291     private:
292         void* _userData;
293         IOCallbacks* _io;
294     }
295 }
296 
297 
298 version(encodeWAV)
299 {
300     /// Use both for scanning and decoding
301     final class WAVEncoder
302     {
303     public:
304     @nogc:
305         enum Format
306         {
307             fp32le,
308             fp64le,
309         }
310 
311         this(IOCallbacks* io, void* userData, int sampleRate, int numChannels, Format format)
312         {
313             _io = io;
314             _userData = userData;
315             _channels = numChannels;
316             _format = format;
317 
318             // Avoids a number of edge cases.
319             if (_channels < 0 || _channels > 1024)
320                 throw mallocNew!Exception("Can't save a WAV with this numnber of channels.");
321 
322             // RIFF header
323             // its size will be overwritten at finalizing
324             _riffLengthOffset = _io.tell(_userData) + 4;
325             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"RIFF", 0);
326             _io.write_uint_BE(_userData, RIFFChunkId!"WAVE");
327 
328             // 'fmt ' sub-chunk
329             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"fmt ", 0x10);
330             _io.write_ushort_LE(_userData, FloatingPointIEEE);
331             _io.write_ushort_LE(_userData, cast(ushort)(_channels));
332             _io.write_uint_LE(_userData, sampleRate);
333 
334             size_t bytesPerSec = sampleRate * cast(size_t) frameSize();
335             _io.write_uint_LE(_userData,  cast(uint)(bytesPerSec));
336 
337             int bytesPerFrame = frameSize();
338             _io.write_ushort_LE(_userData, cast(ushort)bytesPerFrame);
339 
340             _io.write_ushort_LE(_userData, cast(ushort)(sampleSize() * 8));
341 
342             // data sub-chunk
343             _dataLengthOffset = _io.tell(_userData) + 4;
344             _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"data", 0); // write 0 but temporarily, this will be overwritten at finalizing
345             _writtenFrames = 0;
346         }
347 
348         // read interleaved samples
349         // `inSamples` should have enough room for frames * _channels
350         int writeSamples(T)(T* inSamples, int frames) nothrow
351         {
352             int n = 0;
353             try
354             {
355                 int samples = frames * _channels;
356                 
357                 final switch(_format)
358                 {
359                     case Format.fp32le:
360                         for ( ; n < samples; ++n)
361                         {
362                             _io.write_float_LE(_userData, inSamples[n]);
363                         }
364                         break;
365                     case Format.fp64le:
366                         for ( ; n < samples; ++n)
367                         {
368                             _io.write_double_LE(_userData, inSamples[n]);
369                         }
370                         break;
371                 }
372                 _writtenFrames += frames;
373             }
374             catch(Exception e)
375             {
376                 destroyFree(e);
377             }
378             return n;
379         }
380 
381         int sampleSize()
382         {
383             final switch(_format)
384             {
385                 case Format.fp32le: return 4;
386                 case Format.fp64le: return 8;
387             }
388         }
389 
390         int frameSize()
391         {
392             return sampleSize() * _channels;
393         }
394 
395         void finalizeEncoding() 
396         {
397             size_t bytesOfData = frameSize() * _writtenFrames;
398 
399             // write final number of samples for the 'RIFF' chunk
400             {
401                 uint riffLength = cast(uint)( 4 + (4 + 4 + 16) + (4 + 4 + bytesOfData) );
402                 _io.seek(_riffLengthOffset, false, _userData);
403                 _io.write_uint_LE(_userData, riffLength);
404             }
405 
406             // write final number of samples for the 'data' chunk
407             {
408                 _io.seek(_dataLengthOffset, false, _userData);
409                 _io.write_uint_LE(_userData, cast(uint)bytesOfData );
410             }
411         }
412 
413     private:
414         void* _userData;
415         IOCallbacks* _io;
416         Format _format;
417         int _channels;
418         int _writtenFrames;
419         long _riffLengthOffset, _dataLengthOffset;
420     }
421 }
422 
423 
424 private:
425 
426 // wFormatTag
427 immutable int LinearPCM = 0x0001;
428 immutable int FloatingPointIEEE = 0x0003;
429 immutable int WAVE_FORMAT_EXTENSIBLE = 0xFFFE;