1 // Copyright (C) 2012 James Turner - zakalawe@mac.com
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.
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.
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.
18 // adapted from the freealut sources, especially alutBufferData.c, alutLoader.c
19 // and alutCodec.c (freealut is also LGPL licensed)
21 #include "readwav.hxx"
26 #include <zlib.h> // for gzXXX functions
28 #include <simgear/misc/sg_path.hxx>
29 #include <simgear/debug/logstream.hxx>
30 #include <simgear/misc/stdint.hxx>
31 #include <simgear/structure/exception.hxx>
43 Buffer() : data(NULL), format(AL_NONE), length(0), frequency(0.0f) {}
53 ALenum formatConstruct(ALint numChannels, ALint bitsPerSample)
58 switch (bitsPerSample) {
59 case 8: return AL_FORMAT_MONO8;
60 case 16: return AL_FORMAT_MONO16;
64 switch (bitsPerSample) {
65 case 8: return AL_FORMAT_STEREO8;
66 case 16: return AL_FORMAT_STEREO16;
73 // function prototype for decoding audio data
74 typedef void Codec(Buffer* buf);
76 void codecLinear(Buffer* /*buf*/)
80 void codecPCM16 (Buffer* buf)
82 // always byte-swaps here; is this a good idea?
83 uint16_t *d = (uint16_t *) buf->data;
84 size_t i, l = buf->length / 2;
85 for (i = 0; i < l; i++) {
91 * From: http://www.multimedia.cx/simpleaudio.html#tth_sEc6.1
93 int16_t mulaw2linear (uint8_t mulawbyte)
95 static const int16_t exp_lut[8] = {
96 0, 132, 396, 924, 1980, 4092, 8316, 16764
98 int16_t sign, exponent, mantissa, sample;
99 mulawbyte = ~mulawbyte;
100 sign = (mulawbyte & 0x80);
101 exponent = (mulawbyte >> 4) & 0x07;
102 mantissa = mulawbyte & 0x0F;
103 sample = exp_lut[exponent] + (mantissa << (exponent + 3));
104 return sign ? -sample : sample;
107 void codecULaw (Buffer* b)
109 uint8_t *d = (uint8_t *) b->data;
110 size_t newLength = b->length * 2;
111 int16_t *buf = (int16_t *) malloc(newLength);
113 throw sg_exception("malloc failed decoing ULaw WAV file");
115 for (ALsizei i = 0; i < b->length; i++) {
116 buf[i] = mulaw2linear(d[i]);
121 b->length = newLength;
124 bool gzSkip(gzFile fd, int skipCount)
126 int r = gzseek(fd, skipCount, SEEK_CUR);
130 const int32_t WAV_RIFF_4CC = 0x52494646; // 'RIFF'
131 const int32_t WAV_WAVE_4CC = 0x57415645; // 'WAVE'
132 const int32_t WAV_DATA_4CC = 0x64617461; // 'data'
133 const int32_t WAV_FORMAT_4CC = 0x666d7420; // 'fmt '
136 bool wavReadBE(gzFile fd, T& value)
138 if (gzread(fd, &value, sizeof(T)) != sizeof(T))
141 if (sgIsLittleEndian())
142 sgEndianSwap(&value);
148 bool wavReadLE(gzFile fd, T& value)
150 if (gzread(fd, &value, sizeof(T)) != sizeof(T))
154 sgEndianSwap(&value);
159 void loadWavFile(gzFile fd, Buffer* b)
161 assert(b->data == NULL);
163 bool found_header = false;
164 uint32_t chunkLength;
166 uint16_t audioFormat;
167 uint16_t numChannels;
168 uint32_t samplesPerSecond;
171 uint16_t bitsPerSample;
172 Codec *codec = codecLinear;
174 if (!wavReadBE(fd, magic))
175 throw sg_io_exception("corrupt or truncated WAV data", b->path);
177 if (magic != WAV_RIFF_4CC) {
178 throw sg_io_exception("not a .wav file", b->path);
181 if (!wavReadLE(fd, chunkLength) || !wavReadBE(fd, magic))
182 throw sg_io_exception("corrupt or truncated WAV data", b->path);
184 if (magic != WAV_WAVE_4CC) /* "WAVE" */
186 throw sg_io_exception("unrecognized WAV magic", b->path);
190 if (!wavReadBE(fd, magic) || !wavReadLE(fd, chunkLength))
191 throw sg_io_exception("corrupt or truncated WAV data", b->path);
193 if (magic == WAV_FORMAT_4CC) /* "fmt " */
196 if (chunkLength < 16) {
197 throw sg_io_exception("corrupt or truncated WAV data", b->path);
200 if (!wavReadLE (fd, audioFormat) ||
201 !wavReadLE (fd, numChannels) ||
202 !wavReadLE (fd, samplesPerSecond) ||
203 !wavReadLE (fd, byteRate) ||
204 !wavReadLE (fd, blockAlign) ||
205 !wavReadLE (fd, bitsPerSample))
207 throw sg_io_exception("corrupt or truncated WAV data", b->path);
210 if (!gzSkip(fd, chunkLength - 16))
211 throw sg_io_exception("corrupt or truncated WAV data", b->path);
216 codec = (bitsPerSample == 8 || sgIsLittleEndian()) ? codecLinear : codecPCM16;
219 bitsPerSample *= 2; /* ToDo: ??? */
223 throw sg_io_exception("unsupported WAV encoding", b->path);
226 b->frequency = samplesPerSecond;
227 b->format = formatConstruct(numChannels, bitsPerSample);
228 } else if (magic == WAV_DATA_4CC) {
230 /* ToDo: A bit wrong to check here, fmt chunk could come later... */
231 throw sg_io_exception("corrupt or truncated WAV data", b->path);
234 b->data = malloc(chunkLength);
235 b->length = chunkLength;
236 size_t read = gzread(fd, b->data, chunkLength);
237 if (read != chunkLength) {
238 throw sg_io_exception("insufficent data reading WAV file", b->path);
243 if (!gzSkip(fd, chunkLength))
244 throw sg_io_exception("corrupt or truncated WAV data", b->path);
247 if ((chunkLength & 1) && !gzeof(fd) && !gzSkip(fd, 1))
248 throw sg_io_exception("corrupt or truncated WAV data", b->path);
249 } // of file chunk parser loop
251 codec(b); // might throw if something really bad occurs
252 } // of loadWav function
254 } // of anonymous namespace
259 ALvoid* loadWAVFromFile(const SGPath& path, ALenum& format, ALsizei& size, ALfloat& freqf)
261 if (!path.exists()) {
262 throw sg_io_exception("loadWAVFromFile: file not found", path);
269 fd = gzopen(path.c_str(), "rb");
271 throw sg_io_exception("loadWAVFromFile: unable to open file", path);
275 ALvoid* data = b.data;
276 b.data = NULL; // don't free when Buffer does out of scope
285 ALuint createBufferFromFile(const SGPath& path)
289 ALfloat sampleFrequency;
290 ALvoid* data = loadWAVFromFile(path, format, size, sampleFrequency);
294 alGenBuffers(1, &buffer);
295 if (alGetError() != AL_NO_ERROR) {
297 throw sg_io_exception("OpenAL buffer allocation failed", sg_location(path.str()));
300 alBufferData (buffer, format, data, size, (ALsizei) sampleFrequency);
301 if (alGetError() != AL_NO_ERROR) {
302 alDeleteBuffers(1, &buffer);
304 throw sg_io_exception("OpenAL setting buffer data failed", sg_location(path.str()));
310 } // of namespace simgear