Skip to content

Commit

Permalink
Add support for 24 and 32-bit sample formats (#1255)
Browse files Browse the repository at this point in the history
* OboeTester: test 24 and 32-bit PCM

* oboe: add 24 and 32-bit support

* OboeTester: add FormatConverterBox
It uses flowgraph modules to convert between the various data formats.

* Bump OboeTester version to 1.6.4
  • Loading branch information
philburk authored Apr 30, 2021
1 parent d07f827 commit 8041b38
Show file tree
Hide file tree
Showing 38 changed files with 837 additions and 89 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ set (oboe_sources
src/common/LatencyTuner.cpp
src/common/SourceFloatCaller.cpp
src/common/SourceI16Caller.cpp
src/common/SourceI24Caller.cpp
src/common/SourceI32Caller.cpp
src/common/Utilities.cpp
src/common/QuirksManager.cpp
src/fifo/FifoBuffer.cpp
Expand All @@ -37,9 +39,11 @@ set (oboe_sources
src/flowgraph/SinkFloat.cpp
src/flowgraph/SinkI16.cpp
src/flowgraph/SinkI24.cpp
src/flowgraph/SinkI32.cpp
src/flowgraph/SourceFloat.cpp
src/flowgraph/SourceI16.cpp
src/flowgraph/SourceI24.cpp
src/flowgraph/SourceI32.cpp
src/flowgraph/resampler/IntegerRatio.cpp
src/flowgraph/resampler/LinearResampler.cpp
src/flowgraph/resampler/MultiChannelResampler.cpp
Expand Down
4 changes: 2 additions & 2 deletions apps/OboeTester/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.sample.oboe.manualtest"
android:versionCode="45"
android:versionName="1.6.3">
android:versionCode="46"
android:versionName="1.6.4">
<!-- versionCode and versionName also have to be updated in build.gradle -->

<uses-feature android:name="android.hardware.microphone" android:required="true" />
Expand Down
85 changes: 85 additions & 0 deletions apps/OboeTester/app/src/main/cpp/FormatConverterBox.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2021 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 "FormatConverterBox.h"

FormatConverterBox::FormatConverterBox(int32_t numSamples,
oboe::AudioFormat inputFormat,
oboe::AudioFormat outputFormat) {
mInputFormat = inputFormat;
mOutputFormat = outputFormat;

mInputBuffer = std::make_unique<uint8_t[]>(numSamples * sizeof(int32_t));
mOutputBuffer = std::make_unique<uint8_t[]>(numSamples * sizeof(int32_t));

mSource.reset();
switch (mInputFormat) {
case oboe::AudioFormat::I16:
mSource = std::make_unique<oboe::flowgraph::SourceI16>(1);
break;
case oboe::AudioFormat::I24:
mSource = std::make_unique<oboe::flowgraph::SourceI24>(1);
break;
case oboe::AudioFormat::I32:
mSource = std::make_unique<oboe::flowgraph::SourceI32>(1);
break;
case oboe::AudioFormat::Float:
case oboe::AudioFormat::Invalid:
case oboe::AudioFormat::Unspecified:
mSource = std::make_unique<oboe::flowgraph::SourceFloat>(1);
break;
}

mSink.reset();
switch (mOutputFormat) {
case oboe::AudioFormat::I16:
mSink = std::make_unique<oboe::flowgraph::SinkI16>(1);
break;
case oboe::AudioFormat::I24:
mSink = std::make_unique<oboe::flowgraph::SinkI24>(1);
break;
case oboe::AudioFormat::I32:
mSink = std::make_unique<oboe::flowgraph::SinkI32>(1);
break;
case oboe::AudioFormat::Float:
case oboe::AudioFormat::Invalid:
case oboe::AudioFormat::Unspecified:
mSink = std::make_unique<oboe::flowgraph::SinkFloat>(1);
break;
}

if (mSource && mSink) {
mSource->output.connect(&mSink->input);
mSink->pullReset();
}
}

int32_t FormatConverterBox::convertInternalBuffers(int32_t numSamples) {
return convert(getOutputBuffer(), numSamples, getInputBuffer());
}

int32_t FormatConverterBox::convertToInternalOutput(int32_t numSamples, const void *inputBuffer) {
return convert(getOutputBuffer(), numSamples, inputBuffer);
}

int32_t FormatConverterBox::convertFromInternalInput(void *outputBuffer, int32_t numSamples) {
return convert(outputBuffer, numSamples, getInputBuffer());
}

int32_t FormatConverterBox::convert(void *outputBuffer, int32_t numSamples, const void *inputBuffer) {
mSource->setData(inputBuffer, numSamples);
return mSink->read(outputBuffer, numSamples);
}
101 changes: 101 additions & 0 deletions apps/OboeTester/app/src/main/cpp/FormatConverterBox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2021 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.
*/

#ifndef OBOETESTER_FORMAT_CONVERTER_BOX_H
#define OBOETESTER_FORMAT_CONVERTER_BOX_H

#include <unistd.h>
#include <sys/types.h>

#include "oboe/Oboe.h"
#include "flowgraph/SinkFloat.h"
#include "flowgraph/SinkI16.h"
#include "flowgraph/SinkI24.h"
#include "flowgraph/SinkI32.h"
#include "flowgraph/SourceFloat.h"
#include "flowgraph/SourceI16.h"
#include "flowgraph/SourceI24.h"
#include "flowgraph/SourceI32.h"

/**
* Use flowgraph modules to convert between the various data formats.
*
* Note that this does not do channel conversions.
*/

class FormatConverterBox {
public:
FormatConverterBox(int32_t numSamples,
oboe::AudioFormat inputFormat,
oboe::AudioFormat outputFormat);

/**
* @return internal buffer used to store input data
*/
void *getOutputBuffer() {
return (void *) mOutputBuffer.get();
};
/**
* @return internal buffer used to store output data
*/
void *getInputBuffer() {
return (void *) mInputBuffer.get();
};

/** Convert the data from inputFormat to outputFormat
* using both internal buffers.
*/
int32_t convertInternalBuffers(int32_t numSamples);

/**
* Convert data from external buffer into internal output buffer.
* @param numSamples
* @param inputBuffer
* @return
*/
int32_t convertToInternalOutput(int32_t numSamples, const void *inputBuffer);

/**
*
* Convert data from internal input buffer into external output buffer.
* @param outputBuffer
* @param numSamples
* @return
*/
int32_t convertFromInternalInput(void *outputBuffer, int32_t numSamples);

/**
* Convert data formats between the specified external buffers.
* @param outputBuffer
* @param numSamples
* @param inputBuffer
* @return
*/
int32_t convert(void *outputBuffer, int32_t numSamples, const void *inputBuffer);

private:
oboe::AudioFormat mInputFormat{oboe::AudioFormat::Invalid};
oboe::AudioFormat mOutputFormat{oboe::AudioFormat::Invalid};

std::unique_ptr<uint8_t[]> mInputBuffer;
std::unique_ptr<uint8_t[]> mOutputBuffer;

std::unique_ptr<oboe::flowgraph::FlowGraphSourceBuffered> mSource;
std::unique_ptr<oboe::flowgraph::FlowGraphSink> mSink;
};


#endif //OBOETESTER_FORMAT_CONVERTER_BOX_H
8 changes: 4 additions & 4 deletions apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ oboe::Result FullDuplexAnalyzer::start() {
}

oboe::DataCallbackResult FullDuplexAnalyzer::onBothStreamsReady(
const void *inputData,
const float *inputData,
int numInputFrames,
void *outputData,
float *outputData,
int numOutputFrames) {

int32_t inputStride = getInputStream()->getChannelCount();
int32_t outputStride = getOutputStream()->getChannelCount();
float *inputFloat = (float *) inputData;
float *outputFloat = (float *) outputData;
const float *inputFloat = inputData;
float *outputFloat = outputData;

(void) getLoopbackProcessor()->process(inputFloat, inputStride, numInputFrames,
outputFloat, outputStride, numOutputFrames);
Expand Down
4 changes: 2 additions & 2 deletions apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class FullDuplexAnalyzer : public FullDuplexStream {
* Caller should override this method.
*/
oboe::DataCallbackResult onBothStreamsReady(
const void *inputData,
const float *inputData,
int numInputFrames,
void *outputData,
float *outputData,
int numOutputFrames
) override;

Expand Down
7 changes: 2 additions & 5 deletions apps/OboeTester/app/src/main/cpp/FullDuplexEcho.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,10 @@ oboe::Result FullDuplexEcho::start() {
}

oboe::DataCallbackResult FullDuplexEcho::onBothStreamsReady(
const void *inputData,
const float *inputData,
int numInputFrames,
void *outputData,
float *outputData,
int numOutputFrames) {
// FIXME only handles matching stream formats.
// TODO Add delay node
// TODO use flowgraph to handle format conversion
int32_t framesToEcho = std::min(numInputFrames, numOutputFrames);
float *inputFloat = (float *)inputData;
float *outputFloat = (float *)outputData;
Expand Down
4 changes: 2 additions & 2 deletions apps/OboeTester/app/src/main/cpp/FullDuplexEcho.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class FullDuplexEcho : public FullDuplexStream {
* Caller should override this method.
*/
oboe::DataCallbackResult onBothStreamsReady(
const void *inputData,
const float *inputData,
int numInputFrames,
void *outputData,
float *outputData,
int numOutputFrames
) override;

Expand Down
39 changes: 27 additions & 12 deletions apps/OboeTester/app/src/main/cpp/FullDuplexStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@
#include "common/OboeDebug.h"
#include "FullDuplexStream.h"

oboe::ResultWithValue<int32_t> FullDuplexStream::readInput(int32_t numFrames) {
oboe::ResultWithValue<int32_t> result = getInputStream()->read(
mInputConverter->getInputBuffer(),
numFrames,
0 /* timeout */);
if (result == oboe::Result::OK) {
int32_t numSamples = result.value() * getInputStream()->getChannelCount();
mInputConverter->convertInternalBuffers(numSamples);
}
return result;
}

oboe::DataCallbackResult FullDuplexStream::onAudioReady(
oboe::AudioStream *outputStream,
void *audioData,
Expand All @@ -32,9 +44,7 @@ oboe::DataCallbackResult FullDuplexStream::onAudioReady(
// Drain the input.
int32_t totalFramesRead = 0;
do {
oboe::ResultWithValue<int32_t> result = getInputStream()->read(mInputBuffer.get(),
numFrames,
0 /* timeout */);
oboe::ResultWithValue<int32_t> result = readInput(numFrames);
if (!result) {
// Ignore errors because input stream may not be started yet.
break;
Expand Down Expand Up @@ -62,7 +72,7 @@ oboe::DataCallbackResult FullDuplexStream::onAudioReady(
} else {
int32_t framesAvailable = resultAvailable.value();
if (framesAvailable >= mMinimumFramesBeforeRead) {
oboe::ResultWithValue<int32_t> resultRead = getInputStream()->read(mInputBuffer.get(), numFrames, 0 /* timeout */);
oboe::ResultWithValue<int32_t> resultRead = readInput(numFrames);
if (!resultRead) {
LOGE("%s() read() returned %s\n", __func__, convertToText(resultRead.error()));
callbackResult = oboe::DataCallbackResult::Stop;
Expand All @@ -79,7 +89,7 @@ oboe::DataCallbackResult FullDuplexStream::onAudioReady(
int32_t framesAvailable = resultAvailable.value();
if (framesAvailable >= mMinimumFramesBeforeRead) {
// Read data into input buffer.
oboe::ResultWithValue<int32_t> resultRead = getInputStream()->read(mInputBuffer.get(), numFrames, 0 /* timeout */);
oboe::ResultWithValue<int32_t> resultRead = readInput(numFrames);
if (!resultRead) {
LOGE("%s() read() returned %s\n", __func__, convertToText(resultRead.error()));
callbackResult = oboe::DataCallbackResult::Stop;
Expand All @@ -91,9 +101,11 @@ oboe::DataCallbackResult FullDuplexStream::onAudioReady(

if (callbackResult == oboe::DataCallbackResult::Continue) {
callbackResult = onBothStreamsReady(
mInputBuffer.get(), framesRead,
audioData, numFrames);

(const float *) mInputConverter->getOutputBuffer(),
framesRead,
(float *) mOutputConverter->getInputBuffer(), numFrames);
mOutputConverter->convertFromInternalInput( audioData,
numFrames * getOutputStream()->getChannelCount());
}
}

Expand All @@ -112,10 +124,13 @@ oboe::Result FullDuplexStream::start() {
// Determine maximum size that could possibly be called.
int32_t bufferSize = getOutputStream()->getBufferCapacityInFrames()
* getOutputStream()->getChannelCount();
if (bufferSize > mBufferSize) {
mInputBuffer = std::make_unique<float[]>(bufferSize);
mBufferSize = bufferSize;
}
mInputConverter = std::make_unique<FormatConverterBox>(bufferSize,
getInputStream()->getFormat(),
oboe::AudioFormat::Float);
mOutputConverter = std::make_unique<FormatConverterBox>(bufferSize,
oboe::AudioFormat::Float,
getOutputStream()->getFormat());

oboe::Result result = getInputStream()->requestStart();
if (result != oboe::Result::OK) {
return result;
Expand Down
12 changes: 8 additions & 4 deletions apps/OboeTester/app/src/main/cpp/FullDuplexStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include "oboe/Oboe.h"

#include "FormatConverterBox.h"

class FullDuplexStream : public oboe::AudioStreamCallback {
public:
FullDuplexStream() {}
Expand All @@ -46,14 +48,16 @@ class FullDuplexStream : public oboe::AudioStreamCallback {

virtual oboe::Result stop();

oboe::ResultWithValue<int32_t> readInput(int32_t numFrames);

/**
* Called when data is available on both streams.
* Caller should override this method.
*/
virtual oboe::DataCallbackResult onBothStreamsReady(
const void *inputData,
const float *inputData,
int numInputFrames,
void *outputData,
float *outputData,
int numOutputFrames
) = 0;

Expand Down Expand Up @@ -107,8 +111,8 @@ class FullDuplexStream : public oboe::AudioStreamCallback {
oboe::AudioStream *mInputStream = nullptr;
oboe::AudioStream *mOutputStream = nullptr;

int32_t mBufferSize = 0;
std::unique_ptr<float[]> mInputBuffer;
std::unique_ptr<FormatConverterBox> mInputConverter;
std::unique_ptr<FormatConverterBox> mOutputConverter;
};


Expand Down
Loading

0 comments on commit 8041b38

Please sign in to comment.