Remove the DYNLOAD option. It's buggy and causes problems.
[alure.git] / src / codec_wav.cpp
blob101e59112419d3940e7f0346286524c7d22e50d5
1 /*
2 * ALURE OpenAL utility library
3 * Copyright (c) 2009-2010 by Chris Robinson.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
24 #include "config.h"
26 #include "main.h"
28 #include <string.h>
29 #include <assert.h>
31 #include <istream>
34 struct wavStream : public alureStream {
35 private:
36 ALenum format;
37 int samplerate;
38 int blockAlign;
39 int sampleSize;
40 int channels;
41 long dataStart;
42 long dataLen;
43 size_t remLen;
45 public:
46 static void Init() { }
47 static void Deinit() { }
49 virtual bool IsValid()
50 { return (dataStart > 0 && format != AL_NONE); }
52 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
54 *fmt = format;
55 *frequency = samplerate;
56 *blockalign = blockAlign;
57 return true;
60 virtual ALuint GetData(ALubyte *data, ALuint bytes)
62 std::streamsize rem = ((remLen >= bytes) ? bytes : remLen) / blockAlign;
63 fstream->read(reinterpret_cast<char*>(data), rem*blockAlign);
65 std::streamsize got = fstream->gcount();
66 got -= got%blockAlign;
67 remLen -= got;
69 if(BigEndian && sampleSize == 16)
71 for(std::streamsize i = 0;i < got;i+=2)
72 swap(data[i], data[i+1]);
74 else if(BigEndian && sampleSize == 32)
76 for(std::streamsize i = 0;i < got;i+=4)
78 swap(data[i+0], data[i+3]);
79 swap(data[i+1], data[i+2]);
82 else if(BigEndian && sampleSize == 64)
84 for(std::streamsize i = 0;i < got;i+=8)
86 swap(data[i+0], data[i+7]);
87 swap(data[i+1], data[i+6]);
88 swap(data[i+2], data[i+5]);
89 swap(data[i+3], data[i+4]);
93 return got;
96 virtual bool Rewind()
98 fstream->clear();
99 if(fstream->seekg(dataStart))
101 remLen = dataLen;
102 return true;
105 SetError("Seek failed");
106 return false;
109 virtual alureInt64 GetLength()
111 alureInt64 ret = dataLen;
112 return ret / channels * 8 / sampleSize;
115 wavStream(std::istream *_fstream)
116 : alureStream(_fstream), format(0), dataStart(0)
118 ALubyte buffer[25];
119 ALuint length;
121 if(!fstream->read(reinterpret_cast<char*>(buffer), 12) ||
122 memcmp(buffer, "RIFF", 4) != 0 || memcmp(buffer+8, "WAVE", 4) != 0)
123 return;
125 while(!dataStart || format == AL_NONE)
127 char tag[4];
128 if(!fstream->read(tag, 4))
129 break;
131 /* read chunk length */
132 length = read_le32(fstream);
134 if(memcmp(tag, "fmt ", 4) == 0 && length >= 16)
136 /* Data type (should be 1 for PCM data, 3 for float PCM data,
137 * 7 for muLaw, and 17 for IMA4 data) */
138 int type = read_le16(fstream);
139 if(type != 0x0001 && type != 0x0003 && type != 0x0007 &&
140 type != 0x0011)
141 break;
143 /* mono or stereo data */
144 channels = read_le16(fstream);
146 /* sample frequency */
147 samplerate = read_le32(fstream);
149 /* skip average bytes per second */
150 fstream->ignore(4);
152 /* bytes per block */
153 blockAlign = read_le16(fstream);
154 if(blockAlign == 0)
155 break;
157 /* bits per sample */
158 sampleSize = read_le16(fstream);
160 length -= 16;
162 /* Look for any extra data and try to find the format */
163 ALuint extrabytes = 0;
164 if(length >= 2)
166 extrabytes = read_le16(fstream);
167 length -= 2;
169 extrabytes = std::min<ALuint>(extrabytes, length);
171 if(type == 0x0001)
172 format = GetSampleFormat(channels, sampleSize, false);
173 else if(type == 0x0003)
174 format = GetSampleFormat(channels, sampleSize, true);
175 else if(type == 0x0007)
177 if(sampleSize == 8)
179 if(channels == 1)
180 format = AL_FORMAT_MONO_MULAW;
181 else if(channels == 2)
182 format = AL_FORMAT_STEREO_MULAW;
183 else if(channels == 4)
184 format = AL_FORMAT_QUAD_MULAW;
185 else if(channels == 6)
186 format = AL_FORMAT_51CHN_MULAW;
187 else if(channels == 7)
188 format = AL_FORMAT_61CHN_MULAW;
189 else if(channels == 8)
190 format = AL_FORMAT_71CHN_MULAW;
193 else if(type == 0x0011 && extrabytes >= 2)
195 int samples = read_le16(fstream);
196 length -= 2;
198 /* AL_EXT_IMA4 only supports 36 bytes-per-channel block
199 * alignment, which has 65 uncompressed sample frames */
200 if(blockAlign == 36*channels && samples == 65*channels &&
201 alIsExtensionPresent("AL_EXT_IMA4"))
203 if(channels == 1)
204 format = AL_FORMAT_MONO_IMA4;
205 else if(channels == 2)
206 format = AL_FORMAT_STEREO_IMA4;
210 else if(memcmp(tag, "data", 4) == 0)
212 dataStart = fstream->tellg();
213 dataLen = remLen = length;
216 fstream->seekg(length, std::ios_base::cur);
219 if(dataStart > 0 && format != AL_NONE)
220 fstream->seekg(dataStart);
223 virtual ~wavStream()
226 // Priority = 10, prefer this decoder over external ones
227 static DecoderDecl<wavStream,10> wavStream_decoder;
228 Decoder &alure_init_wav(void)
229 { return wavStream_decoder; }