1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <algorithm>
17 #include <string.h>
18
19 #include <android/log.h>
20
21 #include "stream/InputStream.h"
22
23 #include "AudioEncoding.h"
24 #include "WavRIFFChunkHeader.h"
25 #include "WavFmtChunkHeader.h"
26 #include "WavChunkHeader.h"
27 #include "WavStreamReader.h"
28
29 static const char *TAG = "WavStreamReader";
30
31 static constexpr int kConversionBufferFrames = 16;
32
33 namespace parselib {
34
WavStreamReader(InputStream * stream)35 WavStreamReader::WavStreamReader(InputStream *stream) {
36 mStream = stream;
37
38 mWavChunk = nullptr;
39 mFmtChunk = nullptr;
40 mDataChunk = nullptr;
41
42 mAudioDataStartPos = -1;
43 }
44
getSampleEncoding()45 int WavStreamReader::getSampleEncoding() {
46 if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
47 switch (mFmtChunk->mSampleSize) {
48 case 8:
49 return AudioEncoding::PCM_8;
50
51 case 16:
52 return AudioEncoding::PCM_16;
53
54 case 24:
55 // TODO - Support 24-bit WAV data
56 return AudioEncoding::INVALID; // for now
57
58 default:
59 return AudioEncoding::INVALID;
60 }
61 } else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
62 return AudioEncoding::PCM_IEEEFLOAT;
63 }
64
65 return AudioEncoding::INVALID;
66 }
67
parse()68 void WavStreamReader::parse() {
69 RiffID tag;
70
71 while (true) {
72 int numRead = mStream->peek(&tag, sizeof(tag));
73 if (numRead <= 0) {
74 break; // done
75 }
76
77 // char *tagStr = (char *) &tag;
78 // __android_log_print(ANDROID_LOG_INFO, TAG, "[%c%c%c%c]",
79 // tagStr[0], tagStr[1], tagStr[2], tagStr[3]);
80
81 std::shared_ptr<WavChunkHeader> chunk = nullptr;
82 if (tag == WavRIFFChunkHeader::RIFFID_RIFF) {
83 chunk = mWavChunk = std::make_shared<WavRIFFChunkHeader>(WavRIFFChunkHeader(tag));
84 mWavChunk->read(mStream);
85 } else if (tag == WavFmtChunkHeader::RIFFID_FMT) {
86 chunk = mFmtChunk = std::make_shared<WavFmtChunkHeader>(WavFmtChunkHeader(tag));
87 mFmtChunk->read(mStream);
88 } else if (tag == WavChunkHeader::RIFFID_DATA) {
89 chunk = mDataChunk = std::make_shared<WavChunkHeader>(WavChunkHeader(tag));
90 mDataChunk->read(mStream);
91 // We are now positioned at the start of the audio data.
92 mAudioDataStartPos = mStream->getPos();
93 mStream->advance(mDataChunk->mChunkSize);
94 } else {
95 chunk = std::make_shared<WavChunkHeader>(WavChunkHeader(tag));
96 chunk->read(mStream);
97 mStream->advance(chunk->mChunkSize); // skip the body
98 }
99
100 mChunkMap[tag] = chunk;
101 }
102
103 if (mDataChunk != 0) {
104 mStream->setPos(mAudioDataStartPos);
105 }
106 }
107
108 // Data access
positionToAudio()109 void WavStreamReader::positionToAudio() {
110 if (mDataChunk != 0) {
111 mStream->setPos(mAudioDataStartPos);
112 }
113 }
114
115 /**
116 * Read and convert samples in PCM8 format to float
117 */
getDataFloat_PCM8(float * buff,int numFrames)118 int WavStreamReader::getDataFloat_PCM8(float *buff, int numFrames) {
119 int numChannels = mFmtChunk->mNumChannels;
120
121 int buffOffset = 0;
122 int totalFramesRead = 0;
123
124 static constexpr int kSampleSize = sizeof(u_int8_t);
125 static constexpr float kSampleFullScale = (float)0x80;
126 static constexpr float kInverseScale = 1.0f / kSampleFullScale;
127
128 u_int8_t readBuff[kConversionBufferFrames * numChannels];
129 int framesLeft = numFrames;
130 while (framesLeft > 0) {
131 int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
132 //__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
133 int numFramesRead =
134 mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
135 (kSampleSize * numChannels);
136 totalFramesRead += numFramesRead;
137
138 // Convert & Scale
139 for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
140 // PCM8 is unsigned, so we need to make it signed before scaling/converting
141 buff[buffOffset++] = ((float) readBuff[offset] - kSampleFullScale)
142 * kInverseScale;
143 }
144
145 if (numFramesRead < framesThisRead) {
146 break; // none left
147 }
148
149 framesLeft -= framesThisRead;
150 }
151
152 return totalFramesRead;
153 }
154
155 /**
156 * Read and convert samples in PCM16 format to float
157 */
getDataFloat_PCM16(float * buff,int numFrames)158 int WavStreamReader::getDataFloat_PCM16(float *buff, int numFrames) {
159 int numChannels = mFmtChunk->mNumChannels;
160
161 int buffOffset = 0;
162 int totalFramesRead = 0;
163
164 static constexpr int kSampleSize = sizeof(int16_t);
165 static constexpr float kSampleFullScale = (float) 0x8000;
166 static constexpr float kInverseScale = 1.0f / kSampleFullScale;
167
168 int16_t readBuff[kConversionBufferFrames * numChannels];
169 int framesLeft = numFrames;
170 while (framesLeft > 0) {
171 int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
172 //__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
173 int numFramesRead =
174 mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
175 (kSampleSize * numChannels);
176 totalFramesRead += numFramesRead;
177
178 // Convert & Scale
179 for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
180 buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
181 }
182
183 if (numFramesRead < framesThisRead) {
184 break; // none left
185 }
186
187 framesLeft -= framesThisRead;
188 }
189
190 return totalFramesRead;
191 }
192
193 /**
194 * Read and convert samples in PCM24 format to float
195 */
getDataFloat_PCM24(float * buff,int numFrames)196 int WavStreamReader::getDataFloat_PCM24(float *buff, int numFrames) {
197 int numChannels = mFmtChunk->mNumChannels;
198 int numSamples = numFrames * numChannels;
199
200 static constexpr float kSampleFullScale = (float) 0x80000000;
201 static constexpr float kInverseScale = 1.0f / kSampleFullScale;
202
203 uint8_t buffer[3];
204 for(int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
205 if (mStream->read(buffer, 3) < 3) {
206 break; // no more data
207 }
208 int32_t sample = (buffer[0] << 8) | (buffer[1] << 16) | (buffer[2] << 24);
209 buff[sampleIndex] = (float)sample * kInverseScale;
210 }
211
212 return numFrames;
213 }
214
215 /**
216 * Read and convert samples in Float32 format to float
217 */
getDataFloat_Float32(float * buff,int numFrames)218 int WavStreamReader::getDataFloat_Float32(float *buff, int numFrames) {
219 // Turns out that WAV Float32 is just Android floats
220 int numChannels = mFmtChunk->mNumChannels;
221
222 return mStream->read(buff, numFrames * sizeof(float) * numChannels) /
223 (sizeof(float) * numChannels);
224 }
225
226 /**
227 * Read and convert samples in PCM32 format to float
228 */
getDataFloat_PCM32(float * buff,int numFrames)229 int WavStreamReader::getDataFloat_PCM32(float *buff, int numFrames) {
230 int numChannels = mFmtChunk->mNumChannels;
231
232 int buffOffset = 0;
233 int totalFramesRead = 0;
234
235 static constexpr int kSampleSize = sizeof(int32_t);
236 static constexpr float kSampleFullScale = (float) 0x80000000;
237 static constexpr float kInverseScale = 1.0f / kSampleFullScale;
238
239 int32_t readBuff[kConversionBufferFrames * numChannels];
240 int framesLeft = numFrames;
241 while (framesLeft > 0) {
242 int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
243 //__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
244 int numFramesRead =
245 mStream->read(readBuff, framesThisRead * kSampleSize* numChannels) /
246 (kSampleSize * numChannels);
247 totalFramesRead += numFramesRead;
248
249 // convert & Scale
250 for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
251 buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
252 }
253
254 if (numFramesRead < framesThisRead) {
255 break; // none left
256 }
257
258 framesLeft -= framesThisRead;
259 }
260
261 return totalFramesRead;
262 }
263
getDataFloat(float * buff,int numFrames)264 int WavStreamReader::getDataFloat(float *buff, int numFrames) {
265 // __android_log_print(ANDROID_LOG_INFO, TAG, "getData(%d)", numFrames);
266
267 if (mDataChunk == nullptr || mFmtChunk == nullptr) {
268 return ERR_INVALID_STATE;
269 }
270
271 int numFramesRead = 0;
272 switch (mFmtChunk->mSampleSize) {
273 case 8:
274 numFramesRead = getDataFloat_PCM8(buff, numFrames);
275 break;
276
277 case 16:
278 numFramesRead = getDataFloat_PCM16(buff, numFrames);
279 break;
280
281 case 24:
282 if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
283 numFramesRead = getDataFloat_PCM24(buff, numFrames);
284 } else {
285 __android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
286 mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
287 }
288 break;
289
290 case 32:
291 if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
292 numFramesRead = getDataFloat_PCM32(buff, numFrames);
293 } else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
294 numFramesRead = getDataFloat_Float32(buff, numFrames);
295 } else {
296 __android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
297 mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
298 }
299 break;
300
301 default:
302 __android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
303 mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
304 return ERR_INVALID_FORMAT;
305 }
306
307 // Zero out any unread frames
308 if (numFramesRead < numFrames) {
309 int numChannels = getNumChannels();
310 memset(buff + (numFramesRead * numChannels), 0,
311 (numFrames - numFramesRead) * sizeof(buff[0]) * numChannels);
312 }
313
314 return numFramesRead;
315 }
316
317 } // namespace parselib
318