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 namespace parselib {
32
WavStreamReader(InputStream * stream)33 WavStreamReader::WavStreamReader(InputStream *stream) {
34 mStream = stream;
35
36 mWavChunk = 0;
37 mFmtChunk = 0;
38 mDataChunk = 0;
39
40 mAudioDataStartPos = -1;
41
42 mChunkMap = new std::map<RiffID, WavChunkHeader *>();
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 WavChunkHeader *chunk = 0;
82 if (tag == WavRIFFChunkHeader::RIFFID_RIFF) {
83 chunk = mWavChunk = new WavRIFFChunkHeader(tag);
84 mWavChunk->read(mStream);
85 } else if (tag == WavFmtChunkHeader::RIFFID_FMT) {
86 chunk = mFmtChunk = new WavFmtChunkHeader(tag);
87 mFmtChunk->read(mStream);
88 } else if (tag == WavChunkHeader::RIFFID_DATA) {
89 chunk = mDataChunk = new 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 = new 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
getDataFloat(float * buff,int numFrames)115 int WavStreamReader::getDataFloat(float *buff, int numFrames) {
116 // __android_log_print(ANDROID_LOG_INFO, TAG, "getData(%d)", numFrames);
117
118 if (mDataChunk == 0 || mFmtChunk == 0) {
119 return 0;
120 }
121
122 int totalFramesRead = 0;
123
124 int numChans = mFmtChunk->mNumChannels;
125 int buffOffset = 0;
126
127 // TODO - Manage other input formats
128 if (mFmtChunk->mSampleSize == 16) {
129 short *readBuff = new short[128 * numChans];
130 int framesLeft = numFrames;
131 while (framesLeft > 0) {
132 int framesThisRead = std::min(framesLeft, 128);
133 //__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
134 int numFramesRead =
135 mStream->read(readBuff, framesThisRead * sizeof(short) * numChans) /
136 (sizeof(short) * numChans);
137 totalFramesRead += numFramesRead;
138
139 // convert
140 for (int offset = 0; offset < numFramesRead * numChans; offset++) {
141 buff[buffOffset++] = (float) readBuff[offset] / (float) 0x7FFF;
142 }
143
144 if (numFramesRead < framesThisRead) {
145 break; // none left
146 }
147
148 framesLeft -= framesThisRead;
149 }
150 delete[] readBuff;
151
152 // __android_log_print(ANDROID_LOG_INFO, TAG, " returns:%d", totalFramesRead);
153 return totalFramesRead;
154 }
155
156 return 0;
157 }
158
159 } // namespace parselib
160