1 /*
2  * Copyright 2022 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 "SpatializerTest"
18 
19 #include <system/audio_effects/effect_spatializer.h>
20 #include "EffectTestHelper.h"
21 
22 using namespace android;
23 
24 // relying on dlsym to fill the interface context
__anon5499391c0102null25 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = [] {
26     audio_effect_library_t symbol{};
27     void* effectLib = dlopen("libspatialaudio.so", RTLD_NOW);
28     if (effectLib) {
29         audio_effect_library_t* effectInterface =
30                 (audio_effect_library_t*)dlsym(effectLib, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
31         if (effectInterface == nullptr) {
32             ALOGE("dlsym failed: %s", dlerror());
33             dlclose(effectLib);
34             exit(-1);
35         }
36         symbol = (audio_effect_library_t)(*effectInterface);
37     } else {
38         ALOGE("dlopen failed: %s", dlerror());
39         exit(-1);
40     }
41     return symbol;
42 }();
43 
44 // channel masks
45 constexpr audio_channel_mask_t kSpatializerChMasks[] = {
46         AUDIO_CHANNEL_OUT_5POINT1,
47 };
48 constexpr size_t kNumSpatializerChMasks = std::size(kSpatializerChMasks);
49 
50 // sampleRates
51 // TODO(b/234170025): Add all sampling rates once they are handled by spatializer
52 constexpr int kSpatializerSampleRates[] = {44100, 48000, 96000};
53 constexpr size_t kNumSpatializerSampleRates = std::size(kSpatializerSampleRates);
54 
55 // frame counts
56 // TODO(b/234620538): Add sizes smaller than 80 once they are handled by spatializer
57 constexpr size_t kSpatializerFrameCounts[] = {4800, 1920, 480, 80};
58 constexpr size_t kNumSpatializerFrameCounts = std::size(kSpatializerFrameCounts);
59 
60 // effect uuids
61 constexpr effect_uuid_t kSpatializerEffectUuids[] = {
62         {0xcc4677de, 0xff72, 0x11eb, 0x9a03, {0x02, 0x42, 0xac, 0x13, 0x00, 0x03}},
63 };
64 const size_t kNumSpatializerEffectUuids = std::size(kSpatializerEffectUuids);
65 
66 constexpr float kMinAmplitude = -1.0f;
67 constexpr float kMaxAmplitude = 1.0f;
68 constexpr float kSNRThreshold = 100.0f;
69 constexpr size_t kNumBufferSplits = 2;
70 
71 using SingleEffectTestParam = std::tuple<int, int, int, int, int>;
72 
73 class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
74   public:
SingleEffectTest()75     SingleEffectTest()
76         : mInputChMask(kSpatializerChMasks[std::get<0>(GetParam())]),
77           mInputChannelCount(audio_channel_count_from_out_mask(mInputChMask)),
78           mOutputChMask(AUDIO_CHANNEL_OUT_STEREO),
79           mOutputChannelCount(audio_channel_count_from_out_mask(mOutputChMask)),
80           mSampleRate(kSpatializerSampleRates[std::get<1>(GetParam())]),
81           mFrameCount(kSpatializerFrameCounts[std::get<2>(GetParam())]),
82           mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
83           mTotalFrameCount(mFrameCount * mLoopCount),
84           mUuid(&kSpatializerEffectUuids[std::get<4>(GetParam())]) {}
SetUp()85     void SetUp() override {
86         ASSERT_EQ(AUDIO_EFFECT_LIBRARY_TAG, AUDIO_EFFECT_LIBRARY_INFO_SYM.tag)
87                 << "Invalid effect tag";
88     }
89     const size_t mInputChMask;
90     const size_t mInputChannelCount;
91     const size_t mOutputChMask;
92     const size_t mOutputChannelCount;
93     const size_t mSampleRate;
94     const size_t mFrameCount;
95     const size_t mLoopCount;
96     const size_t mTotalFrameCount;
97     const effect_uuid_t* mUuid;
98 };
99 
100 // Test basic spatializer functionality (does not crash) for various combinations of sampling
101 // rates, channel masks and frame counts.
TEST_P(SingleEffectTest,SimpleProcess)102 TEST_P(SingleEffectTest, SimpleProcess) {
103     SCOPED_TRACE(testing::Message()
104                  << "chMask: " << mInputChMask << " sampleRate: " << mSampleRate);
105 
106     EffectTestHelper effect(mUuid, mInputChMask, mOutputChMask, mSampleRate, mFrameCount,
107                             mLoopCount);
108     ASSERT_NO_FATAL_FAILURE(effect.createEffect());
109     ASSERT_NO_FATAL_FAILURE(effect.setConfig());
110 
111     // Initialize input buffer with deterministic pseudo-random values
112     std::vector<float> input(mTotalFrameCount * mInputChannelCount);
113     std::vector<float> output(mTotalFrameCount * mOutputChannelCount);
114     std::minstd_rand gen(mInputChMask);
115     std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
116     for (auto& in : input) {
117         in = dis(gen);
118     }
119     ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
120     ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
121 }
122 
123 INSTANTIATE_TEST_SUITE_P(SpatializerTest, SingleEffectTest,
124                          ::testing::Combine(::testing::Range(0, (int)kNumSpatializerChMasks),
125                                             ::testing::Range(0, (int)kNumSpatializerSampleRates),
126                                             ::testing::Range(0, (int)kNumSpatializerFrameCounts),
127                                             ::testing::Range(0,
128                                                              (int)EffectTestHelper::kNumLoopCounts),
129                                             ::testing::Range(0, (int)kNumSpatializerEffectUuids)));
130 
131 using SingleEffectComparisonTestParam = std::tuple<int, int, int>;
132 
133 class SingleEffectComparisonTest
134     : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
135   public:
SingleEffectComparisonTest()136     SingleEffectComparisonTest()
137         : mInputChMask(kSpatializerChMasks[std::get<0>(GetParam())]),
138           mInputChannelCount(audio_channel_count_from_out_mask(mInputChMask)),
139           mOutputChMask(AUDIO_CHANNEL_OUT_STEREO),
140           mOutputChannelCount(audio_channel_count_from_out_mask(mOutputChMask)),
141           mSampleRate(kSpatializerSampleRates[std::get<1>(GetParam())]),
142           mUuid(&kSpatializerEffectUuids[std::get<2>(GetParam())]) {}
143 
144     const size_t mInputChMask;
145     const size_t mInputChannelCount;
146     const size_t mOutputChMask;
147     const size_t mOutputChannelCount;
148     const size_t mSampleRate;
149     const effect_uuid_t* mUuid;
150 };
151 
152 // Ensure that effect produces similar output when an input is fed in a single call
153 // or called multiples times with buffer split into smaller parts
154 
155 // TODO(b/234619903): This is currently disabled as output from the spatializer has
156 // an algorithm delay that varies with frame count and hence makes it tricky to
157 // compare output from two cases with different frame counts.
158 // Feed valid input to spatializer and dump the output to verify spatializer is being
159 // correctly initialized and once that is verified, enable the following
TEST_P(SingleEffectComparisonTest,DISABLED_SimpleProcess)160 TEST_P(SingleEffectComparisonTest, DISABLED_SimpleProcess) {
161     SCOPED_TRACE(testing::Message()
162                  << "chMask: " << mInputChMask << " sampleRate: " << mSampleRate);
163     int testDurationMs = 20; // 20 ms
164     int testFrameCount = (mSampleRate * testDurationMs) / 1000;
165     int totalFrameCount = testFrameCount * kNumBufferSplits;
166     size_t totalInSamples = totalFrameCount * mInputChannelCount;
167     size_t totalOutSamples = totalFrameCount * mOutputChannelCount;
168     std::vector<float> input(totalInSamples);
169     std::vector<float> outRef(totalOutSamples);
170     std::vector<float> outTest(totalOutSamples);
171 
172     // Initialize input buffer with deterministic pseudo-random values
173     std::minstd_rand gen(mInputChMask);
174     std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
175     for (auto& in : input) {
176         in = dis(gen);
177     }
178 
179     EffectTestHelper refEffect(mUuid, mInputChMask, mOutputChMask, mSampleRate, totalFrameCount, 1);
180     ASSERT_NO_FATAL_FAILURE(refEffect.createEffect());
181     ASSERT_NO_FATAL_FAILURE(refEffect.setConfig());
182     ASSERT_NO_FATAL_FAILURE(refEffect.process(input.data(), outRef.data()));
183     ASSERT_NO_FATAL_FAILURE(refEffect.releaseEffect());
184 
185     EffectTestHelper testEffect(mUuid, mInputChMask, mOutputChMask, mSampleRate,
186                                 totalFrameCount / kNumBufferSplits, kNumBufferSplits);
187     ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
188     ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
189     ASSERT_NO_FATAL_FAILURE(testEffect.process(input.data(), outTest.data()));
190     ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
191 
192     float snr = computeSnr(outTest.data(), outRef.data(), totalOutSamples);
193     ASSERT_GT(snr, kSNRThreshold) << "SNR between reference and test output " << snr
194                                   << " is lower than required " << kSNRThreshold;
195 }
196 
197 INSTANTIATE_TEST_SUITE_P(SpatializerTest, SingleEffectComparisonTest,
198                          ::testing::Combine(::testing::Range(0, (int)kNumSpatializerChMasks),
199                                             ::testing::Range(0, (int)kNumSpatializerSampleRates),
200                                             ::testing::Range(0, (int)kNumSpatializerEffectUuids)));
201 
202 // This test checks if get/set Spatializer effect params are in accordance with documentation. The
203 // test doesn't validate the functionality of the params configured. It only checks the return
204 // status of API calls.
TEST(ParameterTests,CheckParameterSupport)205 TEST(ParameterTests, CheckParameterSupport) {
206     EffectTestHelper effect(&kSpatializerEffectUuids[0], kSpatializerChMasks[0],
207                             AUDIO_CHANNEL_OUT_STEREO, kSpatializerSampleRates[0],
208                             kSpatializerFrameCounts[0], EffectTestHelper::kLoopCounts[0]);
209     ASSERT_NO_FATAL_FAILURE(effect.createEffect());
210 
211     // capture list of channel masks supported
212     std::vector<audio_channel_mask_t> channelMasks;
213     int status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS, channelMasks);
214     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
215     if (!status) {
216         EXPECT_EQ(1, channelMasks.size());
217         EXPECT_EQ(AUDIO_CHANNEL_OUT_5POINT1, channelMasks[0]);
218     }
219 
220     // capture list of spatialization levels supported
221     std::vector<int8_t> spatializationLevels;
222     status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_LEVELS, spatializationLevels);
223     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
224     if (!status) {
225         EXPECT_EQ(1, spatializationLevels.size());
226         EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, spatializationLevels[0]);
227     }
228 
229     // capture list of spatialization modes supported
230     std::vector<int8_t> spatializationModes;
231     status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES,
232                                    spatializationModes);
233     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
234     if (!status) {
235         EXPECT_EQ(1, spatializationModes.size());
236         EXPECT_EQ(SPATIALIZATION_MODE_BINAURAL, spatializationModes[0]);
237     }
238 
239     // check if head tracking is supported
240     std::vector<int8_t> headTracking;
241     status = effect.getParam<false>(SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED, headTracking);
242     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
243     if (!status) {
244         EXPECT_EQ(1, headTracking.size());
245         EXPECT_EQ(true, headTracking[0]);
246     }
247 
248     // verify spatialization level setting
249     std::vector<int8_t> level;
250     status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
251     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
252     if (!status) {
253         EXPECT_EQ(1, level.size());
254         EXPECT_EQ(SPATIALIZATION_LEVEL_NONE, level[0]);
255     }
256 
257     ASSERT_NO_FATAL_FAILURE(effect.setConfig());
258 
259     status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
260     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
261     if (!status) {
262         EXPECT_EQ(1, level.size());
263         EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, level[0]);
264     }
265 
266     // try setting unsupported parameters
267     level.clear();
268     level.push_back(SPATIALIZATION_LEVEL_MCHAN_BED_PLUS_OBJECTS);
269     ASSERT_EQ(1, level.size());
270     EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_LEVEL, level));
271 
272     // Ensure that unsupported level isn't set by above setParam
273     status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
274     EXPECT_EQ(status, 0) << "get Param returned an error " << status;
275     if (!status) {
276         EXPECT_EQ(1, level.size());
277         EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, level[0]);
278     }
279 
280     std::vector<float> hingeAngle = {3.1415f};
281     ASSERT_EQ(1, hingeAngle.size());
282     EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_HINGE_ANGLE, hingeAngle));
283 
284     std::vector<int8_t> headTrackingMode = {2};  // RELATIVE_WORLD
285     ASSERT_EQ(1, headTrackingMode.size());
286     EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_HEADTRACKING_MODE, headTrackingMode));
287 
288     // try setting supported parameters
289     std::vector<float> vectorFloat = {0.1, 0.2, 0.15, 0.04, 2.23, 3.14};
290     ASSERT_EQ(6, vectorFloat.size());
291     EXPECT_EQ(0, effect.setParam(SPATIALIZER_PARAM_HEAD_TO_STAGE, vectorFloat));
292 
293     ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
294 }
295 
main(int argc,char ** argv)296 int main(int argc, char** argv) {
297     ::testing::InitGoogleTest(&argc, argv);
298     int status = RUN_ALL_TESTS();
299     ALOGD("Test result = %d\n", status);
300     return status;
301 }
302