1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsWaveDecoder.h"
4#include "FileSystem/BsDataStream.h"
5
6namespace bs
7{
8#define WAVE_FORMAT_PCM 0x0001
9#define WAVE_FORMAT_EXTENDED 0xFFFE
10
11 bool WaveDecoder::isValid(const SPtr<DataStream>& stream, UINT32 offset)
12 {
13 stream->seek(offset);
14
15 INT8 header[MAIN_CHUNK_SIZE];
16 if (stream->read(header, sizeof(header)) < (sizeof(header)))
17 return false;
18
19 return (header[0] == 'R') && (header[1] == 'I') && (header[2] == 'F') && (header[3] == 'F')
20 && (header[8] == 'W') && (header[9] == 'A') && (header[10] == 'V') && (header[11] == 'E');
21 }
22
23 bool WaveDecoder::open(const SPtr<DataStream>& stream, AudioDataInfo& info, UINT32 offset)
24 {
25 if (stream == nullptr)
26 return false;
27
28 mStream = stream;
29 mStream->seek(offset + MAIN_CHUNK_SIZE);
30
31 if (!parseHeader(info))
32 {
33 LOGERR("Provided file is not a valid WAVE file.");
34 return false;
35 }
36
37 return true;
38 }
39
40 void WaveDecoder::seek(UINT32 offset)
41 {
42 mStream->seek(mDataOffset + offset * mBytesPerSample);
43 }
44
45 UINT32 WaveDecoder::read(UINT8* samples, UINT32 numSamples)
46 {
47 UINT32 numRead = (UINT32)mStream->read(samples, numSamples * mBytesPerSample);
48
49 if(mBytesPerSample == 1) // 8-bit samples are stored as unsigned, but engine convention is to store all bit depths as signed
50 {
51 for(UINT32 i = 0; i < numRead; i++)
52 {
53 INT8 val = samples[i] - 128;
54 samples[i] = *((UINT8*)&val);
55 }
56 }
57
58 return numRead;
59 }
60
61 bool WaveDecoder::parseHeader(AudioDataInfo& info)
62 {
63 bool foundData = false;
64 while (!foundData)
65 {
66 // Get sub-chunk ID and size
67 UINT8 subChunkId[4];
68 if (mStream->read(subChunkId, sizeof(subChunkId)) != sizeof(subChunkId))
69 return false;
70
71 UINT32 subChunkSize = 0;
72 if (mStream->read(&subChunkSize, sizeof(subChunkSize)) != sizeof(subChunkSize))
73 return false;
74
75 // FMT chunk
76 if (subChunkId[0] == 'f' && subChunkId[1] == 'm' && subChunkId[2] == 't' && subChunkId[3] == ' ')
77 {
78 UINT16 format = 0;
79 if (mStream->read(&format, sizeof(format)) != sizeof(format))
80 return false;
81
82 if (format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_EXTENDED)
83 {
84 LOGWRN("Wave file doesn't contain raw PCM data. Not supported.");
85 return false;
86 }
87
88 UINT16 numChannels = 0;
89 if (mStream->read(&numChannels, sizeof(numChannels)) != sizeof(numChannels))
90 return false;
91
92 UINT32 sampleRate = 0;
93 if (mStream->read(&sampleRate, sizeof(sampleRate)) != sizeof(sampleRate))
94 return false;
95
96 UINT32 byteRate = 0;
97 if (mStream->read(&byteRate, sizeof(byteRate)) != sizeof(byteRate))
98 return false;
99
100 UINT16 blockAlign = 0;
101 if (mStream->read(&blockAlign, sizeof(blockAlign)) != sizeof(blockAlign))
102 return false;
103
104 UINT16 bitDepth = 0;
105 if (mStream->read(&bitDepth, sizeof(bitDepth)) != sizeof(bitDepth))
106 return false;
107
108 info.numChannels = numChannels;
109 info.sampleRate = sampleRate;
110 info.bitDepth = bitDepth;
111
112 if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
113 {
114 LOGERR("Unsupported number of bits per sample: " + toString(bitDepth));
115 return false;
116 }
117
118 // Read extension data, and get the actual format
119 if(format == WAVE_FORMAT_EXTENDED)
120 {
121 UINT16 extensionSize = 0;
122 if (mStream->read(&extensionSize, sizeof(extensionSize)) != sizeof(extensionSize))
123 return false;
124
125 if(extensionSize != 22)
126 {
127 LOGWRN("Wave file doesn't contain raw PCM data. Not supported.");
128 return false;
129 }
130
131 UINT16 validBitDepth = 0;
132 if (mStream->read(&validBitDepth, sizeof(validBitDepth)) != sizeof(validBitDepth))
133 return false;
134
135 UINT32 channelMask = 0;
136 if (mStream->read(&channelMask, sizeof(channelMask)) != sizeof(channelMask))
137 return false;
138
139 UINT8 subFormat[16];
140 if (mStream->read(subFormat, sizeof(subFormat)) != sizeof(subFormat))
141 return false;
142
143 memcpy(&format, subFormat, sizeof(format));
144 if (format != WAVE_FORMAT_PCM)
145 {
146 LOGWRN("Wave file doesn't contain raw PCM data. Not supported.");
147 return false;
148 }
149 }
150
151 mBytesPerSample = bitDepth / 8;
152 }
153 // DATA chunk
154 else if (subChunkId[0] == 'd' && subChunkId[1] == 'a' && subChunkId[2] == 't' && subChunkId[3] == 'a')
155 {
156 info.numSamples = subChunkSize / mBytesPerSample;
157 mDataOffset = (UINT32)mStream->tell();
158
159 foundData = true;
160 }
161 // Unsupported chunk type
162 else
163 {
164 mStream->skip(subChunkSize);
165 if (mStream->eof())
166 return false;
167 }
168 }
169
170 return true;
171 }
172}