blob: 98927b53230cbf37f6cff9ef25955402e0d15d4f [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <string.h>
#include <android/log.h>
#include "stream/InputStream.h"
#include "AudioEncoding.h"
#include "WavRIFFChunkHeader.h"
#include "WavFmtChunkHeader.h"
#include "WavChunkHeader.h"
#include "WavStreamReader.h"
static const char *TAG = "WavStreamReader";
namespace parselib {
WavStreamReader::WavStreamReader(InputStream *stream) {
mStream = stream;
mWavChunk = 0;
mFmtChunk = 0;
mDataChunk = 0;
mAudioDataStartPos = -1;
mChunkMap = new std::map<RiffID, WavChunkHeader *>();
}
int WavStreamReader::getSampleEncoding() {
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
switch (mFmtChunk->mSampleSize) {
case 8:
return AudioEncoding::PCM_8;
case 16:
return AudioEncoding::PCM_16;
case 24:
// TODO - Support 24-bit WAV data
return AudioEncoding::INVALID; // for now
default:
return AudioEncoding::INVALID;
}
} else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
return AudioEncoding::PCM_IEEEFLOAT;
}
return AudioEncoding::INVALID;
}
void WavStreamReader::parse() {
RiffID tag;
while (true) {
int numRead = mStream->peek(&tag, sizeof(tag));
if (numRead <= 0) {
break; // done
}
// char *tagStr = (char *) &tag;
// __android_log_print(ANDROID_LOG_INFO, TAG, "[%c%c%c%c]",
// tagStr[0], tagStr[1], tagStr[2], tagStr[3]);
WavChunkHeader *chunk = 0;
if (tag == WavRIFFChunkHeader::RIFFID_RIFF) {
chunk = mWavChunk = new WavRIFFChunkHeader(tag);
mWavChunk->read(mStream);
} else if (tag == WavFmtChunkHeader::RIFFID_FMT) {
chunk = mFmtChunk = new WavFmtChunkHeader(tag);
mFmtChunk->read(mStream);
} else if (tag == WavChunkHeader::RIFFID_DATA) {
chunk = mDataChunk = new WavChunkHeader(tag);
mDataChunk->read(mStream);
// We are now positioned at the start of the audio data.
mAudioDataStartPos = mStream->getPos();
mStream->advance(mDataChunk->mChunkSize);
} else {
chunk = new WavChunkHeader(tag);
chunk->read(mStream);
mStream->advance(chunk->mChunkSize); // skip the body
}
(*mChunkMap)[tag] = chunk;
}
if (mDataChunk != 0) {
mStream->setPos(mAudioDataStartPos);
}
}
// Data access
void WavStreamReader::positionToAudio() {
if (mDataChunk != 0) {
mStream->setPos(mAudioDataStartPos);
}
}
int WavStreamReader::getDataFloat(float *buff, int numFrames) {
// __android_log_print(ANDROID_LOG_INFO, TAG, "getData(%d)", numFrames);
if (mDataChunk == 0 || mFmtChunk == 0) {
return 0;
}
int totalFramesRead = 0;
int numChans = mFmtChunk->mNumChannels;
int buffOffset = 0;
// TODO - Manage other input formats
if (mFmtChunk->mSampleSize == 16) {
short *readBuff = new short[128 * numChans];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, 128);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * sizeof(short) * numChans) /
(sizeof(short) * numChans);
totalFramesRead += numFramesRead;
// convert
for (int offset = 0; offset < numFramesRead * numChans; offset++) {
buff[buffOffset++] = (float) readBuff[offset] / (float) 0x7FFF;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
delete[] readBuff;
// __android_log_print(ANDROID_LOG_INFO, TAG, " returns:%d", totalFramesRead);
return totalFramesRead;
}
return 0;
}
} // namespace parselib