1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "Spatializer_Test"
18
19 #include "Spatializer.h"
20
21 #include <string>
22 #include <unordered_set>
23
24 #include <gtest/gtest.h>
25
26 #include <android/media/audio/common/AudioLatencyMode.h>
27 #include <android/media/audio/common/HeadTracking.h>
28 #include <android/media/audio/common/Spatialization.h>
29 #include <com_android_media_audio.h>
30 #include <utils/Log.h>
31
32 using namespace android;
33 using media::audio::common::HeadTracking;
34 using media::audio::common::Spatialization;
35
36 // Test Spatializer Helper Methods
37
TEST(Spatializer,containsImmersiveChannelMask)38 TEST(Spatializer, containsImmersiveChannelMask) {
39 // Regardless of the implementation, we expect the following
40 // behavior.
41
42 // Pure non-immersive
43 EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
44 { AUDIO_CHANNEL_OUT_STEREO }));
45 EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
46 { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO }));
47 EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
48 { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
49 AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_MONO }));
50
51 // Pure immersive
52 EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
53 { AUDIO_CHANNEL_OUT_5POINT1 }));
54 EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
55 { AUDIO_CHANNEL_OUT_7POINT1 }));
56 EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
57 { AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_7POINT1,
58 AUDIO_CHANNEL_OUT_22POINT2 }));
59
60 // Mixed immersive/non-immersive
61 EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
62 { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_7POINT1POINT4 }));
63 EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
64 { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
65 AUDIO_CHANNEL_OUT_7POINT1 }));
66 }
67
68 class TestSpatializerPolicyCallback :
69 public SpatializerPolicyCallback {
70 public:
onCheckSpatializer()71 void onCheckSpatializer() override {};
72 };
73
74 class SpatializerTest : public ::testing::Test {
75 protected:
SetUp()76 void SetUp() override {
77 const sp<EffectsFactoryHalInterface> effectsFactoryHal
78 = EffectsFactoryHalInterface::create();
79 mSpatializer = Spatializer::create(&mTestCallback, effectsFactoryHal);
80 if (mSpatializer == nullptr) {
81 GTEST_SKIP() << "Skipping Spatializer tests: no spatializer";
82 }
83 std::vector<Spatialization::Level> levels;
84 binder::Status status = mSpatializer->getSupportedLevels(&levels);
85 ASSERT_TRUE(status.isOk());
86 for (auto level : levels) {
87 if (level != Spatialization::Level::NONE) {
88 mSpatializer->setLevel(level);
89 break;
90 }
91 }
92 mSpatializer->setOutput(sTestOutput);
93 }
94
TearDown()95 void TearDown() override {
96 if (mSpatializer == nullptr) {
97 return;
98 }
99 mSpatializer->setLevel(Spatialization::Level::NONE);
100 mSpatializer->setOutput(AUDIO_IO_HANDLE_NONE);
101 mSpatializer->setDesiredHeadTrackingMode(HeadTracking::Mode::DISABLED);
102 mSpatializer->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
103 mSpatializer->updateActiveTracks({});
104 }
105
106 static constexpr audio_io_handle_t sTestOutput= 1977;
107 static constexpr int sTestSensorHandle = 1980;
108
109 const static inline std::vector<audio_latency_mode_t> sA2DPLatencyModes = {
110 AUDIO_LATENCY_MODE_LOW,
111 AUDIO_LATENCY_MODE_FREE
112 };
113 const static inline std::vector<audio_latency_mode_t> sBLELatencyModes = {
114 AUDIO_LATENCY_MODE_LOW,
115 AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE,
116 AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE,
117 AUDIO_LATENCY_MODE_FREE
118 };
119
setpUpForHeadtracking()120 bool setpUpForHeadtracking() {
121 bool htSupported;
122 mSpatializer->isHeadTrackingSupported(&htSupported);
123 if (!htSupported) {
124 return false;
125 }
126
127 std::vector<HeadTracking::Mode> htModes;
128 mSpatializer->getSupportedHeadTrackingModes(&htModes);
129 for (auto htMode : htModes) {
130 if (htMode != HeadTracking::Mode::DISABLED) {
131 mSpatializer->setDesiredHeadTrackingMode(htMode);
132 break;
133 }
134 }
135
136 mSpatializer->setHeadSensor(sTestSensorHandle);
137 return true;
138 }
139
140 TestSpatializerPolicyCallback mTestCallback;
141 sp<Spatializer> mSpatializer;
142 };
143
TEST_F(SpatializerTest,SupportedA2dpLatencyTest)144 TEST_F(SpatializerTest, SupportedA2dpLatencyTest) {
145 if (!setpUpForHeadtracking()) {
146 GTEST_SKIP() << "Skipping SupportedA2dpLatencyTest: head tracking not supported";
147 }
148 std::vector<audio_latency_mode_t> latencies = sA2DPLatencyModes;
149 mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies));
150
151 std::vector<audio_latency_mode_t> supportedLatencies =
152 mSpatializer->getSupportedLatencyModes();
153
154 ASSERT_TRUE(supportedLatencies == sA2DPLatencyModes);
155 // Free mode must always be the last of the ordered list
156 ASSERT_TRUE(supportedLatencies.back() == AUDIO_LATENCY_MODE_FREE);
157 }
158
TEST_F(SpatializerTest,SupportedBleLatencyTest)159 TEST_F(SpatializerTest, SupportedBleLatencyTest) {
160 if (!setpUpForHeadtracking()) {
161 GTEST_SKIP() << "Skipping SupportedBleLatencyTest: head tracking not supported";
162 }
163 if (!com::android::media::audio::dsa_over_bt_le_audio()) {
164 GTEST_SKIP() << "Skipping SupportedBleLatencyTest: DSA over LE not enabled";
165 }
166 std::vector<audio_latency_mode_t> latencies = sBLELatencyModes;
167 mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies));
168
169 std::vector<audio_latency_mode_t> supportedLatencies =
170 mSpatializer->getSupportedLatencyModes();
171
172 ASSERT_TRUE(supportedLatencies.back() == AUDIO_LATENCY_MODE_FREE);
173 ASSERT_TRUE(std::find(supportedLatencies.begin(), supportedLatencies.end(),
174 AUDIO_LATENCY_MODE_LOW) != supportedLatencies.end());
175
176 std::vector<audio_latency_mode_t> orderedLowLatencyModes =
177 mSpatializer->getOrderedLowLatencyModes();
178
179 std::vector<audio_latency_mode_t> supportedLowLatencyModes;
180 // remove free mode at the end of the supported list to only retain low latency modes
181 std::copy(supportedLatencies.begin(),
182 supportedLatencies.begin() + supportedLatencies.size() - 1,
183 std::back_inserter(supportedLowLatencyModes));
184
185 // Verify that supported low latency modes are always in ordered latency modes list and
186 // in the same order
187 std::vector<audio_latency_mode_t>::iterator lastIt = orderedLowLatencyModes.begin();
188 for (auto latency : supportedLowLatencyModes) {
189 auto it = std::find(orderedLowLatencyModes.begin(), orderedLowLatencyModes.end(), latency);
190 ASSERT_NE(it, orderedLowLatencyModes.end());
191 ASSERT_LE(lastIt, it);
192 lastIt = it;
193 }
194 }
195
TEST_F(SpatializerTest,RequestedA2dpLatencyTest)196 TEST_F(SpatializerTest, RequestedA2dpLatencyTest) {
197 if (!setpUpForHeadtracking()) {
198 GTEST_SKIP() << "Skipping RequestedA2dpLatencyTest: head tracking not supported";
199 }
200
201 std::vector<audio_latency_mode_t> latencies = sA2DPLatencyModes;
202 mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies));
203
204 // requested latency mode must be free if no spatialized tracks are active
205 audio_latency_mode_t requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
206 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
207
208 // requested latency mode must be low if at least one spatialized tracks is active
209 mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
210 requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
211 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_LOW);
212
213 // requested latency mode must be free after stopping the last spatialized tracks
214 mSpatializer->updateActiveTracks({});
215 requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
216 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
217 }
218
TEST_F(SpatializerTest,RequestedBleLatencyTest)219 TEST_F(SpatializerTest, RequestedBleLatencyTest) {
220 if (!setpUpForHeadtracking()) {
221 GTEST_SKIP() << "Skipping RequestedBleLatencyTest: head tracking not supported";
222 }
223 if (!com::android::media::audio::dsa_over_bt_le_audio()) {
224 GTEST_SKIP() << "Skipping RequestedBleLatencyTest: DSA over LE not enabled";
225 }
226
227 mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput,
228 { AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE,
229 AUDIO_LATENCY_MODE_FREE });
230
231 // requested latency mode must be free if no spatialized tracks are active
232 audio_latency_mode_t requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
233 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
234
235 // requested latency mode must be low software if at least one spatialized tracks is active
236 // and the only supported low latency mode is low software
237 mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
238 requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
239 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE);
240
241 mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput,
242 { AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE,
243 AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE,
244 AUDIO_LATENCY_MODE_FREE });
245
246 requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
247 HeadTracking::ConnectionMode connectionMode = mSpatializer->getHeadtrackingConnectionMode();
248
249 // If low hardware mode is used, the spatializer must use either use one of the sensor
250 // connection tunneled modes.
251 // Otherwise, low software mode must be used
252 if (requestedLatencyMode == AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE) {
253 ASSERT_TRUE(connectionMode == HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_TUNNEL
254 || connectionMode == HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_SW);
255 } else {
256 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE);
257 }
258
259 // requested latency mode must be free after stopping the last spatialized tracks
260 mSpatializer->updateActiveTracks({});
261 requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
262 ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
263 }
264