Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd4b6d84 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement gain for PCM types in default audio HAL" into main

parents 2c7eec74 550a244d
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -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: [
+114 −46
Original line number Diff line number Diff line
@@ -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)
@@ -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},
@@ -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) {
@@ -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;
@@ -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;
    }
}
+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);