Fix types and casts for MSVC
[alure.git] / src / decoders / wave.cpp
blob1da8112b268384ec5e1da66f278323ad56fca2c8
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 static_assert(sizeof(lhs) == sizeof(rhs.mGuid), "Invalid ID size");
22 return std::equal(std::begin(lhs), std::end(lhs), rhs.mGuid.begin());
25 const IDType SUBTYPE_PCM{{{
26 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
27 0x00, 0x38, 0x9b, 0x71
28 }}};
29 const IDType SUBTYPE_FLOAT{{{
30 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
31 0x00, 0x38, 0x9b, 0x71
32 }}};
34 const IDType SUBTYPE_BFORMAT_PCM{{{
35 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
36 0xca, 0x00, 0x00, 0x00
37 }}};
38 const IDType SUBTYPE_BFORMAT_FLOAT{{{
39 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
40 0xca, 0x00, 0x00, 0x00
41 }}};
43 constexpr int CHANNELS_MONO = 0x04;
44 constexpr int CHANNELS_STEREO = 0x01 | 0x02;
45 constexpr int CHANNELS_QUAD = 0x01 | 0x02 | 0x10 | 0x20;
46 constexpr int CHANNELS_5DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400;
47 constexpr int CHANNELS_5DOT1_REAR = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20;
48 constexpr int CHANNELS_6DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400;
49 constexpr int CHANNELS_7DOT1 = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x200 | 0x400;
52 static ALuint read_le32(std::istream &stream)
54 char buf[4];
55 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
56 return 0;
57 return ((ALuint(buf[0] )&0x000000ff) | (ALuint(buf[1]<< 8)&0x0000ff00) |
58 (ALuint(buf[2]<<16)&0x00ff0000) | (ALuint(buf[3]<<24)&0xff000000));
61 static ALushort read_le16(std::istream &stream)
63 char buf[2];
64 if(!stream.read(buf, sizeof(buf)) || stream.gcount() != sizeof(buf))
65 return 0;
66 return ((ALushort(buf[0] )&0x00ff) | (ALushort(buf[1]<<8)&0xff00));
71 namespace alure
74 class WaveDecoder final : public Decoder {
75 UniquePtr<std::istream> mFile;
77 ChannelConfig mChannelConfig{ChannelConfig::Mono};
78 SampleType mSampleType{SampleType::UInt8};
79 ALuint mFrequency{0};
80 ALuint mFrameSize{0};
82 // In sample frames, relative to sample data start
83 std::pair<uint64_t,uint64_t> mLoopPts{0, 0};
85 // In bytes from beginning of file
86 std::istream::pos_type mStart{0}, mEnd{0};
87 std::istream::pos_type mCurrentPos{0};
89 public:
90 WaveDecoder(UniquePtr<std::istream> file, ChannelConfig channels, SampleType type,
91 ALuint frequency, ALuint framesize, std::istream::pos_type start,
92 std::istream::pos_type end, uint64_t loopstart, uint64_t loopend) noexcept
93 : mFile(std::move(file)), mChannelConfig(channels), mSampleType(type), mFrequency(frequency)
94 , mFrameSize(framesize), mLoopPts{loopstart,loopend}, mStart(start), mEnd(end)
95 { mCurrentPos = mFile->tellg(); }
96 ~WaveDecoder() override;
98 ALuint getFrequency() const noexcept override;
99 ChannelConfig getChannelConfig() const noexcept override;
100 SampleType getSampleType() const noexcept override;
102 uint64_t getLength() const noexcept override;
103 bool seek(uint64_t pos) noexcept override;
105 std::pair<uint64_t,uint64_t> getLoopPoints() const noexcept override;
107 ALuint read(ALvoid *ptr, ALuint count) noexcept override;
110 WaveDecoder::~WaveDecoder()
115 ALuint WaveDecoder::getFrequency() const noexcept
116 { return mFrequency; }
118 ChannelConfig WaveDecoder::getChannelConfig() const noexcept
119 { return mChannelConfig; }
121 SampleType WaveDecoder::getSampleType() const noexcept
122 { return mSampleType; }
125 uint64_t WaveDecoder::getLength() const noexcept
126 { return (mEnd - mStart) / mFrameSize; }
128 bool WaveDecoder::seek(uint64_t pos) noexcept
130 std::streamsize offset = pos*mFrameSize + mStart;
131 mFile->clear();
132 if(offset > mEnd || !mFile->seekg(offset))
133 return false;
134 mCurrentPos = offset;
135 return true;
138 std::pair<uint64_t,uint64_t> WaveDecoder::getLoopPoints() const noexcept
140 return mLoopPts;
143 ALuint WaveDecoder::read(ALvoid *ptr, ALuint count) noexcept
145 mFile->clear();
147 ALuint total = 0;
148 if(mCurrentPos < mEnd)
150 ALuint len = static_cast<ALuint>(
151 std::min<std::istream::pos_type>(count*mFrameSize, mEnd-mCurrentPos)
153 #ifdef __BIG_ENDIAN__
154 switch(mSampleType)
156 case SampleType::Float32:
157 while(total < len && mFile->good() && !mFile->eof())
159 char temp[256];
160 ALuint todo = std::min<ALuint>(len-total, sizeof(temp));
162 mFile->read(temp, todo);
163 ALuint got = static_cast<ALuint>(mFile->gcount());
165 for(ALuint i = 0;i < got;++i)
166 reinterpret_cast<char*>(ptr)[total+i] = temp[i^3];
168 mCurrentPos += got;
169 total += got;
171 total /= mFrameSize;
172 break;
174 case SampleType::Int16:
175 while(total < len && mFile->good() && !mFile->eof())
177 char temp[256];
178 ALuint todo = std::min<ALuint>(len-total, sizeof(temp));
180 mFile->read(temp, todo);
181 ALuint got = static_cast<ALuint>(mFile->gcount());
183 for(ALuint i = 0;i < got;++i)
184 reinterpret_cast<char*>(ptr)[total+i] = temp[i^1];
186 mCurrentPos += got;
187 total += got;
189 total /= mFrameSize;
190 break;
192 case SampleType::UInt8:
193 case SampleType::Mulaw:
194 #else
196 #endif
197 mFile->read(reinterpret_cast<char*>(ptr), len);
198 ALuint got = static_cast<ALuint>(mFile->gcount());
200 mCurrentPos += got;
201 total = got / mFrameSize;
205 return total;
209 SharedPtr<Decoder> WaveDecoderFactory::createDecoder(UniquePtr<std::istream> &file) noexcept
211 ChannelConfig channels = ChannelConfig::Mono;
212 SampleType type = SampleType::UInt8;
213 ALuint frequency = 0;
214 ALuint framesize = 0;
215 uint64_t loop_pts[2]{0, 0};
216 ALuint blockalign = 0;
217 ALuint framealign = 0;
219 char tag[4]{};
220 if(!file->read(tag, 4) || file->gcount() != 4 || memcmp(tag, "RIFF", 4) != 0)
221 return nullptr;
222 ALuint totalsize = read_le32(*file) & ~1u;
223 if(!file->read(tag, 4) || file->gcount() != 4 || memcmp(tag, "WAVE", 4) != 0)
224 return nullptr;
226 while(file->good() && !file->eof() && totalsize > 8)
228 if(!file->read(tag, 4) || file->gcount() != 4)
229 return nullptr;
230 ALuint size = read_le32(*file);
231 if(size < 2) return nullptr;
232 totalsize -= 8;
234 size = std::min((size+1) & ~1u, totalsize);
235 totalsize -= size;
237 if(memcmp(tag, "fmt ", 4) == 0)
239 /* 'fmt ' tag needs at least 16 bytes. */
240 if(size < 16) goto next_chunk;
242 /* format type */
243 ALushort fmttype = read_le16(*file); size -= 2;
245 /* mono or stereo data */
246 int chancount = read_le16(*file); size -= 2;
248 /* sample frequency */
249 frequency = read_le32(*file); size -= 4;
251 /* skip average bytes per second */
252 read_le32(*file); size -= 4;
254 /* bytes per block */
255 blockalign = read_le16(*file); size -= 2;
257 /* bits per sample */
258 int bitdepth = read_le16(*file); size -= 2;
260 /* Look for any extra data and try to find the format */
261 ALuint extrabytes = 0;
262 if(size >= 2)
264 extrabytes = read_le16(*file);
265 size -= 2;
267 extrabytes = std::min<ALuint>(extrabytes, size);
269 /* Format type should be 0x0001 for integer PCM data, 0x0003 for
270 * float PCM data, 0x0007 for muLaw, and 0xFFFE extensible data.
272 if(fmttype == 0x0001)
274 if(chancount == 1)
275 channels = ChannelConfig::Mono;
276 else if(chancount == 2)
277 channels = ChannelConfig::Stereo;
278 else
279 goto next_chunk;
281 if(bitdepth == 8)
282 type = SampleType::UInt8;
283 else if(bitdepth == 16)
284 type = SampleType::Int16;
285 else
286 goto next_chunk;
288 else if(fmttype == 0x0003)
290 if(chancount == 1)
291 channels = ChannelConfig::Mono;
292 else if(chancount == 2)
293 channels = ChannelConfig::Stereo;
294 else
295 goto next_chunk;
297 if(bitdepth == 32)
298 type = SampleType::Float32;
299 else
300 goto next_chunk;
302 else if(fmttype == 0x0007)
304 if(chancount == 1)
305 channels = ChannelConfig::Mono;
306 else if(chancount == 2)
307 channels = ChannelConfig::Stereo;
308 else
309 goto next_chunk;
311 if(bitdepth == 8)
312 type = SampleType::Mulaw;
313 else
314 goto next_chunk;
316 else if(fmttype == 0xFFFE)
318 if(size < 22)
319 goto next_chunk;
321 char subtype[16];
322 ALushort validbits = read_le16(*file); size -= 2;
323 ALuint chanmask = read_le32(*file); size -= 4;
324 file->read(subtype, 16); size -= file->gcount();
326 /* Padded bit depths not supported */
327 if(validbits != bitdepth)
328 goto next_chunk;
330 if(subtype == SUBTYPE_BFORMAT_PCM || subtype == SUBTYPE_BFORMAT_FLOAT)
332 if(chanmask != 0)
333 goto next_chunk;
335 if(chancount == 3)
336 channels = ChannelConfig::BFormat2D;
337 else if(chancount == 4)
338 channels = ChannelConfig::BFormat3D;
339 else
340 goto next_chunk;
342 else if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_FLOAT)
344 if(chancount == 1 && chanmask == CHANNELS_MONO)
345 channels = ChannelConfig::Mono;
346 else if(chancount == 2 && chanmask == CHANNELS_STEREO)
347 channels = ChannelConfig::Stereo;
348 else if(chancount == 4 && chanmask == CHANNELS_QUAD)
349 channels = ChannelConfig::Quad;
350 else if(chancount == 6 && (chanmask == CHANNELS_5DOT1 || chanmask == CHANNELS_5DOT1_REAR))
351 channels = ChannelConfig::X51;
352 else if(chancount == 7 && chanmask == CHANNELS_6DOT1)
353 channels = ChannelConfig::X61;
354 else if(chancount == 8 && chanmask == CHANNELS_7DOT1)
355 channels = ChannelConfig::X71;
356 else
357 goto next_chunk;
360 if(subtype == SUBTYPE_PCM || subtype == SUBTYPE_BFORMAT_PCM)
362 if(bitdepth == 8)
363 type = SampleType::UInt8;
364 else if(bitdepth == 16)
365 type = SampleType::Int16;
366 else
367 goto next_chunk;
369 else if(subtype == SUBTYPE_FLOAT || subtype == SUBTYPE_BFORMAT_FLOAT)
371 if(bitdepth == 32)
372 type = SampleType::Float32;
373 else
374 goto next_chunk;
376 else
377 goto next_chunk;
379 else
380 goto next_chunk;
382 framesize = FramesToBytes(1, channels, type);
384 /* Calculate the number of frames per block (ADPCM will need extra
385 * consideration). */
386 framealign = blockalign / framesize;
388 else if(memcmp(tag, "smpl", 4) == 0)
390 /* sampler data needs at least 36 bytes */
391 if(size < 36) goto next_chunk;
393 /* Most of this only affects MIDI sampling, but we only care about
394 * the loop definitions at the end. */
395 /*ALuint manufacturer =*/ read_le32(*file);
396 /*ALuint product =*/ read_le32(*file);
397 /*ALuint smpperiod =*/ read_le32(*file);
398 /*ALuint unitynote =*/ read_le32(*file);
399 /*ALuint pitchfrac =*/ read_le32(*file);
400 /*ALuint smptefmt =*/ read_le32(*file);
401 /*ALuint smpteoffset =*/ read_le32(*file);
402 ALuint loopcount = read_le32(*file);
403 /*ALuint extrabytes =*/ read_le32(*file);
404 size -= 36;
406 for(ALuint i = 0;i < loopcount && size >= 24;++i)
408 /*ALuint id =*/ read_le32(*file);
409 ALuint type = read_le32(*file);
410 ALuint loopstart = read_le32(*file);
411 ALuint loopend = read_le32(*file);
412 /*ALuint frac =*/ read_le32(*file);
413 ALuint numloops = read_le32(*file);
414 size -= 24;
416 /* Only handle indefinite forward loops. */
417 if(type == 0 || numloops == 0)
419 loop_pts[0] = loopstart;
420 loop_pts[1] = loopend;
421 break;
425 else if(memcmp(tag, "data", 4) == 0)
427 if(framesize == 0 || !Context::GetCurrent().isSupported(channels, type))
428 goto next_chunk;
430 /* Make sure there's at least one sample frame of audio data. */
431 std::istream::pos_type start = file->tellg();
432 std::istream::pos_type end = start + std::istream::pos_type(size - (size%framesize));
433 if(end-start >= framesize)
435 /* Loop points are byte offsets relative to the data start.
436 * Convert to sample frame offsets. */
437 return MakeShared<WaveDecoder>(std::move(file),
438 channels, type, frequency, framesize, start, end,
439 loop_pts[0] / blockalign * framealign,
440 loop_pts[1] / blockalign * framealign
445 next_chunk:
446 if(size > 0)
447 file->ignore(size);
450 return nullptr;