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