xref: /aosp_15_r20/system/media/audio_utils/tests/mel_processor_tests.cpp (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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 // #define LOG_NDEBUG 0
18 #define LOG_TAG "audio_utils_mel_processor_tests"
19 
20 #include <audio_utils/MelProcessor.h>
21 
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24 
25 #include <chrono>
26 #include <cmath>
27 #include <tuple>
28 #include <unordered_map>
29 #include <log/log.h>
30 
31 namespace android::audio_utils {
32 namespace {
33 
34 using ::testing::_;
35 using ::testing::AtMost;
36 using ::testing::Eq;
37 using ::testing::Le;
38 using ::testing::Gt;
39 using ::testing::TestWithParam;
40 using ::testing::Values;
41 using ::testing::Combine;
42 
43 // Contains the sample rate and frequency for sine wave
44 using AudioParam = std::tuple<int32_t, int32_t>;
45 
46 // Contains the frequency response in dB for an ideal A-weight filter
47 const std::unordered_map<int32_t, float> kAWeightFResponse =
48     {{80, -22.5}, {100, -19.1}, {500, -3.2}, {1000, 0}, {2000, 1.2}, {4000, 1.0},
49      {8000, -1.1}, {12000, -4.3}};
50 
51 // MEL values have a range between  0 .. 110dB(A). When comparing to the estimated
52 // attenuation of 1kHz this will result to approx. kFilterAccuracy/2 percent accuracy
53 constexpr float kFilterAccuracy = 2.f;
54 
55 class MelCallbackMock : public MelProcessor::MelCallback {
56 public:
57     MOCK_METHOD(void, onNewMelValues, (const std::vector<float>&, size_t, size_t,
58               audio_port_handle_t, bool), (const override));
59     MOCK_METHOD(void, onMomentaryExposure, (float, audio_port_handle_t), (const override));
60 };
61 
appendSineWaveBuffer(std::vector<float> & buffer,float frequency,size_t samples,int32_t sampleRate,float attenuation=1.0f)62 void appendSineWaveBuffer(std::vector<float>& buffer,
63                           float frequency,
64                           size_t samples,
65                           int32_t sampleRate,
66                           float attenuation = 1.0f) {
67     float rad = 2.0f * (float) M_PI * frequency / (float) sampleRate;
68     for (size_t j = 0; j < samples; ++j) {
69         buffer.push_back(sinf(j * rad) * attenuation);
70     }
71 }
72 
73 class MelProcessorFixtureTest : public TestWithParam<AudioParam> {
74 protected:
MelProcessorFixtureTest()75     MelProcessorFixtureTest()
76         : mSampleRate(std::get<0>(GetParam())),
77           mFrequency(std::get<1>(GetParam())),
78           mMelCallback(sp<MelCallbackMock>::make()),
79           mProcessor(sp<MelProcessor>::make(mSampleRate,
80                                             1,
81                                             AUDIO_FORMAT_PCM_FLOAT,
82                                             mMelCallback,
83                                             mDeviceId,
84                                             mDefaultRs2,
85                                             mMaxMelsCallback)) {
86               ALOGV("Starting test for freq / sr: %d / %d", mFrequency, mSampleRate);
87           }
88 
89 
90     int32_t mSampleRate;
91     int32_t mFrequency;
92     size_t mMaxMelsCallback = 2;
93     audio_port_handle_t mDeviceId = 1;
94     int32_t mDefaultRs2 = 100;
95 
96     sp<MelCallbackMock> mMelCallback;
97     sp<MelProcessor> mProcessor;
98 
99     std::vector<float> mBuffer;
100 };
101 
TEST(MelProcessorTest,UnsupportedSamplerateCheck)102 TEST(MelProcessorTest, UnsupportedSamplerateCheck) {
103     sp<MelCallbackMock> callback = sp<MelCallbackMock>::make();
104     auto processor = sp<MelProcessor>::make(1000, 1, AUDIO_FORMAT_PCM_FLOAT, callback, 1, 100);
105     std::vector<float> buffer(1000);
106 
107     EXPECT_EQ(processor->process(buffer.data(), 1000), 0);
108 }
109 
TEST_P(MelProcessorFixtureTest,CheckNumberOfCallbacks)110 TEST_P(MelProcessorFixtureTest, CheckNumberOfCallbacks) {
111     if (mFrequency != 1000.0f) {
112         ALOGV("NOTE: CheckNumberOfCallbacks disabled for frequency %d", mFrequency);
113         return;
114     }
115 
116     appendSineWaveBuffer(mBuffer, 1000.0f, mSampleRate * mMaxMelsCallback, mSampleRate);
117     appendSineWaveBuffer(mBuffer, 1000.0f, mSampleRate * mMaxMelsCallback, mSampleRate, 0.01f);
118 
119     EXPECT_CALL(*mMelCallback.get(), onMomentaryExposure(Gt(mDefaultRs2), Eq(mDeviceId)))
120         .Times(AtMost(2));
121     EXPECT_CALL(*mMelCallback.get(),
122                 onNewMelValues(_, _, Le(size_t{2}), Eq(mDeviceId), Eq(true))).Times(1);
123 
124     EXPECT_GT(mProcessor->process(mBuffer.data(), mBuffer.size() * sizeof(float)), 0);
125     mProcessor->drainAndWait();
126 }
127 
TEST_P(MelProcessorFixtureTest,CheckAWeightingFrequency)128 TEST_P(MelProcessorFixtureTest, CheckAWeightingFrequency) {
129     appendSineWaveBuffer(mBuffer, mFrequency, mSampleRate, mSampleRate);
130     appendSineWaveBuffer(mBuffer, 1000.0f, mSampleRate, mSampleRate);
131 
132     EXPECT_CALL(*mMelCallback.get(), onMomentaryExposure(Gt(mDefaultRs2), Eq(mDeviceId)))
133         .Times(AtMost(2));
134     EXPECT_CALL(*mMelCallback.get(), onNewMelValues(_, _, _, Eq(mDeviceId), Eq(true)))
135         .Times(1)
136         .WillRepeatedly([&] (const std::vector<float>& mel, size_t offset, size_t length,
137                                 audio_port_handle_t deviceId, bool attenuated) {
138             EXPECT_EQ(offset, size_t{0});
139             EXPECT_EQ(length, mMaxMelsCallback);
140             EXPECT_EQ(deviceId, mDeviceId);
141             EXPECT_EQ(true, attenuated);
142             float deltaValue = mel[0] - mel[1];
143             ALOGV("MEL[%d] = %.2f,  MEL[1000] = %.2f\n", mFrequency, mel[0], mel[1]);
144             EXPECT_TRUE(abs(deltaValue - kAWeightFResponse.at(mFrequency)) <= kFilterAccuracy)
145                 << "Freq response of " << mFrequency << " and sample rate "
146                 << mSampleRate << " compared to 1kHz is " << deltaValue
147                 << ". Should be " << kAWeightFResponse.at(mFrequency);
148         });
149 
150     EXPECT_GT(mProcessor->process(mBuffer.data(), mBuffer.size() * sizeof(float)), 0);
151     mProcessor->drainAndWait();
152 }
153 
TEST_P(MelProcessorFixtureTest,AttenuationCheck)154 TEST_P(MelProcessorFixtureTest, AttenuationCheck) {
155     auto processorAttenuation =
156         sp<MelProcessor>::make(mSampleRate, 1, AUDIO_FORMAT_PCM_FLOAT, mMelCallback, mDeviceId+1,
157                      mDefaultRs2, mMaxMelsCallback);
158     float attenuationDB = 10.f;
159     std::vector<float> bufferAttenuation;
160     float melAttenuation = 0.f;
161     float melNoAttenuation = 0.f;
162 
163     processorAttenuation->setAttenuation(attenuationDB);
164     appendSineWaveBuffer(bufferAttenuation, mFrequency, mSampleRate * mMaxMelsCallback,
165                          mSampleRate);
166     appendSineWaveBuffer(mBuffer, mFrequency, mSampleRate * mMaxMelsCallback, mSampleRate);
167 
168     EXPECT_CALL(*mMelCallback.get(), onMomentaryExposure(Gt(mDefaultRs2), _))
169         .Times(AtMost(2 * mMaxMelsCallback));
170     EXPECT_CALL(*mMelCallback.get(), onNewMelValues(_, _, _, _, Eq(true)))
171         .Times(AtMost(2))
172         .WillRepeatedly([&] (const std::vector<float>& mel, size_t offset, size_t length,
173                                 audio_port_handle_t deviceId, bool attenuated) {
174             EXPECT_EQ(offset, size_t{0});
175             EXPECT_EQ(length, mMaxMelsCallback);
176             EXPECT_EQ(true, attenuated);
177 
178             if (deviceId == mDeviceId) {
179                 melNoAttenuation = mel[0];
180             } else {
181                 melAttenuation = mel[0];
182             }
183         });
184     EXPECT_GT(mProcessor->process(mBuffer.data(),
185                                   mSampleRate * mMaxMelsCallback * sizeof(float)), 0);
186     EXPECT_GT(processorAttenuation->process(bufferAttenuation.data(),
187                                             mSampleRate * mMaxMelsCallback * sizeof(float)), 0);
188     mProcessor->drainAndWait();
189     processorAttenuation->drainAndWait();
190     // with attenuation for some frequencies the MEL callback does not exceed the RS1 threshold
191     if (melAttenuation > 0.f) {
192         EXPECT_EQ(melNoAttenuation - melAttenuation, attenuationDB);
193     }
194 }
195 
196 // A-weight filter loses precision around Nyquist frequency
197 // Splitting into multiple suites that are capable to have an accurate
198 // estimation for a-weight frequency response.
199 INSTANTIATE_TEST_SUITE_P(MelProcessorTestSuite,
200     MelProcessorFixtureTest,
201     Combine(Values(192000, 176400, 96000, 88200, 64000, 48000),
202             Values(80, 100, 500, 1000, 2000, 4000, 8000, 12000))
203 );
204 INSTANTIATE_TEST_SUITE_P(MelProcessorTestSuite2,
205     MelProcessorFixtureTest,
206     Combine(Values(44100, 32000),
207             Values(80, 100, 500, 1000, 2000, 4000, 8000))
208 );
209 INSTANTIATE_TEST_SUITE_P(MelProcessorTestSuite3,
210     MelProcessorFixtureTest,
211     Combine(Values(24000, 22050, 16000, 12000, 11025),
212             Values(80, 100, 500, 1000, 2000, 4000))
213 );
214 INSTANTIATE_TEST_SUITE_P(MelProcessorTestSuite4,
215     MelProcessorFixtureTest,
216     Combine(Values(8000),
217             Values(80, 100, 500, 1000, 2000))
218 );
219 
220 }  // namespace
221 }  // namespace android
222