blob: 226eea02889cf8dcbc5c0dce81c1c23d97d9931d [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "AlsaUtilsTest"
#include <alsa/Utils.h>
#include <android-base/macros.h>
#include <audio_utils/primitives.h>
#include <gtest/gtest.h>
#include <log/log.h>
extern "C" {
#include <tinyalsa/pcm.h>
}
namespace alsa = ::aidl::android::hardware::audio::core::alsa;
namespace {
const static constexpr float kInt16tTolerance = 4;
const static constexpr float kIntTolerance = 1;
const static constexpr float kFloatTolerance = 1e-4;
const static constexpr float kUnityGain = 1;
const static constexpr int32_t kInt24Min = -(1 << 23);
const static constexpr int32_t kInt24Max = (1 << 23) - 1;
const static constexpr float kFloatMin = -1;
const static constexpr float kFloatMax = 1;
const static int32_t kQ8_23Min = 0x80000000;
const static int32_t kQ8_23Max = 0x7FFFFFFF;
const static std::vector<int16_t> kInt16Buffer = {10000, 100, 0, INT16_MAX,
INT16_MIN, -2500, 1000, -5800};
const static std::vector<float> kFloatBuffer = {0.5, -0.6, kFloatMin, 0.01, kFloatMax, 0.0};
const static std::vector<int32_t> kInt32Buffer = {100, 0, 8000, INT32_MAX, INT32_MIN, -300};
const static std::vector<int32_t> kQ8_23Buffer = {
kQ8_23Min, kQ8_23Max, 0x00000000, 0x00000001, 0x00400000, static_cast<int32_t>(0xFFD33333)};
const static std::vector<int32_t> kInt24Buffer = {200, 10, -100, 0, kInt24Min, kInt24Max};
template <typename T>
void* CopyToBuffer(int& bytesToTransfer, std::vector<T>& destBuffer,
const std::vector<T>& srcBuffer) {
bytesToTransfer = srcBuffer.size() * sizeof(T);
destBuffer = srcBuffer;
return destBuffer.data();
}
template <typename T>
void VerifyTypedBufferResults(const std::vector<T>& bufferWithGain, const std::vector<T>& srcBuffer,
float gain, float tolerance) {
for (size_t i = 0; i < srcBuffer.size(); i++) {
EXPECT_NEAR(srcBuffer[i] * gain, static_cast<float>(bufferWithGain[i]), tolerance);
}
}
template <typename T>
void VerifyTypedBufferResultsWithClamp(const std::vector<T>& bufferWithGain,
const std::vector<T>& srcBuffer, float gain, float tolerance,
T minValue, T maxValue) {
for (size_t i = 0; i < srcBuffer.size(); i++) {
float expectedResult = std::clamp(srcBuffer[i] * gain, static_cast<float>(minValue),
static_cast<float>(maxValue));
EXPECT_NEAR(expectedResult, static_cast<float>(bufferWithGain[i]), tolerance);
}
}
} // namespace
using ApplyGainTestParameters = std::tuple<pcm_format, int, float>;
enum { INDEX_PCM_FORMAT, INDEX_CHANNEL_COUNT, INDEX_GAIN };
class ApplyGainTest : public ::testing::TestWithParam<ApplyGainTestParameters> {
protected:
void SetUp() override;
void VerifyBufferResult(const pcm_format pcmFormat, const float gain);
void VerifyBufferResultWithClamp(const pcm_format pcmFormat, const float gain);
pcm_format mPcmFormat;
int mBufferSizeBytes;
void* mBuffer;
private:
std::vector<int16_t> mInt16BufferToConvert;
std::vector<float> mFloatBufferToConvert;
std::vector<int32_t> mInt32BufferToConvert;
std::vector<int32_t> mQ8_23BufferToConvert;
std::vector<int32_t> mInt24BufferToConvert;
};
void ApplyGainTest::SetUp() {
mPcmFormat = std::get<INDEX_PCM_FORMAT>(GetParam());
switch (mPcmFormat) {
case PCM_FORMAT_S16_LE:
mBuffer = CopyToBuffer(mBufferSizeBytes, mInt16BufferToConvert, kInt16Buffer);
break;
case PCM_FORMAT_FLOAT_LE:
mBuffer = CopyToBuffer(mBufferSizeBytes, mFloatBufferToConvert, kFloatBuffer);
break;
case PCM_FORMAT_S32_LE:
mBuffer = CopyToBuffer(mBufferSizeBytes, mInt32BufferToConvert, kInt32Buffer);
break;
case PCM_FORMAT_S24_LE:
mBuffer = CopyToBuffer(mBufferSizeBytes, mQ8_23BufferToConvert, kQ8_23Buffer);
break;
case PCM_FORMAT_S24_3LE: {
std::vector<int32_t> original32BitBuffer(kInt24Buffer.begin(), kInt24Buffer.end());
for (auto& val : original32BitBuffer) {
val <<= 8;
}
mInt24BufferToConvert = std::vector<int32_t>(kInt24Buffer.size());
mBufferSizeBytes = kInt24Buffer.size() * 3 * sizeof(uint8_t);
memcpy_to_p24_from_i32(reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
original32BitBuffer.data(), kInt24Buffer.size());
mBuffer = mInt24BufferToConvert.data();
} break;
default:
FAIL() << "Unsupported pcm format: " << mPcmFormat;
return;
}
}
void ApplyGainTest::VerifyBufferResult(const pcm_format pcmFormat, const float gain) {
switch (pcmFormat) {
case PCM_FORMAT_S16_LE:
VerifyTypedBufferResults(mInt16BufferToConvert, kInt16Buffer, gain, kInt16tTolerance);
break;
case PCM_FORMAT_FLOAT_LE:
VerifyTypedBufferResults(mFloatBufferToConvert, kFloatBuffer, gain, kFloatTolerance);
break;
case PCM_FORMAT_S32_LE:
VerifyTypedBufferResults(mInt32BufferToConvert, kInt32Buffer, gain, kIntTolerance);
break;
case PCM_FORMAT_S24_LE: {
for (size_t i = 0; i < kQ8_23Buffer.size(); i++) {
EXPECT_NEAR(float_from_q8_23(kQ8_23Buffer[i]) * gain,
static_cast<float>(float_from_q8_23(mQ8_23BufferToConvert[i])),
kFloatTolerance);
}
} break;
case PCM_FORMAT_S24_3LE: {
size_t bufferSize = kInt24Buffer.size();
std::vector<int32_t> result32BitBuffer(bufferSize);
memcpy_to_i32_from_p24(result32BitBuffer.data(),
reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
bufferSize);
for (size_t i = 0; i < bufferSize; i++) {
EXPECT_NEAR(kInt24Buffer[i] * gain, result32BitBuffer[i] >> 8, kIntTolerance);
}
} break;
default:
return;
}
}
void ApplyGainTest::VerifyBufferResultWithClamp(const pcm_format pcmFormat, const float gain) {
switch (pcmFormat) {
case PCM_FORMAT_S16_LE:
VerifyTypedBufferResultsWithClamp(mInt16BufferToConvert, kInt16Buffer, gain,
kInt16tTolerance, static_cast<int16_t>(INT16_MIN),
static_cast<int16_t>(INT16_MAX));
break;
case PCM_FORMAT_FLOAT_LE:
VerifyTypedBufferResultsWithClamp(mFloatBufferToConvert, kFloatBuffer, gain,
kFloatTolerance, kFloatMin, kFloatMax);
break;
case PCM_FORMAT_S32_LE:
VerifyTypedBufferResultsWithClamp(mInt32BufferToConvert, kInt32Buffer, gain,
kIntTolerance, INT32_MIN, INT32_MAX);
break;
case PCM_FORMAT_S24_LE: {
for (size_t i = 0; i < kQ8_23Buffer.size(); i++) {
float expectedResult =
std::clamp(float_from_q8_23(kQ8_23Buffer[i]) * gain,
float_from_q8_23(kQ8_23Min), float_from_q8_23(kQ8_23Max));
EXPECT_NEAR(expectedResult,
static_cast<float>(float_from_q8_23(mQ8_23BufferToConvert[i])),
kFloatTolerance);
}
} break;
case PCM_FORMAT_S24_3LE: {
size_t bufferSize = kInt24Buffer.size();
std::vector<int32_t> result32BitBuffer(bufferSize);
memcpy_to_i32_from_p24(result32BitBuffer.data(),
reinterpret_cast<uint8_t*>(mInt24BufferToConvert.data()),
bufferSize);
for (size_t i = 0; i < bufferSize; i++) {
result32BitBuffer[i] >>= 8;
}
VerifyTypedBufferResultsWithClamp(result32BitBuffer, kInt24Buffer, gain, kIntTolerance,
kInt24Min, kInt24Max);
} break;
default:
return;
}
}
TEST_P(ApplyGainTest, ApplyGain) {
float gain = std::get<INDEX_GAIN>(GetParam());
int channelCount = std::get<INDEX_CHANNEL_COUNT>(GetParam());
alsa::applyGain(mBuffer, gain, mBufferSizeBytes, mPcmFormat, channelCount);
if (gain <= kUnityGain) {
VerifyBufferResult(mPcmFormat, gain);
} else {
VerifyBufferResultWithClamp(mPcmFormat, gain);
}
}
std::string GetApplyGainTestName(const testing::TestParamInfo<ApplyGainTestParameters>& info) {
std::string testNameStr;
switch (std::get<INDEX_PCM_FORMAT>(info.param)) {
case PCM_FORMAT_S16_LE:
testNameStr = "S16_LE";
break;
case PCM_FORMAT_FLOAT_LE:
testNameStr = "Float_LE";
break;
case PCM_FORMAT_S32_LE:
testNameStr = "S32_LE";
break;
case PCM_FORMAT_S24_LE:
testNameStr = "S24_LE";
break;
case PCM_FORMAT_S24_3LE:
testNameStr = "S24_3LE";
break;
default:
testNameStr = "UnsupportedPcmFormat";
break;
}
testNameStr += std::get<INDEX_CHANNEL_COUNT>(info.param) == 1 ? "_Mono_" : "_Stereo_";
testNameStr += std::get<INDEX_GAIN>(info.param) <= kUnityGain ? "WithoutClamp" : "WithClamp";
return testNameStr;
}
INSTANTIATE_TEST_SUITE_P(PerPcmFormat, ApplyGainTest,
testing::Combine(testing::Values(PCM_FORMAT_S16_LE, PCM_FORMAT_FLOAT_LE,
PCM_FORMAT_S32_LE, PCM_FORMAT_S24_LE,
PCM_FORMAT_S24_3LE),
testing::Values(1, 2), testing::Values(0.6f, 1.5f)),
GetApplyGainTestName);