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