Loading audio/aidl/default/Android.bp +41 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,47 @@ cc_test { test_suites: ["general-tests"], } cc_test { name: "audio_alsa_utils_tests", vendor_available: true, defaults: [ "latest_android_media_audio_common_types_ndk_static", "latest_android_hardware_audio_core_ndk_static", ], static_libs: [ "libalsautilsv2", "libtinyalsav2", ], shared_libs: [ "libaudio_aidl_conversion_common_ndk", "libaudioaidlcommon", "libaudioutils", "libbase", "libbinder_ndk", "libcutils", "libfmq", "libmedia_helper", "libstagefright_foundation", "libutils", ], header_libs: [ "libaudio_system_headers", "libaudioaidl_headers", ], srcs: [ "alsa/Utils.cpp", "tests/AlsaUtilsTest.cpp", ], cflags: [ "-Wall", "-Wextra", "-Werror", "-Wthread-safety", "-DBACKEND_NDK", ], test_suites: ["general-tests"], } cc_defaults { name: "aidlaudioeffectservice_defaults", defaults: [ Loading audio/aidl/default/alsa/Utils.cpp +114 −46 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ using aidl::android::media::audio::common::PcmType; namespace aidl::android::hardware::audio::core::alsa { const float kUnityGainFloat = 1.0f; DeviceProxy::DeviceProxy() : mProfile(nullptr), mProxy(nullptr, alsaProxyDeleter) {} DeviceProxy::DeviceProxy(const DeviceProfile& deviceProfile) Loading Loading @@ -140,7 +142,6 @@ AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() { static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = { {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8}, {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE}, {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE}, {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE}, Loading @@ -165,6 +166,92 @@ const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() { return pcmFormatToFormatDescMap; } void applyGainToInt16Buffer(void* buffer, const size_t bufferSizeBytes, const float gain, int channelCount) { const uint16_t unityGainQ4_12 = u4_12_from_float(kUnityGainFloat); const uint16_t vl = u4_12_from_float(gain); const uint32_t vrl = (vl << 16) | vl; int numFrames = 0; if (channelCount == 2) { numFrames = bufferSizeBytes / sizeof(uint32_t); if (numFrames == 0) { return; } uint32_t* intBuffer = (uint32_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; l = clamp16(l); r = clamp16(r); *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } else { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } } else { numFrames = bufferSizeBytes / sizeof(uint16_t); if (numFrames == 0) { return; } int16_t* intBuffer = (int16_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { do { int32_t mono = mul(*intBuffer, static_cast<int16_t>(vl)) >> 12; *intBuffer++ = clamp16(mono); } while (--numFrames); } else { do { int32_t mono = mul(*intBuffer, static_cast<int16_t>(vl)) >> 12; *intBuffer++ = static_cast<int16_t>(mono & 0xFFFF); } while (--numFrames); } } } void applyGainToInt32Buffer(int32_t* typedBuffer, const size_t bufferSizeBytes, const float gain) { int numSamples = bufferSizeBytes / sizeof(int32_t); if (numSamples == 0) { return; } if (CC_UNLIKELY(gain > kUnityGainFloat)) { do { float multiplied = (*typedBuffer) * gain; if (multiplied > INT32_MAX) { *typedBuffer++ = INT32_MAX; } else if (multiplied < INT32_MIN) { *typedBuffer++ = INT32_MIN; } else { *typedBuffer++ = multiplied; } } while (--numSamples); } else { do { *typedBuffer++ = (*typedBuffer) * gain; } while (--numSamples); } } void applyGainToFloatBuffer(float* floatBuffer, const size_t bufferSizeBytes, const float gain) { int numSamples = bufferSizeBytes / sizeof(float); if (numSamples == 0) { return; } if (CC_UNLIKELY(gain > kUnityGainFloat)) { do { *floatBuffer++ = std::clamp((*floatBuffer) * gain, -kUnityGainFloat, kUnityGainFloat); } while (--numSamples); } else { do { *floatBuffer++ = (*floatBuffer) * gain; } while (--numSamples); } } } // namespace std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) { Loading Loading @@ -345,7 +432,7 @@ pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID); } void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat, void applyGain(void* buffer, float gain, size_t bufferSizeBytes, enum pcm_format pcmFormat, int channelCount) { if (channelCount != 1 && channelCount != 2) { LOG(WARNING) << __func__ << ": unsupported channel count " << channelCount; Loading @@ -355,56 +442,37 @@ void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format LOG(WARNING) << __func__ << ": unsupported pcm format " << pcmFormat; return; } const float unityGainFloat = 1.0f; if (std::abs(gain - unityGainFloat) < 1e-6) { if (std::abs(gain - kUnityGainFloat) < 1e-6) { return; } int numFrames; switch (pcmFormat) { case PCM_FORMAT_S16_LE: { const uint16_t unityGainQ4_12 = u4_12_from_float(unityGainFloat); const uint16_t vl = u4_12_from_float(gain); const uint32_t vrl = (vl << 16) | vl; if (channelCount == 2) { numFrames = bytesToTransfer / sizeof(uint32_t); uint32_t* intBuffer = (uint32_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { // volume is boosted, so we might need to clamp even though // we process only one track. do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; l = clamp16(l); r = clamp16(r); *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } else { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } } else { numFrames = bytesToTransfer / sizeof(uint16_t); int16_t* intBuffer = (int16_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { // volume is boosted, so we might need to clamp even though // we process only one track. do { int32_t mono = mulRL(1, *intBuffer, vrl) >> 12; *intBuffer++ = clamp16(mono); } while (--numFrames); } else { do { int32_t mono = mulRL(1, *intBuffer, vrl) >> 12; *intBuffer++ = static_cast<int16_t>(mono & 0xFFFF); } while (--numFrames); } case PCM_FORMAT_S16_LE: applyGainToInt16Buffer(buffer, bufferSizeBytes, gain, channelCount); break; case PCM_FORMAT_FLOAT_LE: { float* floatBuffer = (float*)buffer; applyGainToFloatBuffer(floatBuffer, bufferSizeBytes, gain); } break; case PCM_FORMAT_S24_LE: // PCM_FORMAT_S24_LE buffer is composed of signed fixed-point 32-bit Q8.23 data with // min and max limits of the same bit representation as min and max limits of // PCM_FORMAT_S32_LE buffer. case PCM_FORMAT_S32_LE: { int32_t* typedBuffer = (int32_t*)buffer; applyGainToInt32Buffer(typedBuffer, bufferSizeBytes, gain); } break; case PCM_FORMAT_S24_3LE: { int numSamples = bufferSizeBytes / (sizeof(uint8_t) * 3); if (numSamples == 0) { return; } std::unique_ptr<int32_t[]> typedBuffer(new int32_t[numSamples]); memcpy_to_i32_from_p24(typedBuffer.get(), (uint8_t*)buffer, numSamples); applyGainToInt32Buffer(typedBuffer.get(), numSamples * sizeof(int32_t), gain); memcpy_to_p24_from_i32((uint8_t*)buffer, typedBuffer.get(), numSamples); } break; default: // TODO(336370745): Implement gain for other supported formats LOG(FATAL) << __func__ << ": unsupported pcm format " << pcmFormat; break; } } Loading audio/aidl/default/tests/AlsaUtilsTest.cpp 0 → 100644 +253 −0 Original line number Diff line number Diff line /* * 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); Loading
audio/aidl/default/Android.bp +41 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,47 @@ cc_test { test_suites: ["general-tests"], } cc_test { name: "audio_alsa_utils_tests", vendor_available: true, defaults: [ "latest_android_media_audio_common_types_ndk_static", "latest_android_hardware_audio_core_ndk_static", ], static_libs: [ "libalsautilsv2", "libtinyalsav2", ], shared_libs: [ "libaudio_aidl_conversion_common_ndk", "libaudioaidlcommon", "libaudioutils", "libbase", "libbinder_ndk", "libcutils", "libfmq", "libmedia_helper", "libstagefright_foundation", "libutils", ], header_libs: [ "libaudio_system_headers", "libaudioaidl_headers", ], srcs: [ "alsa/Utils.cpp", "tests/AlsaUtilsTest.cpp", ], cflags: [ "-Wall", "-Wextra", "-Werror", "-Wthread-safety", "-DBACKEND_NDK", ], test_suites: ["general-tests"], } cc_defaults { name: "aidlaudioeffectservice_defaults", defaults: [ Loading
audio/aidl/default/alsa/Utils.cpp +114 −46 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ using aidl::android::media::audio::common::PcmType; namespace aidl::android::hardware::audio::core::alsa { const float kUnityGainFloat = 1.0f; DeviceProxy::DeviceProxy() : mProfile(nullptr), mProxy(nullptr, alsaProxyDeleter) {} DeviceProxy::DeviceProxy(const DeviceProfile& deviceProfile) Loading Loading @@ -140,7 +142,6 @@ AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() { static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = { {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8}, {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE}, {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE}, {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE}, Loading @@ -165,6 +166,92 @@ const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() { return pcmFormatToFormatDescMap; } void applyGainToInt16Buffer(void* buffer, const size_t bufferSizeBytes, const float gain, int channelCount) { const uint16_t unityGainQ4_12 = u4_12_from_float(kUnityGainFloat); const uint16_t vl = u4_12_from_float(gain); const uint32_t vrl = (vl << 16) | vl; int numFrames = 0; if (channelCount == 2) { numFrames = bufferSizeBytes / sizeof(uint32_t); if (numFrames == 0) { return; } uint32_t* intBuffer = (uint32_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; l = clamp16(l); r = clamp16(r); *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } else { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } } else { numFrames = bufferSizeBytes / sizeof(uint16_t); if (numFrames == 0) { return; } int16_t* intBuffer = (int16_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { do { int32_t mono = mul(*intBuffer, static_cast<int16_t>(vl)) >> 12; *intBuffer++ = clamp16(mono); } while (--numFrames); } else { do { int32_t mono = mul(*intBuffer, static_cast<int16_t>(vl)) >> 12; *intBuffer++ = static_cast<int16_t>(mono & 0xFFFF); } while (--numFrames); } } } void applyGainToInt32Buffer(int32_t* typedBuffer, const size_t bufferSizeBytes, const float gain) { int numSamples = bufferSizeBytes / sizeof(int32_t); if (numSamples == 0) { return; } if (CC_UNLIKELY(gain > kUnityGainFloat)) { do { float multiplied = (*typedBuffer) * gain; if (multiplied > INT32_MAX) { *typedBuffer++ = INT32_MAX; } else if (multiplied < INT32_MIN) { *typedBuffer++ = INT32_MIN; } else { *typedBuffer++ = multiplied; } } while (--numSamples); } else { do { *typedBuffer++ = (*typedBuffer) * gain; } while (--numSamples); } } void applyGainToFloatBuffer(float* floatBuffer, const size_t bufferSizeBytes, const float gain) { int numSamples = bufferSizeBytes / sizeof(float); if (numSamples == 0) { return; } if (CC_UNLIKELY(gain > kUnityGainFloat)) { do { *floatBuffer++ = std::clamp((*floatBuffer) * gain, -kUnityGainFloat, kUnityGainFloat); } while (--numSamples); } else { do { *floatBuffer++ = (*floatBuffer) * gain; } while (--numSamples); } } } // namespace std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) { Loading Loading @@ -345,7 +432,7 @@ pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID); } void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat, void applyGain(void* buffer, float gain, size_t bufferSizeBytes, enum pcm_format pcmFormat, int channelCount) { if (channelCount != 1 && channelCount != 2) { LOG(WARNING) << __func__ << ": unsupported channel count " << channelCount; Loading @@ -355,56 +442,37 @@ void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format LOG(WARNING) << __func__ << ": unsupported pcm format " << pcmFormat; return; } const float unityGainFloat = 1.0f; if (std::abs(gain - unityGainFloat) < 1e-6) { if (std::abs(gain - kUnityGainFloat) < 1e-6) { return; } int numFrames; switch (pcmFormat) { case PCM_FORMAT_S16_LE: { const uint16_t unityGainQ4_12 = u4_12_from_float(unityGainFloat); const uint16_t vl = u4_12_from_float(gain); const uint32_t vrl = (vl << 16) | vl; if (channelCount == 2) { numFrames = bytesToTransfer / sizeof(uint32_t); uint32_t* intBuffer = (uint32_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { // volume is boosted, so we might need to clamp even though // we process only one track. do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; l = clamp16(l); r = clamp16(r); *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } else { do { int32_t l = mulRL(1, *intBuffer, vrl) >> 12; int32_t r = mulRL(0, *intBuffer, vrl) >> 12; *intBuffer++ = (r << 16) | (l & 0xFFFF); } while (--numFrames); } } else { numFrames = bytesToTransfer / sizeof(uint16_t); int16_t* intBuffer = (int16_t*)buffer; if (CC_UNLIKELY(vl > unityGainQ4_12)) { // volume is boosted, so we might need to clamp even though // we process only one track. do { int32_t mono = mulRL(1, *intBuffer, vrl) >> 12; *intBuffer++ = clamp16(mono); } while (--numFrames); } else { do { int32_t mono = mulRL(1, *intBuffer, vrl) >> 12; *intBuffer++ = static_cast<int16_t>(mono & 0xFFFF); } while (--numFrames); } case PCM_FORMAT_S16_LE: applyGainToInt16Buffer(buffer, bufferSizeBytes, gain, channelCount); break; case PCM_FORMAT_FLOAT_LE: { float* floatBuffer = (float*)buffer; applyGainToFloatBuffer(floatBuffer, bufferSizeBytes, gain); } break; case PCM_FORMAT_S24_LE: // PCM_FORMAT_S24_LE buffer is composed of signed fixed-point 32-bit Q8.23 data with // min and max limits of the same bit representation as min and max limits of // PCM_FORMAT_S32_LE buffer. case PCM_FORMAT_S32_LE: { int32_t* typedBuffer = (int32_t*)buffer; applyGainToInt32Buffer(typedBuffer, bufferSizeBytes, gain); } break; case PCM_FORMAT_S24_3LE: { int numSamples = bufferSizeBytes / (sizeof(uint8_t) * 3); if (numSamples == 0) { return; } std::unique_ptr<int32_t[]> typedBuffer(new int32_t[numSamples]); memcpy_to_i32_from_p24(typedBuffer.get(), (uint8_t*)buffer, numSamples); applyGainToInt32Buffer(typedBuffer.get(), numSamples * sizeof(int32_t), gain); memcpy_to_p24_from_i32((uint8_t*)buffer, typedBuffer.get(), numSamples); } break; default: // TODO(336370745): Implement gain for other supported formats LOG(FATAL) << __func__ << ": unsupported pcm format " << pcmFormat; break; } } Loading
audio/aidl/default/tests/AlsaUtilsTest.cpp 0 → 100644 +253 −0 Original line number Diff line number Diff line /* * 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);