summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio_utils/Balance.cpp19
-rw-r--r--audio_utils/include/audio_utils/Balance.h14
-rw-r--r--audio_utils/tests/Android.bp19
-rw-r--r--audio_utils/tests/balance_tests.cpp124
4 files changed, 176 insertions, 0 deletions
diff --git a/audio_utils/Balance.cpp b/audio_utils/Balance.cpp
index dca9e58..df0e76b 100644
--- a/audio_utils/Balance.cpp
+++ b/audio_utils/Balance.cpp
@@ -77,18 +77,37 @@ void Balance::setChannelMask(audio_channel_mask_t channelMask)
1, // AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000u,
0, // AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT = 0x40000u,
1, // AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT = 0x80000u,
+ 0, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_LEFT = 0x100000u,
+ 2, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_CENTER = 0x200000u,
+ 1, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 0x400000u,
+ 2, // AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2 = 0x800000u,
};
mSides.resize(mChannelCount);
+ // If LFE and LFE2 both exist, it should be L and R in 22.2
+ int lfe = -1;
+ int lfe2 = -1;
+ constexpr unsigned LFE_CHANNEL_INDEX = 3;
+ constexpr unsigned LFE2_CHANNEL_INDEX = 23;
for (unsigned i = 0, channel = channelMask; channel != 0; ++i) {
const int index = __builtin_ctz(channel);
if (static_cast<std::size_t>(index) < std::size(sideFromChannel)) {
mSides[i] = sideFromChannel[index];
+ // Keep track of LFE indices
+ if (index == LFE_CHANNEL_INDEX) {
+ lfe = i;
+ } else if (index == LFE2_CHANNEL_INDEX) {
+ lfe2 = i;
+ }
} else {
mSides[i] = 2; // consider center
}
channel &= ~(1 << index);
}
+ if (lfe >= 0 && lfe2 >= 0) { // if both LFEs exist assign to L and R.
+ mSides[lfe] = 0;
+ mSides[lfe2] = 1;
+ }
setBalance(balance); // recompute balance
}
diff --git a/audio_utils/include/audio_utils/Balance.h b/audio_utils/include/audio_utils/Balance.h
index da9b836..4a07c09 100644
--- a/audio_utils/include/audio_utils/Balance.h
+++ b/audio_utils/include/audio_utils/Balance.h
@@ -71,6 +71,13 @@ public:
}
/**
+ * \brief Returns whether volume change is ramped.
+ */
+ bool getRamp() const {
+ return mRamp;
+ }
+
+ /**
* \brief Sets the channel mask for data passed in.
*
* setChannelMask() must called before process() to set
@@ -95,6 +102,13 @@ public:
void setBalance(float balance);
/**
+ * \brief Returns the current balance.
+ */
+ float getBalance() const {
+ return mBalance;
+ }
+
+ /**
* \brief Processes balance for audio data.
*
* setChannelMask() should be called at least once before calling process()
diff --git a/audio_utils/tests/Android.bp b/audio_utils/tests/Android.bp
index 9326cb5..fb0dd49 100644
--- a/audio_utils/tests/Android.bp
+++ b/audio_utils/tests/Android.bp
@@ -8,6 +8,25 @@ package {
default_applicable_licenses: ["system_media_license"],
}
+cc_test {
+ name: "balance_tests",
+ host_supported: true,
+
+ srcs: [
+ "balance_tests.cpp",
+ ],
+
+ static_libs: [
+ "libaudioutils",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
cc_binary {
name: "biquad_filter",
host_supported: true,
diff --git a/audio_utils/tests/balance_tests.cpp b/audio_utils/tests/balance_tests.cpp
new file mode 100644
index 0000000..aa115d7
--- /dev/null
+++ b/audio_utils/tests/balance_tests.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 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 <audio_utils/Balance.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+TEST(audio_utils_balance, stereo) {
+ // disable ramping so we can check single frame processing.
+ android::audio_utils::Balance balance(false /* ramp */);
+ ASSERT_EQ(false, balance.getRamp());
+
+ balance.setChannelMask(AUDIO_CHANNEL_OUT_STEREO);
+ std::vector<float> buffer = {1.f, -1.f};
+
+ // balance of 0 is no change.
+ ASSERT_EQ(0.f, balance.getBalance());
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f}), buffer);
+
+ // balance of 1.f is right.
+ balance.setBalance(1.f);
+ ASSERT_EQ(1.f, balance.getBalance());
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{0.f, -1.f}), buffer);
+
+ // balance of -1.f is left.
+ buffer = {1.f, -1.f};
+ balance.setBalance(-1.f); // to left
+ ASSERT_EQ(-1.f, balance.getBalance());
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, 0.f}), buffer);
+}
+
+TEST(audio_utils_balance, 7point1) {
+ // disable ramping so we can check single frame processing.
+ android::audio_utils::Balance balance(false /* ramp */);
+ ASSERT_EQ(false, balance.getRamp());
+
+ balance.setChannelMask(AUDIO_CHANNEL_OUT_7POINT1);
+ // FL, FR, FC, LFE, BL, BR, SL, SR
+ std::vector<float> buffer = {1.f, -1.f, 0.5f, 0.25f, 0.75f, 0.75f, 0.125f, 0.125f};
+
+ // balance of 0 is no change.
+ ASSERT_EQ(0.f, balance.getBalance());
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f, 0.5f, 0.25f, 0.75f, 0.75f, 0.125f, 0.125f}), buffer);
+
+ // balance of 1.f is right.
+ balance.setBalance(1.f);
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{0.f, -1.f, 0.5f, 0.25f, 0.f, 0.75f, 0.f, 0.125f}), buffer);
+
+ // balance of -1.f is left.
+ buffer = {1.f, -1.f, 0.5f, 0.25f, 0.75f, 0.75f, 0.125f, 0.125f};
+ balance.setBalance(-1.f); // to left
+ ASSERT_EQ(-1.f, balance.getBalance());
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, 0.f, 0.5f, 0.25f, 0.75f, 0.f, 0.125f, 0.f}), buffer);
+}
+
+TEST(audio_utils_balance, lfe) {
+ // disable ramping so we can check single frame processing.
+ android::audio_utils::Balance balance(false /* ramp */);
+ ASSERT_EQ(false, balance.getRamp());
+ std::vector<float> buffer = {1.f, -1.f};
+
+ // NOTE: single channel falls under mono exception (we ignore balance)
+ // so we pair with another "center" channel.
+ // LFE by itself is considered "center".
+ for (auto channelMask : {
+ (AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+ (AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2),
+ }) {
+ balance.setChannelMask((audio_channel_mask_t)channelMask);
+
+ // balance of 0 is no change.
+ balance.setBalance(0.f);
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f}), buffer);
+
+ // balance of 1.f is right. (center unaffected)
+ balance.setBalance(1.f);
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f}), buffer);
+
+ // balance of -1.f is left. (center unaffected)
+ balance.setBalance(-1.f);
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f}), buffer);
+ }
+
+ // If both LFE and LFE2 are present, we assume L/R.
+ balance.setChannelMask((audio_channel_mask_t)
+ (AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2));
+ // balance of 0 is no change.
+ balance.setBalance(0.f); // to left
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, -1.f}), buffer);
+
+ // balance of 1.f is right.
+ balance.setBalance(1.f);
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{0.f, -1.f}), buffer);
+
+ // balance of -1.f is left.
+ buffer = {1.f, -1.f};
+ balance.setBalance(-1.f); // to left
+ balance.process(buffer.data(), 1 /* frames */);
+ ASSERT_EQ((std::vector<float>{1.f, 0.f}), buffer);
+}