Blend MMAP streams for accessibility
There is an accessibility feature called Mono audio. This feature
allows stereo audio to be blended into mono audio.
Currently, this feature works perfectly in most scenarios. However,
this feature is not used when MMAP is enabled. This cl fixes MMAP
scenarios.
Bug: 69635707
Test: Tested on Bramble and MMAP through the following six scenarios:
1. MMAP Exclusive Mono Audio Enabled
2. MMAP Shared Mono Audio Enabled
3. No MMAP Mono Audio Enabled
4. MMAP Exclusive Mono Audio Disabled
5. MMAP Shared Mono Audio Disabled
6. No MMAP Mono Audio Disabled
You can enable Mono Audio from Settings -> Accessibility -> Audio
adjustment -> Mono audio
With OboeTester playing a different frequency on each ear, this seems
to work as expected.
atest test_flowgraph
Change-Id: I2688f06cc8c6ef0f2965845e65e2582f3cc9788e
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index ddd3f97..38bcb7c 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -211,6 +211,7 @@
"flowgraph/ClipToRange.cpp",
"flowgraph/FlowGraphNode.cpp",
"flowgraph/ManyToMultiConverter.cpp",
+ "flowgraph/MonoBlend.cpp",
"flowgraph/MonoToMultiConverter.cpp",
"flowgraph/MultiToMonoConverter.cpp",
"flowgraph/RampLinear.cpp",
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 68b84c8..d3e2912 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -21,6 +21,7 @@
#include "AAudioFlowGraph.h"
#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
#include <flowgraph/RampLinear.h>
#include <flowgraph/SinkFloat.h>
@@ -37,7 +38,8 @@
aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
int32_t sourceChannelCount,
audio_format_t sinkFormat,
- int32_t sinkChannelCount) {
+ int32_t sinkChannelCount,
+ bool useMonoBlend) {
FlowGraphPortFloatOutput *lastOutput = nullptr;
// TODO change back to ALOGD
@@ -76,6 +78,12 @@
lastOutput = &mClipper->output;
}
+ if (useMonoBlend) {
+ mMonoBlend = std::make_unique<MonoBlend>(sourceChannelCount);
+ lastOutput->connect(&mMonoBlend->input);
+ lastOutput = &mMonoBlend->output;
+ }
+
// Expand the number of channels if required.
if (sourceChannelCount == 1 && sinkChannelCount > 1) {
mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index 079328a..e719d91 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -24,6 +24,7 @@
#include <aaudio/AAudio.h>
#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
#include <flowgraph/RampLinear.h>
@@ -41,7 +42,8 @@
aaudio_result_t configure(audio_format_t sourceFormat,
int32_t sourceChannelCount,
audio_format_t sinkFormat,
- int32_t sinkChannelCount);
+ int32_t sinkChannelCount,
+ bool useMonoBlend);
void process(const void *source, void *destination, int32_t numFrames);
@@ -54,6 +56,7 @@
private:
std::unique_ptr<flowgraph::FlowGraphSourceBuffered> mSource;
+ std::unique_ptr<flowgraph::MonoBlend> mMonoBlend;
std::unique_ptr<flowgraph::RampLinear> mVolumeRamp;
std::unique_ptr<flowgraph::ClipToRange> mClipper;
std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 89d42bf..1b8e224 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -27,6 +27,7 @@
#include <aaudio/AAudio.h>
#include <cutils/properties.h>
+#include <media/AudioParameter.h>
#include <media/AudioSystem.h>
#include <media/MediaMetricsItem.h>
#include <utils/Trace.h>
@@ -270,6 +271,15 @@
mCallbackBuffer = std::make_unique<uint8_t[]>(callbackBufferSize);
}
+ // Exclusive output streams should combine channels when mono audio adjustment
+ // is enabled.
+ if ((getDirection() == AAUDIO_DIRECTION_OUTPUT) &&
+ (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE)) {
+ bool isMasterMono = false;
+ android::AudioSystem::getMasterMono(&isMasterMono);
+ setRequireMonoBlend(isMasterMono);
+ }
+
// For debugging and analyzing the distribution of MMAP timestamps.
// For OUTPUT, use a NEGATIVE offset to move the CPU writes further BEFORE the HW reads.
// For INPUT, use a POSITIVE offset to move the CPU reads further AFTER the HW writes.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 5921799..c17c7a0 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -52,7 +52,8 @@
result = mFlowGraph.configure(getFormat(),
getSamplesPerFrame(),
getDeviceFormat(),
- getDeviceChannelCount());
+ getDeviceChannelCount(),
+ getRequireMonoBlend());
if (result != AAUDIO_OK) {
safeReleaseClose();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index afb8551..a3af753 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -277,6 +277,10 @@
return mIsPrivacySensitive;
}
+ bool getRequireMonoBlend() const {
+ return mRequireMonoBlend;
+ }
+
/**
* This is only valid after setChannelMask() and setFormat()
* have been called.
@@ -631,6 +635,13 @@
mIsPrivacySensitive = privacySensitive;
}
+ /**
+ * This should not be called after the open() call.
+ */
+ void setRequireMonoBlend(bool requireMonoBlend) {
+ mRequireMonoBlend = requireMonoBlend;
+ }
+
std::string mMetricsId; // set once during open()
std::mutex mStreamLock;
@@ -672,6 +683,7 @@
aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED;
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
bool mIsPrivacySensitive = false;
+ bool mRequireMonoBlend = false;
int32_t mSessionId = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/flowgraph/MonoBlend.cpp b/media/libaaudio/src/flowgraph/MonoBlend.cpp
new file mode 100644
index 0000000..62e2809
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoBlend.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <unistd.h>
+
+#include "MonoBlend.h"
+
+using namespace flowgraph;
+
+MonoBlend::MonoBlend(int32_t channelCount)
+ : FlowGraphFilter(channelCount)
+ , mInvChannelCount(1. / channelCount)
+{
+}
+
+int32_t MonoBlend::onProcess(int32_t numFrames) {
+ int32_t channelCount = output.getSamplesPerFrame();
+ const float *inputBuffer = input.getBuffer();
+ float *outputBuffer = output.getBuffer();
+
+ for (size_t i = 0; i < numFrames; ++i) {
+ float accum = 0;
+ for (size_t j = 0; j < channelCount; ++j) {
+ accum += *inputBuffer++;
+ }
+ accum *= mInvChannelCount;
+ for (size_t j = 0; j < channelCount; ++j) {
+ *outputBuffer++ = accum;
+ }
+ }
+
+ return numFrames;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/MonoBlend.h b/media/libaaudio/src/flowgraph/MonoBlend.h
new file mode 100644
index 0000000..7e3c35b
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoBlend.h
@@ -0,0 +1,48 @@
+/*
+ * 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 FLOWGRAPH_MONO_BLEND_H
+#define FLOWGRAPH_MONO_BLEND_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "FlowGraphNode.h"
+
+namespace flowgraph {
+
+/**
+ * Combine data between multiple channels so each channel is an average
+ * of all channels.
+ */
+class MonoBlend : public FlowGraphFilter {
+public:
+ explicit MonoBlend(int32_t channelCount);
+
+ virtual ~MonoBlend() = default;
+
+ int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "MonoBlend";
+ }
+private:
+ const float mInvChannelCount;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MONO_BLEND
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 611cbf7..0792fc5 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include "flowgraph/ClipToRange.h"
+#include "flowgraph/MonoBlend.h"
#include "flowgraph/MonoToMultiConverter.h"
#include "flowgraph/SourceFloat.h"
#include "flowgraph/RampLinear.h"
@@ -164,3 +165,29 @@
EXPECT_NEAR(expected[i], output[i], tolerance);
}
}
+
+TEST(test_flowgraph, module_mono_blend) {
+ // Two channel to two channel with 3 inputs and outputs.
+ constexpr int numChannels = 2;
+ constexpr int numFrames = 3;
+
+ static const float input[] = {-0.7, 0.5, -0.25, 1.25, 1000, 2000};
+ static const float expected[] = {-0.1, -0.1, 0.5, 0.5, 1500, 1500};
+ float output[100];
+ SourceFloat sourceFloat{numChannels};
+ MonoBlend monoBlend{numChannels};
+ SinkFloat sinkFloat{numChannels};
+
+ sourceFloat.setData(input, numFrames);
+
+ sourceFloat.output.connect(&monoBlend.input);
+ monoBlend.output.connect(&sinkFloat.input);
+
+ int32_t numRead = sinkFloat.read(output, numFrames);
+ ASSERT_EQ(numRead, numFrames);
+ constexpr float tolerance = 0.000001f; // arbitrary
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_NEAR(expected[i], output[i], tolerance);
+ }
+}
+