254 lines
10 KiB
C++
254 lines
10 KiB
C++
/*
|
|
* 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);
|