Use a wrapper type for the wave format subtype
[alure.git] / src / decoders / wave.cpp
blob54974dc510bc7df47b54f7e8d0edee48d540d003
2 #include "wave.hpp"
4 #include <stdexcept>
5 #include <iostream>
6 #include <cstring>
8 #include "buffer.h"
11 namespace
14 struct IDType {
15 alure::Array<ALubyte,16> mGuid;
16 using char16 = char[16];
19 inline bool operator==(const IDType::char16 &lhs, const IDType &rhs)
21 auto liter = std::begin(lhs);
22 for(ALubyte c : rhs.mGuid)
24 if(liter == std::end(lhs) || c != *liter)
25 return false;
27 return (liter == std::end(lhs));
30 const IDType SUBTYPE_PCM{{{
31 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
32 0x00, 0x38, 0x9b, 0x71
33 }}};
34 const IDType SUBTYPE_FLOAT{{{
35 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
36 0x00, 0x38, 0x9b, 0x71
37 }}};
39 const IDType SUBTYPE_BFORMAT_PCM{{{
40 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
41 0xca, 0x00, 0x00, 0x00
42 }}};
43 const IDType SUBTYPE_BFORMAT_FLOAT{{{
44 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
45 0xca, 0x00, 0x00, 0x00
46 }}};
48 constexpr int CHANNELS_MONO = 0x04;
49 constexpr int CHANNELS_STEREO = 0x01 | 0x02;
50 constexpr int CHANNELS_QUAD = 0x01 | 0x02 | 0x10 | 0x20;
51 constexpr int CHANNELS_5DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400;
52 constexpr int CHANNELS_5DOT1_REAR = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20;
53 constexpr int CHANNELS_6DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400;
54 constexpr int CHANNELS_7DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x200 | 0x400;
57 static ALuint read_le32(std::istream &stream)
59 char buf[4];
60 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
61 return 0;
62 return ((ALuint(buf[0] )&0x000000ff) | (ALuint(buf[1]<< 8)&0x0000ff00) |
63 (ALuint(buf[2]<<16)&0x00ff0000) | (ALuint(buf[3]<<24)&0xff000000));
66 static ALushort read_le16(std::istream &stream)
68 char buf[2];
69 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
70 return 0;
71 return ((ALushort(buf[0] )&0x00ff) | (ALushort(buf[1]<<8)&0xff00));
76 namespace alure
79 class WaveDecoder final : public Decoder {
80 UniquePtr<std::istream> mFile;
82 ChannelConfig mChannelConfig;
83 SampleType mSampleType;
84 ALuint mFrequency;
85 ALuint mFrameSize;
87 // In sample frames, relative to sample data start
88 std::pair<ALuint,ALuint> mLoopPts;
90 // In bytes from beginning of file
91 std::istream::pos_type mStart, mEnd;
93 public:
94 WaveDecoder(UniquePtr<std::istream> file, ChannelConfig channels, SampleType type, ALuint frequency, ALuint framesize,
95 std::istream::pos_type start, std::istream::pos_type end, ALuint loopstart, ALuint loopend)
96 : mFile(std::move(file)), mChannelConfig(channels), mSampleType(type), mFrequency(frequency)
97 , mFrameSize(framesize), mLoopPts{loopstart,loopend}, mStart(start), mEnd(end)
98 { }
99 ~WaveDecoder() override;
101 ALuint getFrequency() const override;
102 ChannelConfig getChannelConfig() const override;
103 SampleType getSampleType() const override;
105 uint64_t getLength() const override;
106 bool seek(uint64_t pos) override;
108 std::pair<uint64_t,uint64_t> getLoopPoints() const override;
110 ALuint read(ALvoid *ptr, ALuint count) override;
113 WaveDecoder::~WaveDecoder()
118 ALuint WaveDecoder::getFrequency() const
120 return mFrequency;
123 ChannelConfig WaveDecoder::getChannelConfig() const
125 return mChannelConfig;
128 SampleType WaveDecoder::getSampleType() const
130 return mSampleType;
134 uint64_t WaveDecoder::getLength() const
136 return (mEnd - mStart) / mFrameSize;
139 bool WaveDecoder::seek(uint64_t pos)
141 std::streamsize offset = pos*mFrameSize + mStart;
142 mFile->clear();
143 if(offset > mEnd || !mFile->seekg(offset))
144 return false;
145 return true;
148 std::pair<uint64_t,uint64_t> WaveDecoder::getLoopPoints() const
150 return mLoopPts;
153 ALuint WaveDecoder::read(ALvoid *ptr, ALuint count)
155 mFile->clear();
157 auto pos = mFile->tellg();
158 size_t len = count * mFrameSize;
159 ALuint total = 0;
161 if(pos < mEnd)
163 len = std::min<std::istream::pos_type>(len, mEnd-pos);
164 #ifdef __BIG_ENDIAN__
165 switch(mSampleType)
167 case SampleType::Float32:
168 while(total < len && mFile->good() && !mFile->eof())
170 char temp[256];
171 size_t todo = std::min(len-total, sizeof(temp));
173 mFile->read(temp, todo);
174 std::streamsize got = mFile->gcount();
176 for(std::streamsize i = 0;i < got;++i)
177 reinterpret_cast<char*>(ptr)[total+i] = temp[i^3];
179 total += got;
181 total /= mFrameSize;
182 break;
184 case SampleType::Int16:
185 while(total < len && mFile->good() && !mFile->eof())
187 char temp[256];
188 size_t todo = std::min(len-total, sizeof(temp));
190 mFile->read(temp, todo);
191 std::streamsize got = mFile->gcount();
193 for(std::streamsize i = 0;i < got;++i)
194 reinterpret_cast<char*>(ptr)[total+i] = temp[i^1];
196 total += got;
198 total /= mFrameSize;
199 break;
201 case SampleType::UInt8:
202 case SampleType::Mulaw:
203 #else
205 #endif
206 mFile->read(reinterpret_cast<char*>(ptr), len);
207 total = mFile->gcount() / mFrameSize;
211 return total;
215 SharedPtr<Decoder> WaveDecoderFactory::createDecoder(UniquePtr<std::istream> &file)
217 ChannelConfig channels = ChannelConfig::Mono;
218 SampleType type = SampleType::UInt8;
219 ALuint frequency = 0;
220 ALuint framesize = 0;
221 ALuint loop_pts[2]{0, 0};
222 ALuint blockalign = 0;
223 ALuint framealign = 0;
225 char tag[4]{};
226 if(!file->read(tag, 4) || file->gcount() != 4 || memcmp(tag, "RIFF", 4) != 0)
227 return nullptr;
228 ALuint totalsize = read_le32(*file) & ~1u;
229 if(!file->read(tag, 4) || file->gcount() != 4 || memcmp(tag, "WAVE", 4) != 0)
230 return nullptr;
232 while(file->good() && !file->eof() && totalsize > 8)
234 if(!file->read(tag, 4) || file->gcount() != 4)
235 return nullptr;
236 ALuint size = read_le32(*file);
237 if(size < 2) return nullptr;
238 totalsize -= 8;
240 size = std::min((size+1) & ~1u, totalsize);
241 totalsize -= size;
243 if(memcmp(tag, "fmt ", 4) == 0)
245 /* 'fmt ' tag needs at least 16 bytes. */
246 if(size < 16) goto next_chunk;
248 /* format type */
249 ALushort fmttype = read_le16(*file); size -= 2;
251 /* mono or stereo data */
252 int chancount = read_le16(*file); size -= 2;
254 /* sample frequency */
255 frequency = read_le32(*file); size -= 4;
257 /* skip average bytes per second */
258 read_le32(*file); size -= 4;
260 /* bytes per block */
261 blockalign = read_le16(*file); size -= 2;
263 /* bits per sample */
264 int bitdepth = read_le16(*file); size -= 2;
266 /* Look for any extra data and try to find the format */
267 ALuint extrabytes = 0;
268 if(size >= 2)
270 extrabytes = read_le16(*file);
271 size -= 2;
273 extrabytes = std::min<ALuint>(extrabytes, size);
275 /* Format type should be 0x0001 for integer PCM data, 0x0003 for
276 * float PCM data, 0x0007 for muLaw, and 0xFFFE extensible data.
278 if(fmttype == 0x0001)
280 if(chancount == 1)
281 channels = ChannelConfig::Mono;
282 else if(chancount == 2)
283 channels = ChannelConfig::Stereo;
284 else
285 goto next_chunk;
287 if(bitdepth == 8)
288 type = SampleType::UInt8;
289 else if(bitdepth == 16)
290 type = SampleType::Int16;
291 else
292 goto next_chunk;
294 else if(fmttype == 0x0003)
296 if(chancount == 1)
297 channels = ChannelConfig::Mono;
298 else if(chancount == 2)
299 channels = ChannelConfig::Stereo;
300 else
301 goto next_chunk;
303 if(bitdepth == 32)
304 type = SampleType::Float32;
305 else
306 goto next_chunk;
308 else if(fmttype == 0x0007)
310 if(chancount == 1)
311 channels = ChannelConfig::Mono;
312 else if(chancount == 2)
313 channels = ChannelConfig::Stereo;
314 else
315 goto next_chunk;
317 if(bitdepth == 8)
318 type = SampleType::Mulaw;
319 else
320 goto next_chunk;
322 else if(fmttype == 0xFFFE)
324 if(size < 22)
325 goto next_chunk;
327 char subtype[16];
328 ALushort validbits = read_le16(*file); size -= 2;
329 ALuint chanmask = read_le32(*file); size -= 4;
330 file->read(subtype, 16); size -= file->gcount();
332 /* Padded bit depths not supported */
333 if(validbits != bitdepth)
334 goto next_chunk;
336 if(subtype == SUBTYPE_BFORMAT_PCM || subtype == SUBTYPE_BFORMAT_FLOAT)
338 if(chanmask != 0)
339 goto next_chunk;
341 if(chancount == 3)
342 channels = ChannelConfig::BFormat2D;
343 else if(chancount == 4)
344 channels = ChannelConfig::BFormat3D;
345 else
346 goto next_chunk;
348 else if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_FLOAT)
350 if(chancount == 1 && chanmask == CHANNELS_MONO)
351 channels = ChannelConfig::Mono;
352 else if(chancount == 2 && chanmask == CHANNELS_STEREO)
353 channels = ChannelConfig::Stereo;
354 else if(chancount == 4 && chanmask == CHANNELS_QUAD)
355 channels = ChannelConfig::Quad;
356 else if(chancount == 6 && (chanmask == CHANNELS_5DOT1 || chanmask == CHANNELS_5DOT1_REAR))
357 channels = ChannelConfig::X51;
358 else if(chancount == 7 && chanmask == CHANNELS_6DOT1)
359 channels = ChannelConfig::X61;
360 else if(chancount == 8 && chanmask == CHANNELS_7DOT1)
361 channels = ChannelConfig::X71;
362 else
363 goto next_chunk;
366 if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_BFORMAT_PCM)
368 if(bitdepth == 8)
369 type = SampleType::UInt8;
370 else if(bitdepth == 16)
371 type = SampleType::Int16;
372 else
373 goto next_chunk;
375 else if(subtype == SUBTYPE_FLOAT || subtype == SUBTYPE_BFORMAT_FLOAT)
377 if(bitdepth == 32)
378 type = SampleType::Float32;
379 else
380 goto next_chunk;
382 else
383 goto next_chunk;
385 else
386 goto next_chunk;
388 framesize = FramesToBytes(1, channels, type);
390 /* Calculate the number of frames per block (ADPCM will need extra
391 * consideration). */
392 framealign = blockalign / framesize;
394 else if(memcmp(tag, "smpl", 4) == 0)
396 /* sampler data needs at least 36 bytes */
397 if(size < 36) goto next_chunk;
399 /* Most of this only affects MIDI sampling, but we only care about
400 * the loop definitions at the end. */
401 /*ALuint manufacturer =*/ read_le32(*file);
402 /*ALuint product =*/ read_le32(*file);
403 /*ALuint smpperiod =*/ read_le32(*file);
404 /*ALuint unitynote =*/ read_le32(*file);
405 /*ALuint pitchfrac =*/ read_le32(*file);
406 /*ALuint smptefmt =*/ read_le32(*file);
407 /*ALuint smpteoffset =*/ read_le32(*file);
408 ALuint loopcount = read_le32(*file);
409 /*ALuint extrabytes =*/ read_le32(*file);
410 size -= 36;
412 for(ALuint i = 0;i < loopcount && size >= 24;++i)
414 /*ALuint id =*/ read_le32(*file);
415 ALuint type = read_le32(*file);
416 ALuint loopstart = read_le32(*file);
417 ALuint loopend = read_le32(*file);
418 /*ALuint frac =*/ read_le32(*file);
419 ALuint numloops = read_le32(*file);
420 size -= 24;
422 /* Only handle indefinite forward loops. */
423 if(type == 0 || numloops == 0)
425 loop_pts[0] = loopstart;
426 loop_pts[1] = loopend;
427 break;
431 else if(memcmp(tag, "data", 4) == 0)
433 if(framesize == 0 || !Context::GetCurrent().isSupported(channels, type))
434 goto next_chunk;
436 /* Make sure there's at least one sample frame of audio data. */
437 std::istream::pos_type start = file->tellg();
438 std::istream::pos_type end = start + std::istream::pos_type(size - (size%framesize));
439 if(end-start >= framesize)
441 /* Loop points are byte offsets relative to the data start.
442 * Convert to sample frame offsets. */
443 return MakeShared<WaveDecoder>(std::move(file),
444 channels, type, frequency, framesize, start, end,
445 loop_pts[0] / blockalign * framealign,
446 loop_pts[1] / blockalign * framealign
451 next_chunk:
452 if(size > 0)
453 file->ignore(size);
456 return nullptr;