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