xref: /aosp_15_r20/frameworks/av/services/audiopolicy/tests/spatializer_tests.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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