eden/externals/oboe/samples/parselib/src/main/cpp/wav/WavStreamReader.cpp
swurl 6c655321e6
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
2025-05-31 02:33:02 -04:00

319 lines
10 KiB
C++

/*
* 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";
static constexpr int kConversionBufferFrames = 16;
namespace parselib {
WavStreamReader::WavStreamReader(InputStream *stream) {
mStream = stream;
mWavChunk = nullptr;
mFmtChunk = nullptr;
mDataChunk = nullptr;
mAudioDataStartPos = -1;
}
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:
return AudioEncoding::PCM_24;
case 32:
return AudioEncoding::PCM_32;
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]);
std::shared_ptr<WavChunkHeader> chunk = nullptr;
if (tag == WavRIFFChunkHeader::RIFFID_RIFF) {
chunk = mWavChunk = std::make_shared<WavRIFFChunkHeader>(WavRIFFChunkHeader(tag));
mWavChunk->read(mStream);
} else if (tag == WavFmtChunkHeader::RIFFID_FMT) {
chunk = mFmtChunk = std::make_shared<WavFmtChunkHeader>(WavFmtChunkHeader(tag));
mFmtChunk->read(mStream);
} else if (tag == WavChunkHeader::RIFFID_DATA) {
chunk = mDataChunk = std::make_shared<WavChunkHeader>(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 = std::make_shared<WavChunkHeader>(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);
}
}
/**
* Read and convert samples in PCM8 format to float
*/
int WavStreamReader::getDataFloat_PCM8(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(u_int8_t);
static constexpr float kSampleFullScale = (float)0x80;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
u_int8_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// Convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
// PCM8 is unsigned, so we need to make it signed before scaling/converting
buff[buffOffset++] = ((float) readBuff[offset] - kSampleFullScale)
* kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
/**
* Read and convert samples in PCM16 format to float
*/
int WavStreamReader::getDataFloat_PCM16(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(int16_t);
static constexpr float kSampleFullScale = (float) 0x8000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
int16_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// Convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
/**
* Read and convert samples in PCM24 format to float
*/
int WavStreamReader::getDataFloat_PCM24(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int numSamples = numFrames * numChannels;
static constexpr float kSampleFullScale = (float) 0x80000000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
uint8_t buffer[3];
for(int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
if (mStream->read(buffer, 3) < 3) {
break; // no more data
}
int32_t sample = (buffer[0] << 8) | (buffer[1] << 16) | (buffer[2] << 24);
buff[sampleIndex] = (float)sample * kInverseScale;
}
return numFrames;
}
/**
* Read and convert samples in Float32 format to float
*/
int WavStreamReader::getDataFloat_Float32(float *buff, int numFrames) {
// Turns out that WAV Float32 is just Android floats
int numChannels = mFmtChunk->mNumChannels;
return mStream->read(buff, numFrames * sizeof(float) * numChannels) /
(sizeof(float) * numChannels);
}
/**
* Read and convert samples in PCM32 format to float
*/
int WavStreamReader::getDataFloat_PCM32(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(int32_t);
static constexpr float kSampleFullScale = (float) 0x80000000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
int32_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize* numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
int WavStreamReader::getDataFloat(float *buff, int numFrames) {
// __android_log_print(ANDROID_LOG_INFO, TAG, "getData(%d)", numFrames);
if (mDataChunk == nullptr || mFmtChunk == nullptr) {
return ERR_INVALID_STATE;
}
int numFramesRead = 0;
switch (mFmtChunk->mSampleSize) {
case 8:
numFramesRead = getDataFloat_PCM8(buff, numFrames);
break;
case 16:
numFramesRead = getDataFloat_PCM16(buff, numFrames);
break;
case 24:
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
numFramesRead = getDataFloat_PCM24(buff, numFrames);
} else {
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
}
break;
case 32:
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
numFramesRead = getDataFloat_PCM32(buff, numFrames);
} else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
numFramesRead = getDataFloat_Float32(buff, numFrames);
} else {
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
}
break;
default:
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
return ERR_INVALID_FORMAT;
}
// Zero out any unread frames
if (numFramesRead < numFrames) {
int numChannels = getNumChannels();
memset(buff + (numFramesRead * numChannels), 0,
(numFrames - numFramesRead) * sizeof(buff[0]) * numChannels);
}
return numFramesRead;
}
} // namespace parselib