Merge pull request #40 from McSinyx/travis
[alure.git] / src / decoders / wave.cpp
blob53c3fb356dacc7ab1678725d1e80a6a9d75687d6
2 #include "wave.hpp"
4 #include <stdexcept>
5 #include <iostream>
6 #include <cstring>
8 #include "buffer.h"
11 namespace {
13 constexpr int FORMAT_TYPE_PCM = 0x0001;
14 constexpr int FORMAT_TYPE_FLOAT = 0x0003;
15 constexpr int FORMAT_TYPE_MULAW = 0x0007;
16 constexpr int FORMAT_TYPE_EXTENSIBLE = 0xFFFE;
18 struct IDType {
19 using ubyte16 = ALubyte[16];
20 alure::Array<ALubyte,16> mGuid;
23 inline bool operator==(const IDType::ubyte16 &lhs, const IDType &rhs)
25 static_assert(sizeof(lhs) == sizeof(rhs.mGuid), "Invalid ID size");
26 return std::equal(std::begin(lhs), std::end(lhs), rhs.mGuid.begin());
29 const IDType SUBTYPE_PCM{{{
30 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
31 0x00, 0x38, 0x9b, 0x71
32 }}};
33 const IDType SUBTYPE_FLOAT{{{
34 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
35 0x00, 0x38, 0x9b, 0x71
36 }}};
38 const IDType SUBTYPE_BFORMAT_PCM{{{
39 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
40 0xca, 0x00, 0x00, 0x00
41 }}};
42 const IDType SUBTYPE_BFORMAT_FLOAT{{{
43 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
44 0xca, 0x00, 0x00, 0x00
45 }}};
47 constexpr int CHANNELS_MONO = 0x04;
48 constexpr int CHANNELS_STEREO = 0x01 | 0x02;
49 constexpr int CHANNELS_QUAD = 0x01 | 0x02 | 0x10 | 0x20;
50 constexpr int CHANNELS_5DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400;
51 constexpr int CHANNELS_5DOT1_REAR = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20;
52 constexpr int CHANNELS_6DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400;
53 constexpr int CHANNELS_7DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x200 | 0x400;
56 ALuint read_le32(std::istream &stream)
58 char buf[4];
59 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
60 return 0;
61 return ((ALuint(buf[0] )&0x000000ff) | (ALuint(buf[1]<< 8)&0x0000ff00) |
62 (ALuint(buf[2]<<16)&0x00ff0000) | (ALuint(buf[3]<<24)&0xff000000));
65 ALushort read_le16(std::istream &stream)
67 char buf[2];
68 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
69 return 0;
70 return ((ALushort(buf[0] )&0x00ff) | (ALushort(buf[1]<<8)&0xff00));
73 } // namespace
75 namespace alure {
77 class WaveDecoder final : public Decoder {
78 UniquePtr<std::istream> mFile;
80 ChannelConfig mChannelConfig{ChannelConfig::Mono};
81 SampleType mSampleType{SampleType::UInt8};
82 ALuint mFrequency{0};
83 ALuint mFrameSize{0};
85 // In sample frames, relative to sample data start
86 std::pair<uint64_t,uint64_t> mLoopPts{0, 0};
88 // In bytes from beginning of file
89 std::istream::pos_type mStart{0}, mEnd{0};
90 std::istream::pos_type mCurrentPos{0};
92 public:
93 WaveDecoder(UniquePtr<std::istream> file, ChannelConfig channels, SampleType type,
94 ALuint frequency, ALuint framesize, std::istream::pos_type start,
95 std::istream::pos_type end, uint64_t loopstart, uint64_t loopend) noexcept
96 : mFile(std::move(file)), mChannelConfig(channels), mSampleType(type), mFrequency(frequency)
97 , mFrameSize(framesize), mLoopPts{loopstart,loopend}, mStart(start), mEnd(end)
98 { mCurrentPos = mFile->tellg(); }
99 ~WaveDecoder() override { }
101 ALuint getFrequency() const noexcept override;
102 ChannelConfig getChannelConfig() const noexcept override;
103 SampleType getSampleType() const noexcept override;
105 uint64_t getLength() const noexcept override;
106 bool seek(uint64_t pos) noexcept override;
108 std::pair<uint64_t,uint64_t> getLoopPoints() const noexcept override;
110 ALuint read(ALvoid *ptr, ALuint count) noexcept override;
113 ALuint WaveDecoder::getFrequency() const noexcept { return mFrequency; }
114 ChannelConfig WaveDecoder::getChannelConfig() const noexcept { return mChannelConfig; }
115 SampleType WaveDecoder::getSampleType() const noexcept { return mSampleType; }
117 uint64_t WaveDecoder::getLength() const noexcept
118 { return (mEnd - mStart) / mFrameSize; }
120 bool WaveDecoder::seek(uint64_t pos) noexcept
122 std::streamsize offset = pos*mFrameSize + mStart;
123 mFile->clear();
124 if(offset > mEnd || !mFile->seekg(offset))
125 return false;
126 mCurrentPos = offset;
127 return true;
130 std::pair<uint64_t,uint64_t> WaveDecoder::getLoopPoints() const noexcept { return mLoopPts; }
132 ALuint WaveDecoder::read(ALvoid *ptr, ALuint count) noexcept
134 mFile->clear();
136 ALuint total = 0;
137 if(mCurrentPos < mEnd)
139 ALuint len = static_cast<ALuint>(
140 std::min<std::istream::pos_type>(count*mFrameSize, mEnd-mCurrentPos)
142 #ifdef __BIG_ENDIAN__
143 switch(mSampleType)
145 case SampleType::Float32:
146 while(total < len && mFile->good() && !mFile->eof())
148 char temp[256];
149 ALuint todo = std::min<ALuint>(len-total, sizeof(temp));
151 mFile->read(temp, todo);
152 ALuint got = static_cast<ALuint>(mFile->gcount());
154 for(ALuint i = 0;i < got;++i)
155 reinterpret_cast<char*>(ptr)[total+i] = temp[i^3];
157 mCurrentPos += got;
158 total += got;
159 if(got < todo) break;
161 total /= mFrameSize;
162 break;
164 case SampleType::Int16:
165 while(total < len && mFile->good() && !mFile->eof())
167 char temp[256];
168 ALuint todo = std::min<ALuint>(len-total, sizeof(temp));
170 mFile->read(temp, todo);
171 ALuint got = static_cast<ALuint>(mFile->gcount());
173 for(ALuint i = 0;i < got;++i)
174 reinterpret_cast<char*>(ptr)[total+i] = temp[i^1];
176 mCurrentPos += got;
177 total += got;
178 if(got < todo) break;
180 total /= mFrameSize;
181 break;
183 case SampleType::UInt8:
184 case SampleType::Mulaw:
185 #else
187 #endif
188 mFile->read(reinterpret_cast<char*>(ptr), len);
189 ALuint got = static_cast<ALuint>(mFile->gcount());
191 mCurrentPos += got;
192 total = got / mFrameSize;
196 return total;
200 SharedPtr<Decoder> WaveDecoderFactory::createDecoder(UniquePtr<std::istream> &file) noexcept
202 ChannelConfig channels = ChannelConfig::Mono;
203 SampleType type = SampleType::UInt8;
204 ALuint frequency = 0;
205 ALuint framesize = 0;
206 uint64_t loop_pts[2]{0, 0};
207 ALuint blockalign = 0;
208 ALuint framealign = 0;
210 char tag_[4]{};
211 if(!file->read(tag_, 4) || file->gcount() != 4 || memcmp(tag_, "RIFF", 4) != 0)
212 return nullptr;
213 ALuint totalsize = read_le32(*file) & ~1u;
214 if(!file->read(tag_, 4) || file->gcount() != 4 || memcmp(tag_, "WAVE", 4) != 0)
215 return nullptr;
217 while(file->good() && !file->eof() && totalsize > 8)
219 if(!file->read(tag_, 4) || file->gcount() != 4)
220 return nullptr;
221 ALuint size = read_le32(*file);
222 if(size < 2) return nullptr;
223 totalsize -= 8;
225 size = std::min(size, totalsize);
226 ALuint padbyte = size & 1u;
227 totalsize -= size+padbyte;
229 StringView tag(tag_, 4);
230 if(tag == "fmt ")
232 /* 'fmt ' tag needs at least 16 bytes. */
233 if(size < 16) goto next_chunk;
235 int fmttype = read_le16(*file); size -= 2;
236 int chancount = read_le16(*file); size -= 2;
237 frequency = read_le32(*file); size -= 4;
239 /* skip average bytes per second */
240 read_le32(*file); size -= 4;
242 blockalign = read_le16(*file); size -= 2;
243 int bitdepth = read_le16(*file); size -= 2;
245 /* Look for any extra data and try to find the format */
246 ALuint extrabytes = 0;
247 if(size >= 2)
249 extrabytes = read_le16(*file);
250 size -= 2;
252 extrabytes = std::min<ALuint>(extrabytes, size);
254 if(fmttype == FORMAT_TYPE_PCM)
256 if(chancount == 1)
257 channels = ChannelConfig::Mono;
258 else if(chancount == 2)
259 channels = ChannelConfig::Stereo;
260 else
261 goto next_chunk;
263 if(bitdepth == 8)
264 type = SampleType::UInt8;
265 else if(bitdepth == 16)
266 type = SampleType::Int16;
267 else
268 goto next_chunk;
270 else if(fmttype == FORMAT_TYPE_FLOAT)
272 if(chancount == 1)
273 channels = ChannelConfig::Mono;
274 else if(chancount == 2)
275 channels = ChannelConfig::Stereo;
276 else
277 goto next_chunk;
279 if(bitdepth == 32)
280 type = SampleType::Float32;
281 else
282 goto next_chunk;
284 else if(fmttype == FORMAT_TYPE_MULAW)
286 if(chancount == 1)
287 channels = ChannelConfig::Mono;
288 else if(chancount == 2)
289 channels = ChannelConfig::Stereo;
290 else
291 goto next_chunk;
293 if(bitdepth == 8)
294 type = SampleType::Mulaw;
295 else
296 goto next_chunk;
298 else if(fmttype == FORMAT_TYPE_EXTENSIBLE)
300 if(size < 22) goto next_chunk;
302 ALubyte subtype[16];
303 ALushort validbits = read_le16(*file); size -= 2;
304 ALuint chanmask = read_le32(*file); size -= 4;
305 file->read(reinterpret_cast<char*>(subtype), 16);
306 size -= static_cast<ALuint>(file->gcount());
308 /* Padded bit depths not supported */
309 if(validbits != bitdepth)
310 goto next_chunk;
312 if(subtype == SUBTYPE_BFORMAT_PCM || subtype == SUBTYPE_BFORMAT_FLOAT)
314 if(chanmask != 0)
315 goto next_chunk;
317 if(chancount == 3)
318 channels = ChannelConfig::BFormat2D;
319 else if(chancount == 4)
320 channels = ChannelConfig::BFormat3D;
321 else
322 goto next_chunk;
324 else if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_FLOAT)
326 if(chancount == 1 && chanmask == CHANNELS_MONO)
327 channels = ChannelConfig::Mono;
328 else if(chancount == 2 && chanmask == CHANNELS_STEREO)
329 channels = ChannelConfig::Stereo;
330 else if(chancount == 4 && chanmask == CHANNELS_QUAD)
331 channels = ChannelConfig::Quad;
332 else if(chancount == 6 && (chanmask == CHANNELS_5DOT1 || chanmask == CHANNELS_5DOT1_REAR))
333 channels = ChannelConfig::X51;
334 else if(chancount == 7 && chanmask == CHANNELS_6DOT1)
335 channels = ChannelConfig::X61;
336 else if(chancount == 8 && chanmask == CHANNELS_7DOT1)
337 channels = ChannelConfig::X71;
338 else
339 goto next_chunk;
342 if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_BFORMAT_PCM)
344 if(bitdepth == 8)
345 type = SampleType::UInt8;
346 else if(bitdepth == 16)
347 type = SampleType::Int16;
348 else
349 goto next_chunk;
351 else if(subtype == SUBTYPE_FLOAT || subtype == SUBTYPE_BFORMAT_FLOAT)
353 if(bitdepth == 32)
354 type = SampleType::Float32;
355 else
356 goto next_chunk;
358 else
359 goto next_chunk;
361 else
362 goto next_chunk;
364 framesize = FramesToBytes(1, channels, type);
366 /* Calculate the number of frames per block (ADPCM will need extra
367 * consideration). */
368 framealign = blockalign / framesize;
370 else if(tag == "smpl")
372 /* sampler data needs at least 36 bytes */
373 if(size < 36) goto next_chunk;
375 /* Most of this only affects MIDI sampling, but we only care about
376 * the loop definitions at the end. */
377 /*ALuint manufacturer =*/ read_le32(*file);
378 /*ALuint product =*/ read_le32(*file);
379 /*ALuint smpperiod =*/ read_le32(*file);
380 /*ALuint unitynote =*/ read_le32(*file);
381 /*ALuint pitchfrac =*/ read_le32(*file);
382 /*ALuint smptefmt =*/ read_le32(*file);
383 /*ALuint smpteoffset =*/ read_le32(*file);
384 ALuint loopcount = read_le32(*file);
385 /*ALuint extrabytes =*/ read_le32(*file);
386 size -= 36;
388 for(ALuint i = 0;i < loopcount && size >= 24;++i)
390 /*ALuint id =*/ read_le32(*file);
391 ALuint type = read_le32(*file);
392 ALuint loopstart = read_le32(*file);
393 ALuint loopend = read_le32(*file);
394 /*ALuint frac =*/ read_le32(*file);
395 ALuint numloops = read_le32(*file);
396 size -= 24;
398 /* Only handle indefinite forward loops. */
399 if(type == 0 && numloops == 0)
401 loop_pts[0] = loopstart;
402 loop_pts[1] = loopend;
403 break;
407 else if(tag == "data")
409 if(framesize == 0 || !Context::GetCurrent().isSupported(channels, type))
410 goto next_chunk;
412 /* Make sure there's at least one sample frame of audio data. */
413 std::istream::pos_type start = file->tellg();
414 std::istream::pos_type end = start + std::istream::pos_type(size - (size%framesize));
415 if(end-start >= framesize)
417 /* Loop points are byte offsets relative to the data start.
418 * Convert to sample frame offsets. */
419 return MakeShared<WaveDecoder>(std::move(file),
420 channels, type, frequency, framesize, start, end,
421 loop_pts[0] / blockalign * framealign,
422 loop_pts[1] / blockalign * framealign
427 next_chunk:
428 size += padbyte;
429 if(size > 0)
430 file->ignore(size);
433 return nullptr;
436 } // namespace alure