Skip to content

Commit

Permalink
OboeTester: write WAV chunk sizes when recording (#2109)
Browse files Browse the repository at this point in the history
Fixes #2108
  • Loading branch information
philburk authored Oct 15, 2024
1 parent 0c815b5 commit 272cf29
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 20 deletions.
3 changes: 2 additions & 1 deletion apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,11 @@ int32_t ActivityContext::saveWaveFile(const char *filename) {
}
MyOboeOutputStream outStream;
WaveFileWriter writer(&outStream);

// You must setup the format before the first write().
writer.setFrameRate(mSampleRate);
writer.setSamplesPerFrame(mRecording->getChannelCount());
writer.setBitsPerSample(24);
writer.setFrameCount(mRecording->getSizeInFrames());
float buffer[mRecording->getChannelCount()];
// Read samples from start to finish.
mRecording->rewind();
Expand Down
28 changes: 18 additions & 10 deletions apps/OboeTester/app/src/main/cpp/util/WaveFileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
#include "WaveFileWriter.h"

void WaveFileWriter::WaveFileWriter::write(float value) {
if (!headerWritten) {
if (!mHeaderWritten) {
writeHeader();
}
if (bitsPerSample == 24) {
if (mBitsPerSample == 24) {
writePCM24(value);
} else {
writePCM16(value);
Expand All @@ -46,7 +46,7 @@ void WaveFileWriter::writeShortLittle(int16_t n) {
}

void WaveFileWriter::writeFormatChunk() {
int32_t bytesPerSample = (bitsPerSample + 7) / 8;
int32_t bytesPerSample = (mBitsPerSample + 7) / 8;

writeByte('f');
writeByte('m');
Expand All @@ -60,30 +60,34 @@ void WaveFileWriter::writeFormatChunk() {
writeIntLittle(mFrameRate * mSamplesPerFrame * bytesPerSample);
// block align
writeShortLittle((int16_t) (mSamplesPerFrame * bytesPerSample));
writeShortLittle((int16_t) bitsPerSample);
writeShortLittle((int16_t) mBitsPerSample);
}

int32_t WaveFileWriter::getDataSizeInBytes() {
if (mFrameCount <= 0) return INT32_MAX;
int64_t dataSize = ((int64_t)mFrameCount) * mSamplesPerFrame * mBitsPerSample / 8;
return (int32_t)std::min(dataSize, (int64_t)INT32_MAX);
}

void WaveFileWriter::writeDataChunkHeader() {
writeByte('d');
writeByte('a');
writeByte('t');
writeByte('a');
// Maximum size is not strictly correct but is commonly used
// when we do not know the final size.
writeIntLittle(INT32_MAX);
writeIntLittle(getDataSizeInBytes());
}

void WaveFileWriter::writeHeader() {
writeRiffHeader();
writeFormatChunk();
writeDataChunkHeader();
headerWritten = true;
mHeaderWritten = true;
}

// Write lower 8 bits. Upper bits ignored.
void WaveFileWriter::writeByte(uint8_t b) {
mOutputStream->write(b);
bytesWritten += 1;
mBytesWritten += 1;
}

void WaveFileWriter::writePCM24(float value) {
Expand Down Expand Up @@ -124,7 +128,11 @@ void WaveFileWriter::writeRiffHeader() {
writeByte('F');
// Maximum size is not strictly correct but is commonly used
// when we do not know the final size.
writeIntLittle(INT32_MAX);
const int kExtraHeaderBytes = 36;
int32_t dataSize = getDataSizeInBytes();
writeIntLittle((dataSize > (INT32_MAX - kExtraHeaderBytes))
? INT32_MAX
: dataSize + kExtraHeaderBytes);
writeByte('W');
writeByte('A');
writeByte('V');
Expand Down
46 changes: 39 additions & 7 deletions apps/OboeTester/app/src/main/cpp/util/WaveFileWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <cassert>
#include <stdio.h>
#include <algorithm>

class WaveFileOutputStream {
public:
Expand Down Expand Up @@ -57,6 +58,10 @@ class WaveFileWriter {
}

/**
* Set the number of frames per second, also known as "sample rate".
*
* If you call this then it must be called before the first write().
*
* @param frameRate default is 44100
*/
void setFrameRate(int32_t frameRate) {
Expand All @@ -68,25 +73,49 @@ class WaveFileWriter {
}

/**
* Set the size of one frame.
* For stereo, set this to 2. Default is mono = 1.
* Also known as ChannelCount
* Also known as ChannelCount.
*
* If you call this then it must be called before the first write().
*
* @param samplesPerFrame is 2 for stereo or 1 for mono
*/
void setSamplesPerFrame(int32_t samplesPerFrame) {
mSamplesPerFrame = samplesPerFrame;
}

/**
* Sets the number of frames in the file.
*
* If you do not know the final number of frames then that is OK.
* Just do not call this method and the RIFF and DATA chunk sizes
* will default to INT32_MAX. That is technically invalid WAV format
* but is common practice.
*
* If you call this then it must be called before the first write().
* @param frameCount number of frames to be written
*/
void setFrameCount(int32_t frameCount) {
mFrameCount = frameCount;
}

int32_t getSamplesPerFrame() const {
return mSamplesPerFrame;
}

/** Only 16 or 24 bit samples supported at the moment. Default is 16. */
/** Only 16 or 24 bit samples supported at the moment. Default is 16.
*
* If you call this then it must be called before the first write().
* @param bits number of bits in a PCM sample
*/
void setBitsPerSample(int32_t bits) {
assert((bits == 16) || (bits == 24));
bitsPerSample = bits;
mBitsPerSample = bits;
}

int32_t getBitsPerSample() const {
return bitsPerSample;
return mBitsPerSample;
}

void close() {
Expand Down Expand Up @@ -139,13 +168,16 @@ class WaveFileWriter {
*/
void writeRiffHeader();

int32_t getDataSizeInBytes();

static constexpr int WAVE_FORMAT_PCM = 1;
WaveFileOutputStream *mOutputStream = nullptr;
int32_t mFrameRate = 48000;
int32_t mSamplesPerFrame = 1;
int32_t bitsPerSample = 16;
int32_t bytesWritten = 0;
bool headerWritten = false;
int32_t mFrameCount = 0; // 0 for unknown
int32_t mBitsPerSample = 16;
int32_t mBytesWritten = 0;
bool mHeaderWritten = false;
static constexpr int32_t PCM24_MIN = -(1 << 23);
static constexpr int32_t PCM24_MAX = (1 << 23) - 1;

Expand Down
2 changes: 1 addition & 1 deletion src/opensles/AudioInputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Result AudioInputStreamOpenSLES::open() {
SL_DATAFORMAT_PCM, // formatType
static_cast<SLuint32>(mChannelCount), // numChannels
static_cast<SLuint32>(mSampleRate * kMillisPerSecond), // milliSamplesPerSec
bitsPerSample, // bitsPerSample
bitsPerSample, // mBitsPerSample
bitsPerSample, // containerSize;
channelCountToChannelMask(mChannelCount), // channelMask
getDefaultByteOrder(),
Expand Down
2 changes: 1 addition & 1 deletion src/opensles/AudioOutputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Result AudioOutputStreamOpenSLES::open() {
SL_DATAFORMAT_PCM, // formatType
static_cast<SLuint32>(mChannelCount), // numChannels
static_cast<SLuint32>(mSampleRate * kMillisPerSecond), // milliSamplesPerSec
bitsPerSample, // bitsPerSample
bitsPerSample, // mBitsPerSample
bitsPerSample, // containerSize;
channelCountToChannelMask(mChannelCount), // channelMask
getDefaultByteOrder(),
Expand Down

0 comments on commit 272cf29

Please sign in to comment.