• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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