Weilin Xu | 550a244 | 2024-09-19 20:33:36 -0700 | [diff] [blame] | 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> |
| 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> |
| 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> |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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); |