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