1 /*
2  * Copyright 2021 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 #include <system/audio_effects/effect_bassboost.h>
18 #include <system/audio_effects/effect_equalizer.h>
19 #include <system/audio_effects/effect_virtualizer.h>
20 #include "EffectTestHelper.h"
21 
22 using namespace android;
23 typedef enum {
24     EFFECT_BASS_BOOST,
25     EFFECT_EQUALIZER,
26     EFFECT_VIRTUALIZER,
27     EFFECT_VOLUME
28 } effect_type_t;
29 
30 const std::map<effect_type_t, effect_uuid_t> kEffectUuids = {
31         // NXP SW BassBoost
32         {EFFECT_BASS_BOOST,
33          {0x8631f300, 0x72e2, 0x11df, 0xb57e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}},
34         // NXP SW Equalizer
35         {EFFECT_EQUALIZER,
36          {0xce772f20, 0x847d, 0x11df, 0xbb17, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}},
37         // NXP SW Virtualizer
38         {EFFECT_VIRTUALIZER,
39          {0x1d4033c0, 0x8557, 0x11df, 0x9f2d, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}},
40         // NXP SW Volume
41         {EFFECT_VOLUME, {0x119341a0, 0x8469, 0x11df, 0x81f9, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}},
42 };
43 
44 const size_t kNumEffectUuids = std::size(kEffectUuids);
45 
46 constexpr float kMinAmplitude = -1.0f;
47 constexpr float kMaxAmplitude = 1.0f;
48 
49 using SingleEffectTestParam = std::tuple<int, int, int, int, int>;
50 class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
51   public:
SingleEffectTest()52     SingleEffectTest()
53         : mChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
54           mChannelCount(audio_channel_count_from_out_mask(mChMask)),
55           mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
56           mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
57           mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
58           mTotalFrameCount(mFrameCount * mLoopCount),
59           mEffectType((effect_type_t)std::get<4>(GetParam())),
60           mUuid(kEffectUuids.at(mEffectType)) {}
61 
62     const size_t mChMask;
63     const size_t mChannelCount;
64     const size_t mSampleRate;
65     const size_t mFrameCount;
66     const size_t mLoopCount;
67     const size_t mTotalFrameCount;
68     const effect_type_t mEffectType;
69     const effect_uuid_t mUuid;
70 };
71 
72 // Tests applying a single effect
TEST_P(SingleEffectTest,SimpleProcess)73 TEST_P(SingleEffectTest, SimpleProcess) {
74     SCOPED_TRACE(testing::Message()
75                  << "chMask: " << mChMask << " sampleRate: " << mSampleRate
76                  << " frameCount: " << mFrameCount << " loopCount: " << mLoopCount);
77 
78     EffectTestHelper effect(&mUuid, mChMask, mChMask, mSampleRate, mFrameCount, mLoopCount);
79 
80     ASSERT_NO_FATAL_FAILURE(effect.createEffect());
81     ASSERT_NO_FATAL_FAILURE(effect.setConfig());
82 
83     // Initialize input buffer with deterministic pseudo-random values
84     std::vector<float> input(mTotalFrameCount * mChannelCount);
85     std::vector<float> output(mTotalFrameCount * mChannelCount);
86     std::minstd_rand gen(mChMask);
87     std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
88     for (auto& in : input) {
89         in = dis(gen);
90     }
91     ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
92     ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
93 }
94 
95 INSTANTIATE_TEST_SUITE_P(
96         EffectBundleTestAll, SingleEffectTest,
97         ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
98                            ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
99                            ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
100                            ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
101                            ::testing::Range(0, (int)kNumEffectUuids)));
102 
103 using SingleEffectComparisonTestParam = std::tuple<int, int, int, int>;
104 class SingleEffectComparisonTest
105     : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
106   public:
SingleEffectComparisonTest()107     SingleEffectComparisonTest()
108         : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
109           mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
110           mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
111           mTotalFrameCount(mFrameCount * mLoopCount),
112           mEffectType((effect_type_t)std::get<3>(GetParam())),
113           mUuid(kEffectUuids.at(mEffectType)) {}
114 
115     const size_t mSampleRate;
116     const size_t mFrameCount;
117     const size_t mLoopCount;
118     const size_t mTotalFrameCount;
119     const effect_type_t mEffectType;
120     const effect_uuid_t mUuid;
121 };
122 
123 // Compares first two channels in multi-channel output to stereo output when same effect is applied
TEST_P(SingleEffectComparisonTest,SimpleProcess)124 TEST_P(SingleEffectComparisonTest, SimpleProcess) {
125     SCOPED_TRACE(testing::Message() << " sampleRate: " << mSampleRate << " frameCount: "
126                                     << mFrameCount << " loopCount: " << mLoopCount);
127 
128     // Initialize mono input buffer with deterministic pseudo-random values
129     std::vector<float> monoInput(mTotalFrameCount);
130 
131     std::minstd_rand gen(mSampleRate);
132     std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
133     for (auto& in : monoInput) {
134         in = dis(gen);
135     }
136 
137     // Generate stereo by repeating mono channel data
138     std::vector<float> stereoInput(mTotalFrameCount * FCC_2);
139     adjust_channels(monoInput.data(), FCC_1, stereoInput.data(), FCC_2, sizeof(float),
140                     mTotalFrameCount * sizeof(float) * FCC_1);
141 
142     // Apply effect on stereo channels
143     EffectTestHelper stereoEffect(&mUuid, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_STEREO,
144                                   mSampleRate, mFrameCount, mLoopCount);
145 
146     ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
147     ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
148 
149     std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
150     ASSERT_NO_FATAL_FAILURE(stereoEffect.process(stereoInput.data(), stereoOutput.data()));
151     ASSERT_NO_FATAL_FAILURE(stereoEffect.releaseEffect());
152 
153     // Convert stereo float data to stereo int16_t to be used as reference
154     std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
155     memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
156 
157     for (size_t chMask : EffectTestHelper::kChMasks) {
158         size_t channelCount = audio_channel_count_from_out_mask(chMask);
159         EffectTestHelper testEffect(&mUuid, chMask, chMask, mSampleRate, mFrameCount, mLoopCount);
160 
161         ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
162         ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
163 
164         std::vector<float> testInput(mTotalFrameCount * channelCount);
165 
166         // Repeat mono channel data to all the channels
167         // adjust_channels() zero fills channels > 2, hence can't be used here
168         for (size_t i = 0; i < mTotalFrameCount; ++i) {
169             auto* fp = &testInput[i * channelCount];
170             std::fill(fp, fp + channelCount, monoInput[i]);
171         }
172 
173         std::vector<float> testOutput(mTotalFrameCount * channelCount);
174         ASSERT_NO_FATAL_FAILURE(testEffect.process(testInput.data(), testOutput.data()));
175         ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
176 
177         // Extract first two channels
178         std::vector<float> stereoTestOutput(mTotalFrameCount * FCC_2);
179         adjust_channels(testOutput.data(), channelCount, stereoTestOutput.data(), FCC_2,
180                         sizeof(float), mTotalFrameCount * sizeof(float) * channelCount);
181 
182         // Convert the test data to int16_t
183         std::vector<int16_t> stereoTestI16(mTotalFrameCount * FCC_2);
184         memcpy_to_i16_from_float(stereoTestI16.data(), stereoTestOutput.data(),
185                                  mTotalFrameCount * FCC_2);
186 
187         if (EFFECT_BASS_BOOST == mEffectType) {
188             // SNR must be above the threshold
189             float snr = computeSnr<int16_t>(stereoRefI16.data(), stereoTestI16.data(),
190                                             mTotalFrameCount * FCC_2);
191             ASSERT_GT(snr, EffectTestHelper::kSNRThreshold)
192                     << "SNR " << snr << "is lower than " << EffectTestHelper::kSNRThreshold;
193         } else {
194             ASSERT_EQ(0,
195                       memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
196                     << "First two channels do not match with stereo output \n";
197         }
198     }
199 }
200 
201 INSTANTIATE_TEST_SUITE_P(
202         EffectBundleTestAll, SingleEffectComparisonTest,
203         ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
204                            ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
205                            ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
206                            ::testing::Range(0, (int)kNumEffectUuids)));
207 
208 using SingleEffectDefaultSetParamTestParam = std::tuple<int, int, int>;
209 class SingleEffectDefaultSetParamTest
210     : public ::testing::TestWithParam<SingleEffectDefaultSetParamTestParam> {
211   public:
SingleEffectDefaultSetParamTest()212     SingleEffectDefaultSetParamTest()
213         : mChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
214           mChannelCount(audio_channel_count_from_out_mask(mChMask)),
215           mSampleRate(16000),
216           mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
217           mLoopCount(1),
218           mTotalFrameCount(mFrameCount * mLoopCount),
219           mEffectType((effect_type_t)std::get<2>(GetParam())),
220           mUuid(kEffectUuids.at(mEffectType)) {}
221 
222     const size_t mChMask;
223     const size_t mChannelCount;
224     const size_t mSampleRate;
225     const size_t mFrameCount;
226     const size_t mLoopCount;
227     const size_t mTotalFrameCount;
228     const effect_type_t mEffectType;
229     const effect_uuid_t mUuid;
230 };
231 
232 // Tests verifying that redundant setParam calls do not alter output
TEST_P(SingleEffectDefaultSetParamTest,SimpleProcess)233 TEST_P(SingleEffectDefaultSetParamTest, SimpleProcess) {
234     SCOPED_TRACE(testing::Message()
235                  << "chMask: " << mChMask << " sampleRate: " << mSampleRate
236                  << " frameCount: " << mFrameCount << " loopCount: " << mLoopCount);
237     // effect.process() handles mTotalFrameCount * mChannelCount samples in each call.
238     // This test calls process() twice per effect, hence total samples when allocating
239     // input and output vectors is twice the number of samples processed in one call.
240     size_t totalNumSamples = 2 * mTotalFrameCount * mChannelCount;
241     // Initialize input buffer with deterministic pseudo-random values
242     std::vector<float> input(totalNumSamples);
243     std::minstd_rand gen(mChMask);
244     std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
245     for (auto& in : input) {
246         in = dis(gen);
247     }
248 
249     uint32_t key;
250     int32_t value1, value2;
251     switch (mEffectType) {
252         case EFFECT_BASS_BOOST:
253             key = BASSBOOST_PARAM_STRENGTH;
254             value1 = 1;
255             value2 = 14;
256             break;
257         case EFFECT_VIRTUALIZER:
258             key = VIRTUALIZER_PARAM_STRENGTH;
259             value1 = 0;
260             value2 = 100;
261             break;
262         case EFFECT_EQUALIZER:
263             key = EQ_PARAM_CUR_PRESET;
264             value1 = 0;
265             value2 = 1;
266             break;
267         case EFFECT_VOLUME:
268             key = 0 /* VOLUME_PARAM_LEVEL */;
269             value1 = 0;
270             value2 = -100;
271             break;
272         default:
273             FAIL() << "Unsupported effect type : " << mEffectType;
274     }
275 
276     EffectTestHelper refEffect(&mUuid, mChMask, mChMask, mSampleRate, mFrameCount, mLoopCount);
277 
278     ASSERT_NO_FATAL_FAILURE(refEffect.createEffect());
279     ASSERT_NO_FATAL_FAILURE(refEffect.setConfig());
280 
281     if (EFFECT_BASS_BOOST == mEffectType) {
282         ASSERT_NO_FATAL_FAILURE(refEffect.setParam<int16_t>(key, value1));
283     } else {
284         ASSERT_NO_FATAL_FAILURE(refEffect.setParam<int32_t>(key, value1));
285     }
286     std::vector<float> refOutput(totalNumSamples);
287     float* pInput = input.data();
288     float* pOutput = refOutput.data();
289     ASSERT_NO_FATAL_FAILURE(refEffect.process(pInput, pOutput));
290 
291     pInput += totalNumSamples / 2;
292     pOutput += totalNumSamples / 2;
293     ASSERT_NO_FATAL_FAILURE(refEffect.process(pInput, pOutput));
294     ASSERT_NO_FATAL_FAILURE(refEffect.releaseEffect());
295 
296     EffectTestHelper testEffect(&mUuid, mChMask, mChMask, mSampleRate, mFrameCount, mLoopCount);
297 
298     ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
299     ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
300 
301     if (EFFECT_BASS_BOOST == mEffectType) {
302         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int16_t>(key, value1));
303     } else {
304         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int32_t>(key, value1));
305     }
306 
307     std::vector<float> testOutput(totalNumSamples);
308     pInput = input.data();
309     pOutput = testOutput.data();
310     ASSERT_NO_FATAL_FAILURE(testEffect.process(pInput, pOutput));
311 
312     // Call setParam once to change the parameters, and then call setParam again
313     // to restore the parameters to the initial state, making the first setParam
314     // call redundant
315     if (EFFECT_BASS_BOOST == mEffectType) {
316         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int16_t>(key, value2));
317         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int16_t>(key, value1));
318     } else {
319         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int32_t>(key, value2));
320         ASSERT_NO_FATAL_FAILURE(testEffect.setParam<int32_t>(key, value1));
321     }
322 
323     pInput += totalNumSamples / 2;
324     pOutput += totalNumSamples / 2;
325     ASSERT_NO_FATAL_FAILURE(testEffect.process(pInput, pOutput));
326     ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
327     ASSERT_TRUE(areNearlySame(refOutput.data(), testOutput.data(), totalNumSamples))
328             << "Outputs do not match with default setParam calls";
329 }
330 
331 INSTANTIATE_TEST_SUITE_P(
332         EffectBundleTestAll, SingleEffectDefaultSetParamTest,
333         ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
334                            ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
335                            ::testing::Range(0, (int)kNumEffectUuids)));
336 
main(int argc,char ** argv)337 int main(int argc, char** argv) {
338     ::testing::InitGoogleTest(&argc, argv);
339     int status = RUN_ALL_TESTS();
340     ALOGV("Test result = %d\n", status);
341     return status;
342 }
343