1 /*
2 * Copyright (C) 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 #include <fstream>
18 #include <iostream>
19 #include <string>
20 #include <tuple>
21 #include <vector>
22
23 // #define LOG_NDEBUG 0
24 #define LOG_TAG "AudioEffectAnalyser"
25
26 #include <android-base/file.h>
27 #include <android-base/stringprintf.h>
28 #include <binder/ProcessState.h>
29 #include <gtest/gtest.h>
30 #include <media/AudioEffect.h>
31 #include <system/audio_effects/effect_bassboost.h>
32 #include <system/audio_effects/effect_equalizer.h>
33
34 #include "audio_test_utils.h"
35 #include "pffft.hpp"
36 #include "test_execution_tracer.h"
37
38 #define CHECK_OK(expr, msg) \
39 mStatus = (expr); \
40 if (OK != mStatus) { \
41 mMsg = (msg); \
42 return; \
43 }
44
45 using namespace android;
46
47 constexpr float kDefAmplitude = 0.60f;
48
49 constexpr float kPlayBackDurationSec = 1.5;
50 constexpr float kCaptureDurationSec = 1.0;
51 constexpr float kPrimeDurationInSec = 0.5;
52
53 // chosen to safely sample largest center freq of eq bands
54 constexpr uint32_t kSamplingFrequency = 48000;
55
56 // allows no fmt conversion before fft
57 constexpr audio_format_t kFormat = AUDIO_FORMAT_PCM_FLOAT;
58
59 // playback and capture are done with channel mask configured to mono.
60 // effect analysis should not depend on mask, mono makes it easier.
61
62 constexpr int kNPointFFT = 16384;
63 constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
64
65 // frequency used to generate testing tone
66 constexpr uint32_t kTestFrequency = 1400;
67
68 // Tolerance of audio gain difference in dB, which is 10^(0.1/20) (around 1.0116%) difference in
69 // amplitude
70 constexpr float kAudioGainDiffTolerancedB = .1f;
71
72 const std::string kDataTempPath = "/data/local/tmp";
73
74 const char* gPackageName = "AudioEffectAnalyser";
75
76 static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kCaptureDurationSec,
77 "capture at least, prime, pad, nPointFft size of samples");
78 static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kPlayBackDurationSec,
79 "playback needs to be active during capture");
80
81 struct CaptureEnv {
82 // input args
83 uint32_t mSampleRate{kSamplingFrequency};
84 audio_format_t mFormat{kFormat};
85 audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_IN_MONO};
86 float mCaptureDuration{kCaptureDurationSec};
87 // output val
88 status_t mStatus{OK};
89 std::string mMsg;
90 std::string mDumpFileName;
91
92 ~CaptureEnv();
93 void capture();
94 };
95
~CaptureEnv()96 CaptureEnv::~CaptureEnv() {
97 if (!mDumpFileName.empty()) {
98 std::ifstream f(mDumpFileName);
99 if (f.good()) {
100 f.close();
101 remove(mDumpFileName.c_str());
102 }
103 }
104 }
105
capture()106 void CaptureEnv::capture() {
107 audio_port_v7 port;
108 CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
109 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port),
110 "Could not find port")
111 const auto capture =
112 sp<AudioCapture>::make(AUDIO_SOURCE_REMOTE_SUBMIX, mSampleRate, mFormat, mChannelMask);
113 CHECK_OK(capture->create(), "record creation failed")
114 CHECK_OK(capture->setRecordDuration(mCaptureDuration), "set record duration failed")
115 CHECK_OK(capture->enableRecordDump(), "enable record dump failed")
116 auto cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
117 CHECK_OK(capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture),
118 "addAudioDeviceCallback failed")
119 CHECK_OK(capture->start(), "start recording failed")
120 CHECK_OK(capture->audioProcess(), "recording process failed")
121 CHECK_OK(cbCapture->waitForAudioDeviceCb(), "audio device callback notification timed out");
122 DeviceIdVector routedDeviceIds = capture->getAudioRecordHandle()->getRoutedDeviceIds();
123 if (port.id != routedDeviceIds[0]) {
124 CHECK_OK(BAD_VALUE, "Capture NOT routed on expected port")
125 }
126 CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
127 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port),
128 "Could not find port")
129 CHECK_OK(capture->stop(), "record stop failed")
130 mDumpFileName = capture->getRecordDumpFileName();
131 }
132
133 struct PlaybackEnv {
134 // input args
135 uint32_t mSampleRate{kSamplingFrequency};
136 audio_format_t mFormat{kFormat};
137 audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_OUT_MONO};
138 audio_session_t mSessionId{AUDIO_SESSION_NONE};
139 std::string mRes;
140 // output val
141 status_t mStatus{OK};
142 std::string mMsg;
143
144 void play();
145 };
146
play()147 void PlaybackEnv::play() {
148 const auto ap =
149 sp<AudioPlayback>::make(mSampleRate, mFormat, mChannelMask, AUDIO_OUTPUT_FLAG_NONE,
150 mSessionId, AudioTrack::TRANSFER_OBTAIN);
151 CHECK_OK(ap->loadResource(mRes.c_str()), "Unable to open Resource")
152 const auto cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
153 CHECK_OK(ap->create(), "track creation failed")
154 ap->getAudioTrackHandle()->setVolume(1.0f);
155 CHECK_OK(ap->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback),
156 "addAudioDeviceCallback failed")
157 CHECK_OK(ap->start(), "audio track start failed")
158 CHECK_OK(cbPlayback->waitForAudioDeviceCb(), "audio device callback notification timed out")
159 CHECK_OK(ap->onProcess(), "playback process failed")
160 ap->stop();
161 }
162
generateMultiTone(const std::vector<int> & toneFrequencies,float samplingFrequency,float duration,float amplitude,float * buffer,int numSamples)163 void generateMultiTone(const std::vector<int>& toneFrequencies, float samplingFrequency,
164 float duration, float amplitude, float* buffer, int numSamples) {
165 int totalFrameCount = (samplingFrequency * duration);
166 int limit = std::min(totalFrameCount, numSamples);
167
168 for (auto i = 0; i < limit; i++) {
169 buffer[i] = 0;
170 for (auto j = 0; j < toneFrequencies.size(); j++) {
171 buffer[i] += sin(2 * M_PI * toneFrequencies[j] * i / samplingFrequency);
172 }
173 buffer[i] *= (amplitude / toneFrequencies.size());
174 }
175 }
176
createEffect(const effect_uuid_t * type,audio_session_t sessionId=AUDIO_SESSION_OUTPUT_MIX)177 sp<AudioEffect> createEffect(const effect_uuid_t* type,
178 audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX) {
179 std::string packageName{gPackageName};
180 AttributionSourceState attributionSource;
181 attributionSource.packageName = packageName;
182 attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
183 attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
184 attributionSource.token = sp<BBinder>::make();
185 sp<AudioEffect> effect = sp<AudioEffect>::make(attributionSource);
186 effect->set(type, nullptr, 0, nullptr, sessionId, AUDIO_IO_HANDLE_NONE, {}, false, false);
187 return effect;
188 }
189
computeFilterGainsAtTones(float captureDuration,int nPointFft,std::vector<int> binOffsets,float * inputMag,float * gaindB,const std::string res,audio_session_t sessionId,const std::string res2="",audio_session_t sessionId2=AUDIO_SESSION_NONE)190 void computeFilterGainsAtTones(float captureDuration, int nPointFft, std::vector<int> binOffsets,
191 float* inputMag, float* gaindB, const std::string res,
192 audio_session_t sessionId, const std::string res2 = "",
193 audio_session_t sessionId2 = AUDIO_SESSION_NONE) {
194 int totalFrameCount = captureDuration * kSamplingFrequency;
195 auto output = pffft::AlignedVector<float>(totalFrameCount);
196 auto fftOutput = pffft::AlignedVector<float>(nPointFft);
197 PlaybackEnv argsP, argsP2;
198 argsP.mRes = res;
199 argsP.mSessionId = sessionId;
200 CaptureEnv argsR;
201 argsR.mCaptureDuration = captureDuration;
202 std::thread playbackThread(&PlaybackEnv::play, &argsP);
203 std::optional<std::thread> playbackThread2;
204 if (res2 != "") {
205 argsP2 = {.mSessionId = sessionId2, .mRes = res2};
206 playbackThread2 = std::thread(&PlaybackEnv::play, &argsP2);
207 }
208 std::thread captureThread(&CaptureEnv::capture, &argsR);
209 captureThread.join();
210 playbackThread.join();
211 if (playbackThread2 != std::nullopt) {
212 playbackThread2->join();
213 }
214 ASSERT_EQ(OK, argsR.mStatus) << argsR.mMsg;
215 ASSERT_EQ(OK, argsP.mStatus) << argsP.mMsg;
216 ASSERT_FALSE(argsR.mDumpFileName.empty()) << "recorded not written to file";
217 std::ifstream fin(argsR.mDumpFileName, std::ios::in | std::ios::binary);
218 fin.read((char*)output.data(), totalFrameCount * sizeof(output[0]));
219 fin.close();
220 PFFFT_Setup* handle = pffft_new_setup(nPointFft, PFFFT_REAL);
221 // ignore first few samples. This is to not analyse until audio track is re-routed to remote
222 // submix source, also for the effect filter response to reach steady-state (priming / pruning
223 // samples).
224 int rerouteOffset = kPrimeDurationInSec * kSamplingFrequency;
225 pffft_transform_ordered(handle, output.data() + rerouteOffset, fftOutput.data(), nullptr,
226 PFFFT_FORWARD);
227 pffft_destroy_setup(handle);
228 for (auto i = 0; i < binOffsets.size(); i++) {
229 auto k = binOffsets[i];
230 auto outputMag = sqrt((fftOutput[k * 2] * fftOutput[k * 2]) +
231 (fftOutput[k * 2 + 1] * fftOutput[k * 2 + 1]));
232 if (inputMag == nullptr) {
233 gaindB[i] = 20 * log10(outputMag);
234 } else {
235 gaindB[i] = 20 * log10(outputMag / inputMag[i]);
236 }
237 }
238 }
239
roundToFreqCenteredToFftBin(float binWidth,float freq)240 std::tuple<int, int> roundToFreqCenteredToFftBin(float binWidth, float freq) {
241 int bin_index = std::round(freq / binWidth);
242 int cfreq = std::round(bin_index * binWidth);
243 return std::make_tuple(bin_index, cfreq);
244 }
245
TEST(AudioEffectTest,CheckEqualizerEffect)246 TEST(AudioEffectTest, CheckEqualizerEffect) {
247 audio_session_t sessionId =
248 (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
249 sp<AudioEffect> equalizer = createEffect(SL_IID_EQUALIZER, sessionId);
250 ASSERT_EQ(OK, equalizer->initCheck());
251 ASSERT_EQ(NO_ERROR, equalizer->setEnabled(true));
252 if ((equalizer->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
253 GTEST_SKIP() << "effect processed output inaccessible, skipping test";
254 }
255 #define MAX_PARAMS 64
256 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + MAX_PARAMS];
257 effect_param_t* eqParam = (effect_param_t*)(&buf32);
258
259 // get num of presets
260 eqParam->psize = sizeof(uint32_t);
261 eqParam->vsize = sizeof(uint16_t);
262 *(int32_t*)eqParam->data = EQ_PARAM_GET_NUM_OF_PRESETS;
263 EXPECT_EQ(0, equalizer->getParameter(eqParam));
264 EXPECT_EQ(0, eqParam->status);
265 int numPresets = *((uint16_t*)((int32_t*)eqParam->data + 1));
266
267 // get num of bands
268 eqParam->psize = sizeof(uint32_t);
269 eqParam->vsize = sizeof(uint16_t);
270 *(int32_t*)eqParam->data = EQ_PARAM_NUM_BANDS;
271 EXPECT_EQ(0, equalizer->getParameter(eqParam));
272 EXPECT_EQ(0, eqParam->status);
273 int numBands = *((uint16_t*)((int32_t*)eqParam->data + 1));
274
275 const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
276
277 // get band center frequencies
278 std::vector<int> centerFrequencies;
279 std::vector<int> binOffsets;
280 for (auto i = 0; i < numBands; i++) {
281 eqParam->psize = sizeof(uint32_t) * 2;
282 eqParam->vsize = sizeof(uint32_t);
283 *(int32_t*)eqParam->data = EQ_PARAM_CENTER_FREQ;
284 *((uint16_t*)((int32_t*)eqParam->data + 1)) = i;
285 EXPECT_EQ(0, equalizer->getParameter(eqParam));
286 EXPECT_EQ(0, eqParam->status);
287 float cfreq = *((int32_t*)eqParam->data + 2) / 1000; // milli hz
288 // pick frequency close to bin center frequency
289 auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, cfreq);
290 centerFrequencies.push_back(bin_freq);
291 binOffsets.push_back(bin_index);
292 }
293
294 // input for effect module
295 auto input = pffft::AlignedVector<float>(totalFrameCount);
296 generateMultiTone(centerFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
297 input.data(), totalFrameCount);
298 auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
299 PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
300 pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
301 pffft_destroy_setup(handle);
302 float inputMag[numBands];
303 for (auto i = 0; i < numBands; i++) {
304 auto k = binOffsets[i];
305 inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
306 (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
307 }
308 TemporaryFile tf(kDataTempPath);
309 close(tf.release());
310 std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
311 fout.write((char*)input.data(), input.size() * sizeof(input[0]));
312 fout.close();
313
314 float expGaindB[numBands], actGaindB[numBands];
315
316 std::string msg = "";
317 int numPresetsOk = 0;
318 for (auto preset = 0; preset < numPresets; preset++) {
319 // set preset
320 eqParam->psize = sizeof(uint32_t);
321 eqParam->vsize = sizeof(uint32_t);
322 *(int32_t*)eqParam->data = EQ_PARAM_CUR_PRESET;
323 *((uint16_t*)((int32_t*)eqParam->data + 1)) = preset;
324 EXPECT_EQ(0, equalizer->setParameter(eqParam));
325 EXPECT_EQ(0, eqParam->status);
326 // get preset gains
327 eqParam->psize = sizeof(uint32_t);
328 eqParam->vsize = (numBands + 1) * sizeof(uint32_t);
329 *(int32_t*)eqParam->data = EQ_PARAM_PROPERTIES;
330 EXPECT_EQ(0, equalizer->getParameter(eqParam));
331 EXPECT_EQ(0, eqParam->status);
332 t_equalizer_settings* settings =
333 reinterpret_cast<t_equalizer_settings*>((int32_t*)eqParam->data + 1);
334 EXPECT_EQ(preset, settings->curPreset);
335 EXPECT_EQ(numBands, settings->numBands);
336 for (auto i = 0; i < numBands; i++) {
337 expGaindB[i] = ((int16_t)settings->bandLevels[i]) / 100.0f; // gain in milli bels
338 }
339 memset(actGaindB, 0, sizeof(actGaindB));
340 ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
341 binOffsets, inputMag, actGaindB, tf.path,
342 sessionId));
343 bool isOk = true;
344 for (auto i = 0; i < numBands - 1; i++) {
345 auto diffA = expGaindB[i] - expGaindB[i + 1];
346 auto diffB = actGaindB[i] - actGaindB[i + 1];
347 if (diffA == 0 && fabs(diffA - diffB) > 1.0f) {
348 msg += (android::base::StringPrintf(
349 "For eq preset : %d, between bands %d and %d, expected relative gain is : "
350 "%f, got relative gain is : %f, error : %f \n",
351 preset, i, i + 1, diffA, diffB, diffA - diffB));
352 isOk = false;
353 } else if (diffA * diffB < 0) {
354 msg += (android::base::StringPrintf(
355 "For eq preset : %d, between bands %d and %d, expected relative gain and "
356 "seen relative gain are of opposite signs \n. Expected relative gain is : "
357 "%f, seen relative gain is : %f \n",
358 preset, i, i + 1, diffA, diffB));
359 isOk = false;
360 }
361 }
362 if (isOk) numPresetsOk++;
363 }
364 EXPECT_EQ(numPresetsOk, numPresets) << msg;
365 }
366
TEST(AudioEffectTest,CheckBassBoostEffect)367 TEST(AudioEffectTest, CheckBassBoostEffect) {
368 audio_session_t sessionId =
369 (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
370 sp<AudioEffect> bassboost = createEffect(SL_IID_BASSBOOST, sessionId);
371 ASSERT_EQ(OK, bassboost->initCheck());
372 ASSERT_EQ(NO_ERROR, bassboost->setEnabled(true));
373 if ((bassboost->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
374 GTEST_SKIP() << "effect processed output inaccessible, skipping test";
375 }
376 int32_t buf32[sizeof(effect_param_t) / sizeof(int32_t) + MAX_PARAMS];
377 effect_param_t* bbParam = (effect_param_t*)(&buf32);
378
379 bbParam->psize = sizeof(int32_t);
380 bbParam->vsize = sizeof(int32_t);
381 *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH_SUPPORTED;
382 EXPECT_EQ(0, bassboost->getParameter(bbParam));
383 EXPECT_EQ(0, bbParam->status);
384 bool strengthSupported = *((int32_t*)bbParam->data + 1);
385
386 const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
387
388 // selecting bass frequency, speech tone (for relative gain)
389 std::vector<int> testFrequencies{100, 1200};
390 std::vector<int> binOffsets;
391 for (auto i = 0; i < testFrequencies.size(); i++) {
392 // pick frequency close to bin center frequency
393 auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, testFrequencies[i]);
394 testFrequencies[i] = bin_freq;
395 binOffsets.push_back(bin_index);
396 }
397
398 // input for effect module
399 auto input = pffft::AlignedVector<float>(totalFrameCount);
400 generateMultiTone(testFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
401 input.data(), totalFrameCount);
402 auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
403 PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
404 pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
405 pffft_destroy_setup(handle);
406 float inputMag[testFrequencies.size()];
407 for (auto i = 0; i < testFrequencies.size(); i++) {
408 auto k = binOffsets[i];
409 inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
410 (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
411 }
412 TemporaryFile tf(kDataTempPath);
413 close(tf.release());
414 std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
415 fout.write((char*)input.data(), input.size() * sizeof(input[0]));
416 fout.close();
417
418 float gainWithOutFilter[testFrequencies.size()];
419 memset(gainWithOutFilter, 0, sizeof(gainWithOutFilter));
420 ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, binOffsets,
421 inputMag, gainWithOutFilter, tf.path,
422 AUDIO_SESSION_NONE));
423 float diffA = gainWithOutFilter[0] - gainWithOutFilter[1];
424 float prevGain = -100.f;
425 for (auto strength = 150; strength < 1000; strength += strengthSupported ? 150 : 1000) {
426 // configure filter strength
427 if (strengthSupported) {
428 bbParam->psize = sizeof(int32_t);
429 bbParam->vsize = sizeof(int16_t);
430 *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH;
431 *((int16_t*)((int32_t*)bbParam->data + 1)) = strength;
432 EXPECT_EQ(0, bassboost->setParameter(bbParam));
433 EXPECT_EQ(0, bbParam->status);
434 }
435 float gainWithFilter[testFrequencies.size()];
436 memset(gainWithFilter, 0, sizeof(gainWithFilter));
437 ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
438 binOffsets, inputMag, gainWithFilter,
439 tf.path, sessionId));
440 float diffB = gainWithFilter[0] - gainWithFilter[1];
441 EXPECT_GT(diffB, diffA) << "bassboost effect not seen";
442 EXPECT_GE(diffB, prevGain) << "increase in boost strength causing fall in gain";
443 prevGain = diffB;
444 }
445 }
446
447 // assert the silent audio session with effect does not override the output audio
TEST(AudioEffectTest,SilentAudioEffectSessionNotOverrideOutput)448 TEST(AudioEffectTest, SilentAudioEffectSessionNotOverrideOutput) {
449 audio_session_t sessionId =
450 (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
451 sp<AudioEffect> bassboost = createEffect(SL_IID_BASSBOOST, sessionId);
452 if ((bassboost->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
453 GTEST_SKIP() << "effect processed output inaccessible, skipping test";
454 }
455 ASSERT_EQ(OK, bassboost->initCheck());
456 ASSERT_EQ(NO_ERROR, bassboost->setEnabled(true));
457
458 const auto bin = roundToFreqCenteredToFftBin(kBinWidth, kTestFrequency);
459 const int binIndex = std::get<0 /* index */>(bin);
460 const int binFrequency = std::get<1 /* freq */>(bin);
461
462 const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
463 // input for effect module
464 auto silentAudio = pffft::AlignedVector<float>(totalFrameCount);
465 auto input = pffft::AlignedVector<float>(totalFrameCount);
466 generateMultiTone({binFrequency}, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
467 input.data(), totalFrameCount);
468 TemporaryFile tf(kDataTempPath);
469 close(tf.release());
470 std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
471 fout.write((char*)input.data(), input.size() * sizeof(input[0]));
472 fout.close();
473
474 // play non-silent audio file on AUDIO_SESSION_NONE
475 float audioGain, audioPlusSilentEffectGain;
476 ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, {binIndex},
477 nullptr, &audioGain, tf.path,
478 AUDIO_SESSION_NONE));
479 EXPECT_FALSE(std::isinf(audioGain)) << "output gain should not be -inf";
480
481 TemporaryFile silentFile(kDataTempPath);
482 close(silentFile.release());
483 std::ofstream fSilent(silentFile.path, std::ios::out | std::ios::binary);
484 fSilent.write((char*)silentAudio.data(), silentAudio.size() * sizeof(silentAudio[0]));
485 fSilent.close();
486 // play non-silent audio file on AUDIO_SESSION_NONE and silent audio on sessionId, expect
487 // the new output gain to be almost same as last playback
488 ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(
489 kCaptureDurationSec, kNPointFFT, {binIndex}, nullptr, &audioPlusSilentEffectGain,
490 tf.path, AUDIO_SESSION_NONE, silentFile.path, sessionId));
491 EXPECT_FALSE(std::isinf(audioPlusSilentEffectGain))
492 << "output might have been overwritten in effect accumulate mode";
493 EXPECT_NEAR(audioGain, audioPlusSilentEffectGain, kAudioGainDiffTolerancedB)
494 << " output gain should almost same with one more silent audio stream";
495 }
496
main(int argc,char ** argv)497 int main(int argc, char** argv) {
498 android::ProcessState::self()->startThreadPool();
499 ::testing::InitGoogleTest(&argc, argv);
500 ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
501 return RUN_ALL_TESTS();
502 }
503