From cb01be56e08c1e6ac24c3bf202dc5add75895051 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 16 Jun 2020 16:37:37 -0700 Subject: [PATCH] oboe: add ChannelCountConverter --- CMakeLists.txt | 3 +- src/common/DataConversionFlowGraph.cpp | 41 +++++++++++-------- src/common/DataConversionFlowGraph.h | 4 +- src/common/QuirksManager.cpp | 10 ++--- src/common/QuirksManager.h | 3 ++ src/flowgraph/ChannelCountConverter.cpp | 52 +++++++++++++++++++++++++ src/flowgraph/ChannelCountConverter.h | 52 +++++++++++++++++++++++++ 7 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 src/flowgraph/ChannelCountConverter.cpp create mode 100644 src/flowgraph/ChannelCountConverter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ba5d1f9c..51a45b2c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,8 @@ set (oboe_sources src/fifo/FifoController.cpp src/fifo/FifoControllerBase.cpp src/fifo/FifoControllerIndirect.cpp - src/flowgraph/FlowGraphNode.cpp + src/flowgraph/FlowGraphNode.cpp + src/flowgraph/ChannelCountConverter.cpp src/flowgraph/ClipToRange.cpp src/flowgraph/ManyToMultiConverter.cpp src/flowgraph/MonoToMultiConverter.cpp diff --git a/src/common/DataConversionFlowGraph.cpp b/src/common/DataConversionFlowGraph.cpp index e40a58aa4..fd6695b32 100644 --- a/src/common/DataConversionFlowGraph.cpp +++ b/src/common/DataConversionFlowGraph.cpp @@ -140,21 +140,20 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream lastOutput = &mSource->output; } - if (sourceChannelCount != sinkChannelCount - && sourceChannelCount != 1 - && sinkChannelCount != 1 - ) { - LOGW("%s() Channel conversion %d to %d not supported.", __func__, - sourceChannelCount, sinkChannelCount); - return Result::ErrorUnimplemented; - } - // If we are going to reduce the number of channels then do it before the // sample rate converter. - if (sourceChannelCount > 1 && sinkChannelCount == 1) { - mMultiToMonoConverter = std::make_unique(sourceChannelCount); - lastOutput->connect(&mMultiToMonoConverter->input); - lastOutput = &mMultiToMonoConverter->output; + if (sourceChannelCount > sinkChannelCount) { + if (sinkChannelCount == 1) { + mMultiToMonoConverter = std::make_unique(sourceChannelCount); + lastOutput->connect(&mMultiToMonoConverter->input); + lastOutput = &mMultiToMonoConverter->output; + } else { + mChannelCountConverter = std::make_unique( + sourceChannelCount, + sinkChannelCount); + lastOutput->connect(&mChannelCountConverter->input); + lastOutput = &mChannelCountConverter->output; + } } // Sample Rate conversion @@ -173,10 +172,18 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream } // Expand the number of channels if required. - if (sourceChannelCount == 1 && sinkChannelCount > 1) { - mMonoToMultiConverter = std::make_unique(sinkChannelCount); - lastOutput->connect(&mMonoToMultiConverter->input); - lastOutput = &mMonoToMultiConverter->output; + if (sourceChannelCount < sinkChannelCount) { + if (sourceChannelCount == 1) { + mMonoToMultiConverter = std::make_unique(sinkChannelCount); + lastOutput->connect(&mMonoToMultiConverter->input); + lastOutput = &mMonoToMultiConverter->output; + } else { + mChannelCountConverter = std::make_unique( + sourceChannelCount, + sinkChannelCount); + lastOutput->connect(&mChannelCountConverter->input); + lastOutput = &mChannelCountConverter->output; + } } // Sink diff --git a/src/common/DataConversionFlowGraph.h b/src/common/DataConversionFlowGraph.h index 3bc3801ec..5b8d3f664 100644 --- a/src/common/DataConversionFlowGraph.h +++ b/src/common/DataConversionFlowGraph.h @@ -21,10 +21,11 @@ #include #include +#include #include +#include #include #include -#include #include "AudioSourceCaller.h" #include "FixedBlockWriter.h" @@ -70,6 +71,7 @@ class DataConversionFlowGraph : public FixedBlockProcessor { std::unique_ptr mSourceCaller; std::unique_ptr mMonoToMultiConverter; std::unique_ptr mMultiToMonoConverter; + std::unique_ptr mChannelCountConverter; std::unique_ptr mResampler; std::unique_ptr mRateConverter; std::unique_ptr mSink; diff --git a/src/common/QuirksManager.cpp b/src/common/QuirksManager.cpp index 9c874da8d..505cddb1e 100644 --- a/src/common/QuirksManager.cpp +++ b/src/common/QuirksManager.cpp @@ -59,7 +59,7 @@ bool QuirksManager::DeviceQuirks::isAAudioMMapPossible(const AudioStreamBuilder || builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None; return builder.getPerformanceMode() == PerformanceMode::LowLatency && isSampleRateCompatible - && builder.getChannelCount() <= 2; + && builder.getChannelCount() <= kChannelCountStereo; } class SamsungDeviceQuirks : public QuirksManager::DeviceQuirks { @@ -147,18 +147,18 @@ bool QuirksManager::isConversionNeeded( // Channel Count conversions if (OboeGlobals::areWorkaroundsEnabled() && builder.isChannelConversionAllowed() - && builder.getChannelCount() == 2 // stereo? + && builder.getChannelCount() == kChannelCountStereo && isInput && isLowLatency && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__)) ) { // Workaround for heap size regression in O. // b/66967812 AudioRecord does not allow FAST track for stereo capture in O - childBuilder.setChannelCount(1); + childBuilder.setChannelCount(kChannelCountMono); conversionNeeded = true; LOGI("QuirksManager::%s() using mono internally for low latency on O", __func__); } else if (OboeGlobals::areWorkaroundsEnabled() - && builder.getChannelCount() == 1 // mono? + && builder.getChannelCount() == kChannelCountMono && isInput && mDeviceQuirks->isMonoMMapActuallyStereo() && builder.willUseAAudio() @@ -168,7 +168,7 @@ bool QuirksManager::isConversionNeeded( && mDeviceQuirks->isAAudioMMapPossible(builder) ) { // Workaround for mono actually running in stereo mode. - childBuilder.setChannelCount(2); // Use stereo and extract first channel. + childBuilder.setChannelCount(kChannelCountStereo); // Use stereo and extract first channel. conversionNeeded = true; LOGI("QuirksManager::%s() using stereo internally to avoid broken mono", __func__); } diff --git a/src/common/QuirksManager.h b/src/common/QuirksManager.h index fb9973558..b4e38ded7 100644 --- a/src/common/QuirksManager.h +++ b/src/common/QuirksManager.h @@ -110,6 +110,9 @@ class QuirksManager { private: + static constexpr int32_t kChannelCountMono = 1; + static constexpr int32_t kChannelCountStereo = 2; + std::unique_ptr mDeviceQuirks{}; }; diff --git a/src/flowgraph/ChannelCountConverter.cpp b/src/flowgraph/ChannelCountConverter.cpp new file mode 100644 index 000000000..03de262ca --- /dev/null +++ b/src/flowgraph/ChannelCountConverter.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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 +#include "FlowGraphNode.h" +#include "ChannelCountConverter.h" + +using namespace flowgraph; + +ChannelCountConverter::ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount) + : input(*this, inputChannelCount) + , output(*this, outputChannelCount) { +} + +ChannelCountConverter::~ChannelCountConverter() { } + +int32_t ChannelCountConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t inputChannelCount = input.getSamplesPerFrame(); + int32_t outputChannelCount = output.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + int inputChannel = 0; + for (int outputChannel = 0; outputChannel < outputChannelCount; outputChannel++) { + // Copy input channels to output channels. + // Wrap if we run out of inputs. + // Discard if we run out of outputs. + outputBuffer[outputChannel] = inputBuffer[inputChannel]; + inputChannel = (inputChannel == inputChannelCount) + ? 0 : inputChannel + 1; + } + inputBuffer += inputChannelCount; + outputBuffer += outputChannelCount; + } + return numFrames; +} + diff --git a/src/flowgraph/ChannelCountConverter.h b/src/flowgraph/ChannelCountConverter.h new file mode 100644 index 000000000..e4b6f4e66 --- /dev/null +++ b/src/flowgraph/ChannelCountConverter.h @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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 FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H +#define FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace flowgraph { + +/** + * Change the number of number of channels without mixing. + * When increasing the channel count, duplicate input channels. + * When decreasing the channel count, drop input channels. + */ + class ChannelCountConverter : public FlowGraphNode { + public: + explicit ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount); + + virtual ~ChannelCountConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "ChannelCountConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; + }; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H