1 // Copyright (C) 2012  James Turner - zakalawe@mac.com
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 //
17 
18 // adapted from the freealut sources, especially alutBufferData.c, alutLoader.c
19 // and alutCodec.c (freealut is also LGPL licensed)
20 
21 #include <simgear_config.h>
22 
23 #include "readwav.hxx"
24 
25 #include <cassert>
26 #include <cstdlib>
27 
28 #include <stdio.h> // snprintf
29 #include <zlib.h> // for gzXXX functions
30 
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/debug/logstream.hxx>
33 #include <simgear/misc/stdint.hxx>
34 #include <simgear/structure/exception.hxx>
35 #include <simgear/debug/ErrorReportingCallback.hxx>
36 
37 #include "sample.hxx"
38 
39 namespace
40 {
41   class Buffer {
42   public:
43     ALvoid* data;
44     unsigned int format;
45     unsigned int block_align;
46     ALsizei length;
47     ALfloat frequency;
48     SGPath path;
49 
Buffer()50     Buffer() : data(nullptr), format(AL_NONE), length(0), frequency(0.0f) {}
51 
~Buffer()52     ~Buffer()
53     {
54       if (data) {
55         free(data);
56       }
57     }
58   };
59 
formatConstruct(ALint numChannels,ALint bitsPerSample,bool compressed,const SGPath & path)60   unsigned int formatConstruct(ALint numChannels, ALint bitsPerSample, bool compressed, const SGPath& path)
61   {
62     unsigned int rv = 0;
63     if (!compressed) {
64       if (numChannels == 1 && bitsPerSample == 16) rv = SG_SAMPLE_MONO16;
65       else if (numChannels == 1 && bitsPerSample == 8) rv = SG_SAMPLE_MONO8;
66       else if (numChannels == 2 && bitsPerSample == 16) rv = SG_SAMPLE_STEREO16;
67       else if (numChannels == 2 && bitsPerSample == 8) rv = SG_SAMPLE_STEREO8;
68       else {
69         char msg[81];
70         snprintf(msg, 80, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
71           throw sg_exception(msg, {}, path, false);
72       }
73     } else {
74       if (numChannels == 1 && bitsPerSample == 4) rv = SG_SAMPLE_ADPCM;
75       else if (numChannels == 1 && bitsPerSample == 8) rv = SG_SAMPLE_MULAW;
76       else {
77         char msg[81];
78         snprintf(msg, 80, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
79           throw sg_exception(msg, {}, path, false);
80       }
81     }
82     return rv;
83   }
84 
85 // function prototype for decoding audio data
86   typedef void Codec(Buffer* buf);
87 
codecLinear(Buffer *)88   void codecLinear(Buffer* /*buf*/)
89   {
90   }
91 
codecPCM16BE(Buffer * buf)92   void codecPCM16BE (Buffer* buf)
93   {
94     uint16_t *d = (uint16_t *) buf->data;
95     size_t i, l = buf->length / 2;
96     for (i = 0; i < l; i++) {
97       *d = sg_bswap_16(*d); ++d;
98     }
99   }
100 
101  /*
102   * From: http://www.multimedia.cx/simpleaudio.html#tth_sEc6.1
103   */
mulaw2linear(uint8_t mulawbyte)104   int16_t mulaw2linear (uint8_t mulawbyte)
105   {
106     static const int16_t exp_lut[8] = {
107       0, 132, 396, 924, 1980, 4092, 8316, 16764
108     };
109     int16_t sign, exponent, mantissa, sample;
110     mulawbyte = ~mulawbyte;
111     sign = (mulawbyte & 0x80);
112     exponent = (mulawbyte >> 4) & 0x07;
113     mantissa = mulawbyte & 0x0F;
114     sample = exp_lut[exponent] + (mantissa << (exponent + 3));
115     return sign ? -sample : sample;
116   }
117 
codecULaw(Buffer * b)118   void codecULaw (Buffer* b)
119   {
120     uint8_t *d = (uint8_t *) b->data;
121     size_t newLength = b->length * 2;
122     int16_t *buf = (int16_t *) malloc(newLength);
123     if (buf == nullptr)
124       throw sg_exception("malloc failed decoing ULaw WAV file");
125 
126     for (ALsizei i = 0; i < b->length; i++) {
127       buf[i] = mulaw2linear(d[i]);
128     }
129 
130     free(b->data);
131     b->data = buf;
132     b->length = newLength;
133   }
134 
ima2linear(uint8_t nibble,int16_t * val,uint8_t * idx)135   int16_t ima2linear (uint8_t nibble, int16_t *val, uint8_t *idx)
136   {
137     const int16_t _ima4_index_table[16] =
138     {
139        -1, -1, -1, -1, 2, 4, 6, 8,
140        -1, -1, -1, -1, 2, 4, 6, 8
141     };
142     const int16_t _ima4_step_table[89] =
143     {
144          7,     8,     9,    10,    11,    12,    13,    14,    16,    17,
145         19,    21,    23,    25,    28,    31,    34,    37,    41,    45,
146         50,    55,    60,    66,    73,    80,    88,    97,   107,   118,
147        130,   143,   157,   173,   190,   209,   230,   253,   279,   307,
148        337,   371,   408,   449,   494,   544,   598,   658,   724,   796,
149        876,   963,  1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,
150       2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,
151       5894,  6484,  7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899,
152      15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
153     };
154     int32_t predictor;
155     int16_t diff, step;
156     int8_t delta, sign;
157     int8_t index;
158 
159     index = *idx;
160     if (index > 88) index = 88;
161     else if (index < 0) index = 0;
162 
163     predictor = *val;
164     step = _ima4_step_table[index];
165 
166     sign = nibble & 0x8;
167     delta = nibble & 0x7;
168 
169     diff = 0;
170     if (delta & 4) diff += step;
171     if (delta & 2) diff += (step >> 1);
172     if (delta & 1) diff += (step >> 2);
173     diff += (step >> 3);
174 
175     if (sign) predictor -= diff;
176     else predictor += diff;
177 
178     index += _ima4_index_table[nibble];
179     if (index > 88) index = 88;
180     else if (index < 0) index = 0;
181     *idx = index;
182 
183     if (predictor < -32768) predictor = -32768;
184     else if (predictor > 32767) predictor = 32767;
185     *val = predictor;
186 
187     return *val;
188   }
189 
codecIMA4(Buffer * b)190   void codecIMA4 (Buffer* b)
191   {
192     uint8_t *d = (uint8_t *) b->data;
193     unsigned int block_align = b->block_align;
194     size_t blocks = b->length/block_align;
195     size_t newLength = block_align * blocks * 4;
196     int16_t *buf = (int16_t *) malloc ( newLength );
197     if (buf == nullptr)
198       throw sg_exception("malloc failed decoing IMA4 WAV file");
199 
200     int16_t *ptr = buf;
201     for (size_t i = 0; i < blocks; i++)
202     {
203       int16_t predictor;
204       uint8_t index;
205 
206       predictor = *d++;
207       predictor |= *d++ << 8;
208       index = *d++;
209       d++;
210 
211       for (size_t j = 0; j < block_align; j += 4)
212       {
213         for (unsigned int q=0; q<4; q++)
214         {
215           uint8_t nibble = *d++;
216           *ptr++ = ima2linear(nibble & 0xF, &predictor, &index);
217           *ptr++ = ima2linear(nibble >> 4, &predictor, &index);
218         }
219       }
220     }
221 
222     free(b->data);
223     b->data = buf;
224     b->length = newLength;
225   }
226 
gzSkip(gzFile fd,int skipCount)227   bool gzSkip(gzFile fd, int skipCount)
228   {
229       int r = gzseek(fd, skipCount, SEEK_CUR);
230       return (r >= 0);
231   }
232 
233   const int32_t WAV_RIFF_4CC = 0x52494646; // 'RIFF'
234   const int32_t WAV_WAVE_4CC = 0x57415645; // 'WAVE'
235   const int32_t WAV_DATA_4CC = 0x64617461; // 'data'
236   const int32_t WAV_FORMAT_4CC = 0x666d7420; // 'fmt '
237 
238   template<class T>
wavReadBE(gzFile fd,T & value)239   bool wavReadBE(gzFile fd, T& value)
240   {
241     if (gzread(fd, &value, sizeof(T)) != sizeof(T))
242       return false;
243 
244     if (sgIsLittleEndian())
245       sgEndianSwap(&value);
246 
247     return true;
248   }
249 
250   template<class T>
wavReadLE(gzFile fd,T & value)251   bool wavReadLE(gzFile fd, T& value)
252   {
253     if (gzread(fd, &value, sizeof(T)) != sizeof(T))
254       return false;
255 
256     if (sgIsBigEndian())
257       sgEndianSwap(&value);
258 
259     return true;
260   }
261 
loadWavFile(gzFile fd,Buffer * b)262   void loadWavFile(gzFile fd, Buffer* b)
263   {
264     assert(b->data == nullptr);
265 
266     bool found_header = false;
267     bool compressed = false;
268     uint16_t bitsPerSample = 8;
269     uint16_t numChannels = 1;
270     uint32_t chunkLength;
271     int32_t magic;
272     uint16_t audioFormat;
273     uint32_t samplesPerSecond;
274     uint32_t byteRate;
275     uint16_t blockAlign;
276     Codec *codec = codecLinear;
277 
278     if (!wavReadBE(fd, magic))
279         throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
280 
281     if (magic != WAV_RIFF_4CC) {
282       throw sg_io_exception("not a .wav file", b->path, {}, false);
283     }
284 
285     if (!wavReadLE(fd, chunkLength) || !wavReadBE(fd, magic))
286       throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
287 
288     if (magic != WAV_WAVE_4CC)      /* "WAVE" */
289     {
290         throw sg_io_exception("unrecognized WAV magic", b->path, {}, false);
291     }
292 
293     while (1) {
294         if (!wavReadBE(fd, magic) || !wavReadLE(fd, chunkLength))
295             throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
296 
297         if (magic == WAV_FORMAT_4CC)  /* "fmt " */
298         {
299             found_header = true;
300             if (chunkLength < 16) {
301               throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
302             }
303 
304             if (!wavReadLE (fd, audioFormat) ||
305                 !wavReadLE (fd, numChannels) ||
306                 !wavReadLE (fd, samplesPerSecond) ||
307                 !wavReadLE (fd, byteRate) ||
308                 !wavReadLE (fd, blockAlign) ||
309                 !wavReadLE (fd, bitsPerSample))
310             {
311                 throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
312             }
313 
314             if (!gzSkip(fd, chunkLength - 16))
315                 throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
316 
317             switch (audioFormat)
318               {
319               case 1:            /* PCM */
320                 codec = (bitsPerSample == 8 || sgIsLittleEndian()) ? codecLinear : codecPCM16BE;
321                 break;
322               case 7:            /* uLaw */
323                 if (alIsExtensionPresent((ALchar *)"AL_EXT_mulaw")) {
324                   compressed = true;
325                   codec = codecLinear;
326                 } else {
327                   bitsPerSample *= 2; /* uLaw is 16-bit packed into 8 bits */
328                   codec = codecULaw;
329                }
330                 break;
331               case 17:		/* IMA4 ADPCM */
332                 if (alIsExtensionPresent((ALchar *)"AL_EXT_ima4") &&
333                     (alIsExtensionPresent((ALchar *)"AL_SOFT_block_alignment")
334                      || blockAlign == 65)) {
335                   compressed = true;
336                   codec = codecLinear;
337                 } else {
338                   bitsPerSample *= 4; /* adpcm is 16-bit packed into 4 bits */
339                   codec = codecIMA4;
340 
341                 }
342                 break;
343               default:
344                 throw sg_io_exception("unsupported WAV encoding:" + std::to_string(audioFormat), b->path, {}, false);
345               }
346 
347               b->block_align = blockAlign;
348               b->frequency = samplesPerSecond;
349               b->format = formatConstruct(numChannels, bitsPerSample, compressed, b->path);
350         } else if (magic == WAV_DATA_4CC) {
351             if (!found_header) {
352                 /* ToDo: A bit wrong to check here, fmt chunk could come later... */
353                 throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
354             }
355 
356             b->data = malloc(chunkLength);
357             b->length = chunkLength;
358             size_t read = gzread(fd, b->data, chunkLength);
359             if (read != chunkLength) {
360                 throw sg_io_exception("insufficent data reading WAV file", b->path, {}, false);
361             }
362 
363             break;
364         } else {
365             if (!gzSkip(fd, chunkLength))
366               throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
367         }
368 
369         if ((chunkLength & 1) && !gzeof(fd) && !gzSkip(fd, 1))
370           throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
371       } // of file chunk parser loop
372 
373       codec(b); // might throw if something really bad occurs
374   } // of loadWav function
375 
376 } // of anonymous namespace
377 
378 namespace simgear
379 {
380 
loadWAVFromFile(const SGPath & path,unsigned int & format,ALsizei & size,ALfloat & freqf,unsigned int & block_align)381 ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align)
382 {
383   if (!path.exists()) {
384       simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AudioFX, "loadWAVFromFile: not found", path);
385     return nullptr;
386   }
387 
388   Buffer b;
389   b.path = path;
390 
391   gzFile fd;
392 #if defined(SG_WINDOWS)
393 	std::wstring ws = path.wstr();
394 	fd = gzopen_w(ws.c_str(), "rb");
395 #else
396   std::string ps = path.utf8Str();
397   fd = gzopen(ps.c_str(), "rb");
398 #endif
399   if (!fd) {
400       simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::AudioFX, "loadWAVFromFile: unable to open file", path);
401     return nullptr;
402   }
403 
404   try {
405       loadWavFile(fd, &b);
406   } catch (sg_exception& e) {
407       simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::AudioFX, "loadWAVFromFile: unable to read file:"
408                              + e.getFormattedMessage(), e.getLocation());
409       return nullptr;
410   }
411 
412   ALvoid* data = b.data;
413   b.data = nullptr; // don't free when Buffer does out of scope
414   format = b.format;
415   block_align = b.block_align;
416   size = b.length;
417   freqf = b.frequency;
418 
419   gzclose(fd);
420   return data;
421 }
422 
423 } // of namespace simgear
424