1 /*
2 * Copyright (C) 2024 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_TAG "AlsaUtilsTest"
18
19 #include <alsa/Utils.h>
20 #include <android-base/macros.h>
21 #include <audio_utils/primitives.h>
22 #include <gtest/gtest.h>
23 #include <log/log.h>
24
25 extern "C" {
26 #include <tinyalsa/pcm.h>
27 }
28
29 namespace alsa = ::aidl::android::hardware::audio::core::alsa;
30
31 namespace {
32
33 const static constexpr float kInt16tTolerance = 4;
34 const static constexpr float kIntTolerance = 1;
35 const static constexpr float kFloatTolerance = 1e-4;
36 const static constexpr float kUnityGain = 1;
37 const static constexpr int32_t kInt24Min = -(1 << 23);
38 const static constexpr int32_t kInt24Max = (1 << 23) - 1;
39 const static constexpr float kFloatMin = -1;
40 const static constexpr float kFloatMax = 1;
41 const static int32_t kQ8_23Min = 0x80000000;
42 const static int32_t kQ8_23Max = 0x7FFFFFFF;
43 const static std::vector<int16_t> kInt16Buffer = {10000, 100, 0, INT16_MAX,
44 INT16_MIN, -2500, 1000, -5800};
45 const static std::vector<float> kFloatBuffer = {0.5, -0.6, kFloatMin, 0.01, kFloatMax, 0.0};
46 const static std::vector<int32_t> kInt32Buffer = {100, 0, 8000, INT32_MAX, INT32_MIN, -300};
47 const static std::vector<int32_t> kQ8_23Buffer = {
48 kQ8_23Min, kQ8_23Max, 0x00000000, 0x00000001, 0x00400000, static_cast<int32_t>(0xFFD33333)};
49 const static std::vector<int32_t> kInt24Buffer = {200, 10, -100, 0, kInt24Min, kInt24Max};
50
51 template <typename T>
CopyToBuffer(int & bytesToTransfer,std::vector<T> & destBuffer,const std::vector<T> & srcBuffer)52 void* CopyToBuffer(int& bytesToTransfer, std::vector<T>& destBuffer,
53 const std::vector<T>& srcBuffer) {
54 bytesToTransfer = srcBuffer.size() * sizeof(T);
55 destBuffer = srcBuffer;
56 return destBuffer.data();
57 }
58
59 template <typename T>
VerifyTypedBufferResults(const std::vector<T> & bufferWithGain,const std::vector<T> & srcBuffer,float gain,float tolerance)60 void VerifyTypedBufferResults(const std::vector<T>& bufferWithGain, const std::vector<T>& srcBuffer,
61 float gain, float tolerance) {
62 for (size_t i = 0; i < srcBuffer.size(); i++) {
63 EXPECT_NEAR(srcBuffer[i] * gain, static_cast<float>(bufferWithGain[i]), tolerance);
64 }
65 }
66
67 template <typename T>
VerifyTypedBufferResultsWithClamp(const std::vector<T> & bufferWithGain,const std::vector<T> & srcBuffer,float gain,float tolerance,T minValue,T maxValue)68 void VerifyTypedBufferResultsWithClamp(const std::vector<T>& bufferWithGain,
69 const std::vector<T>& srcBuffer, float gain, float tolerance,
70 T minValue, T maxValue) {
71 for (size_t i = 0; i < srcBuffer.size(); i++) {
72 float expectedResult = std::clamp(srcBuffer[i] * gain, static_cast<float>(minValue),
73 static_cast<float>(maxValue));
74 EXPECT_NEAR(expectedResult, static_cast<float>(bufferWithGain[i]), tolerance);
75 }
76 }
77
78 } // namespace
79
80 using ApplyGainTestParameters = std::tuple<pcm_format, int, float>;
81 enum { INDEX_PCM_FORMAT, INDEX_CHANNEL_COUNT, INDEX_GAIN };
82
83 class ApplyGainTest : public ::testing::TestWithParam<ApplyGainTestParameters> {
84 protected:
85 void SetUp() override;
86 void VerifyBufferResult(const pcm_format pcmFormat, const float gain);
87 void VerifyBufferResultWithClamp(const pcm_format pcmFormat, const float gain);
88
89 pcm_format mPcmFormat;
90 int mBufferSizeBytes;
91 void* mBuffer;
92
93 private:
94 std::vector<int16_t> mInt16BufferToConvert;
95 std::vector<float> mFloatBufferToConvert;
96 std::vector<int32_t> mInt32BufferToConvert;
97 std::vector<int32_t> mQ8_23BufferToConvert;
98 std::vector<int32_t> mInt24BufferToConvert;
99 };
100
SetUp()101 void ApplyGainTest::SetUp() {
102 mPcmFormat = std::get<INDEX_PCM_FORMAT>(GetParam());
103 switch (mPcmFormat) {
104 case PCM_FORMAT_S16_LE:
105 mBuffer = CopyToBuffer(mBufferSizeBytes, mInt16BufferToConvert, kInt16Buffer);
106 break;
107 case PCM_FORMAT_FLOAT_LE:
108 mBuffer = CopyToBuffer(mBufferSizeBytes, mFloatBufferToConvert, kFloatBuffer);
109 break;
110 case PCM_FORMAT_S32_LE:
111 mBuffer = CopyToBuffer(mBufferSizeBytes, mInt32BufferToConvert, kInt32Buffer);
112 break;
113 case PCM_FORMAT_S24_LE:
114 mBuffer = CopyToBuffer(mBufferSizeBytes, mQ8_23BufferToConvert, kQ8_23Buffer);
115 break;
116 case PCM_FORMAT_S24_3LE: {
117 std::vector<int32_t> original32BitBuffer(kInt24Buffer.begin(), kInt24Buffer.end());
118 for (auto& val : original32BitBuffer) {
119 val <<= 8;
120 }
121 mInt24BufferToConvert = std::vector<int32_t>(kInt24Buffer.size());
122 mBufferSizeBytes = kInt24Buffer.size() * 3 * sizeof(uint8_t);
123 memcpy_to_p24_from_i32(reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
124 original32BitBuffer.data(), kInt24Buffer.size());
125 mBuffer = mInt24BufferToConvert.data();
126 } break;
127 default:
128 FAIL() << "Unsupported pcm format: " << mPcmFormat;
129 return;
130 }
131 }
132
VerifyBufferResult(const pcm_format pcmFormat,const float gain)133 void ApplyGainTest::VerifyBufferResult(const pcm_format pcmFormat, const float gain) {
134 switch (pcmFormat) {
135 case PCM_FORMAT_S16_LE:
136 VerifyTypedBufferResults(mInt16BufferToConvert, kInt16Buffer, gain, kInt16tTolerance);
137 break;
138 case PCM_FORMAT_FLOAT_LE:
139 VerifyTypedBufferResults(mFloatBufferToConvert, kFloatBuffer, gain, kFloatTolerance);
140 break;
141 case PCM_FORMAT_S32_LE:
142 VerifyTypedBufferResults(mInt32BufferToConvert, kInt32Buffer, gain, kIntTolerance);
143 break;
144 case PCM_FORMAT_S24_LE: {
145 for (size_t i = 0; i < kQ8_23Buffer.size(); i++) {
146 EXPECT_NEAR(float_from_q8_23(kQ8_23Buffer[i]) * gain,
147 static_cast<float>(float_from_q8_23(mQ8_23BufferToConvert[i])),
148 kFloatTolerance);
149 }
150 } break;
151 case PCM_FORMAT_S24_3LE: {
152 size_t bufferSize = kInt24Buffer.size();
153 std::vector<int32_t> result32BitBuffer(bufferSize);
154 memcpy_to_i32_from_p24(result32BitBuffer.data(),
155 reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
156 bufferSize);
157 for (size_t i = 0; i < bufferSize; i++) {
158 EXPECT_NEAR(kInt24Buffer[i] * gain, result32BitBuffer[i] >> 8, kIntTolerance);
159 }
160 } break;
161 default:
162 return;
163 }
164 }
165
VerifyBufferResultWithClamp(const pcm_format pcmFormat,const float gain)166 void ApplyGainTest::VerifyBufferResultWithClamp(const pcm_format pcmFormat, const float gain) {
167 switch (pcmFormat) {
168 case PCM_FORMAT_S16_LE:
169 VerifyTypedBufferResultsWithClamp(mInt16BufferToConvert, kInt16Buffer, gain,
170 kInt16tTolerance, static_cast<int16_t>(INT16_MIN),
171 static_cast<int16_t>(INT16_MAX));
172 break;
173 case PCM_FORMAT_FLOAT_LE:
174 VerifyTypedBufferResultsWithClamp(mFloatBufferToConvert, kFloatBuffer, gain,
175 kFloatTolerance, kFloatMin, kFloatMax);
176 break;
177 case PCM_FORMAT_S32_LE:
178 VerifyTypedBufferResultsWithClamp(mInt32BufferToConvert, kInt32Buffer, gain,
179 kIntTolerance, INT32_MIN, INT32_MAX);
180 break;
181 case PCM_FORMAT_S24_LE: {
182 for (size_t i = 0; i < kQ8_23Buffer.size(); i++) {
183 float expectedResult =
184 std::clamp(float_from_q8_23(kQ8_23Buffer[i]) * gain,
185 float_from_q8_23(kQ8_23Min), float_from_q8_23(kQ8_23Max));
186 EXPECT_NEAR(expectedResult,
187 static_cast<float>(float_from_q8_23(mQ8_23BufferToConvert[i])),
188 kFloatTolerance);
189 }
190 } break;
191 case PCM_FORMAT_S24_3LE: {
192 size_t bufferSize = kInt24Buffer.size();
193 std::vector<int32_t> result32BitBuffer(bufferSize);
194 memcpy_to_i32_from_p24(result32BitBuffer.data(),
195 reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
196 bufferSize);
197 for (size_t i = 0; i < bufferSize; i++) {
198 result32BitBuffer[i] >>= 8;
199 }
200 VerifyTypedBufferResultsWithClamp(result32BitBuffer, kInt24Buffer, gain, kIntTolerance,
201 kInt24Min, kInt24Max);
202 } break;
203 default:
204 return;
205 }
206 }
207
TEST_P(ApplyGainTest,ApplyGain)208 TEST_P(ApplyGainTest, ApplyGain) {
209 float gain = std::get<INDEX_GAIN>(GetParam());
210 int channelCount = std::get<INDEX_CHANNEL_COUNT>(GetParam());
211
212 alsa::applyGain(mBuffer, gain, mBufferSizeBytes, mPcmFormat, channelCount);
213
214 if (gain <= kUnityGain) {
215 VerifyBufferResult(mPcmFormat, gain);
216 } else {
217 VerifyBufferResultWithClamp(mPcmFormat, gain);
218 }
219 }
220
GetApplyGainTestName(const testing::TestParamInfo<ApplyGainTestParameters> & info)221 std::string GetApplyGainTestName(const testing::TestParamInfo<ApplyGainTestParameters>& info) {
222 std::string testNameStr;
223 switch (std::get<INDEX_PCM_FORMAT>(info.param)) {
224 case PCM_FORMAT_S16_LE:
225 testNameStr = "S16_LE";
226 break;
227 case PCM_FORMAT_FLOAT_LE:
228 testNameStr = "Float_LE";
229 break;
230 case PCM_FORMAT_S32_LE:
231 testNameStr = "S32_LE";
232 break;
233 case PCM_FORMAT_S24_LE:
234 testNameStr = "S24_LE";
235 break;
236 case PCM_FORMAT_S24_3LE:
237 testNameStr = "S24_3LE";
238 break;
239 default:
240 testNameStr = "UnsupportedPcmFormat";
241 break;
242 }
243 testNameStr += std::get<INDEX_CHANNEL_COUNT>(info.param) == 1 ? "_Mono_" : "_Stereo_";
244 testNameStr += std::get<INDEX_GAIN>(info.param) <= kUnityGain ? "WithoutClamp" : "WithClamp";
245 return testNameStr;
246 }
247
248 INSTANTIATE_TEST_SUITE_P(PerPcmFormat, ApplyGainTest,
249 testing::Combine(testing::Values(PCM_FORMAT_S16_LE, PCM_FORMAT_FLOAT_LE,
250 PCM_FORMAT_S32_LE, PCM_FORMAT_S24_LE,
251 PCM_FORMAT_S24_3LE),
252 testing::Values(1, 2), testing::Values(0.6f, 1.5f)),
253 GetApplyGainTestName);
254