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

Commit 0a11b4f0 authored by Vlad Popa's avatar Vlad Popa Committed by Android (Google) Code Review
Browse files

Merge "CSD: Attenuate the HAL Mel values" into main

parents f59e5ce5 8ce28dae
Loading
Loading
Loading
Loading
+81 −11
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@
#include "SoundDoseManager.h"

#include "android/media/SoundDoseRecord.h"
#include <algorithm>
#include <android-base/stringprintf.h>
#include <media/AidlConversionCppNdk.h>
#include <cinttypes>
#include <ctime>
#include <functional>
#include <media/AidlConversionCppNdk.h>
#include <utils/Log.h>

namespace android {
@@ -46,6 +48,8 @@ int64_t getMonotonicSecond() {
    return now_ts.tv_sec;
}

constexpr float kDefaultRs2LowerBound = 80.f;  // dBA

}  // namespace

sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice(
@@ -187,6 +191,21 @@ void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) {
    }
}

float SoundDoseManager::getAttenuationForDeviceId(audio_port_handle_t id) const {
    float attenuation = 0.f;

    const std::lock_guard _l(mLock);
    const auto deviceTypeIt = mActiveDeviceTypes.find(id);
    if (deviceTypeIt != mActiveDeviceTypes.end()) {
        auto attenuationIt = mMelAttenuationDB.find(deviceTypeIt->second);
        if (attenuationIt != mMelAttenuationDB.end()) {
            attenuation = attenuationIt->second;
        }
    }

    return attenuation;
}

audio_port_handle_t SoundDoseManager::getIdForAudioDevice(const AudioDevice& audioDevice) const {
    if (isComputeCsdForcedOnAllDevices()) {
        // If CSD is forced on all devices return random port id. Used only in testing.
@@ -260,7 +279,11 @@ ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onMomentaryExposureWa
                in_audioDevice.address.toString().c_str());
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    soundDoseManager->onMomentaryExposure(in_currentDbA, id);

    float attenuation = soundDoseManager->getAttenuationForDeviceId(id);
    ALOGV("%s: attenuating received momentary exposure with %f dB", __func__, attenuation);
    // TODO: remove attenuation when enforcing HAL MELs to always be attenuated
    soundDoseManager->onMomentaryExposure(in_currentDbA + attenuation, id);

    return ndk::ScopedAStatus::ok();
}
@@ -289,9 +312,10 @@ ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onNewMelValues(
                in_audioDevice.address.toString().c_str());
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    // TODO: introduce timestamp in onNewMelValues callback
    soundDoseManager->onNewMelValues(in_melRecord.melValues, 0,
                                     in_melRecord.melValues.size(), id);
    soundDoseManager->onNewMelValues(in_melRecord.melValues, 0, in_melRecord.melValues.size(),
                                     id, /*attenuated=*/false);

    return ndk::ScopedAStatus::ok();
}
@@ -601,26 +625,68 @@ void SoundDoseManager::resetCsd(float currentCsd,
}

void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
                                      audio_port_handle_t deviceId) const {
                                      audio_port_handle_t deviceId, bool attenuated) const {
    ALOGV("%s", __func__);


    sp<media::ISoundDoseCallback> soundDoseCallback;
    std::vector<audio_utils::CsdRecord> records;
    float currentCsd;

    // TODO: delete this case when enforcing HAL MELs to always be attenuated
    float attenuation = attenuated ? 0.0f : getAttenuationForDeviceId(deviceId);

    {
        const std::lock_guard _l(mLock);
        if (!mEnabledCsd) {
            return;
        }


        const int64_t timestampSec = getMonotonicSecond();

        // only for internal callbacks
        if (attenuated) {
            records = mMelAggregator->aggregateAndAddNewMelRecord(audio_utils::MelRecord(
                deviceId, std::vector<float>(mels.begin() + offset, mels.begin() + offset + length),
                    deviceId,
                    std::vector<float>(mels.begin() + offset, mels.begin() + offset + length),
                    timestampSec - length));
        } else {
            ALOGV("%s: attenuating received values with %f dB", __func__, attenuation);

            // Extracting all intervals that contain values >= RS2 low limit (80dBA) after the
            // attenuation is applied
            size_t start = offset;
            size_t stop = offset;
            for (; stop < mels.size() && stop < offset + length; ++stop) {
                if (mels[stop] + attenuation < kDefaultRs2LowerBound) {
                    if (start < stop) {
                        std::vector<float> attMel(stop-start, attenuation);
                        // attMel[i] = mels[i] + attenuation, i in [start, stop)
                        std::transform(mels.begin() + start, mels.begin() + stop, attMel.begin(),
                                       attMel.begin(), std::plus<float>());
                        std::vector<audio_utils::CsdRecord> newRec =
                                mMelAggregator->aggregateAndAddNewMelRecord(
                                        audio_utils::MelRecord(deviceId,
                                                               attMel,
                                                               timestampSec - length + start -
                                                               offset));
                        std::copy(newRec.begin(), newRec.end(), std::back_inserter(records));
                    }
                    start = stop+1;
                }
            }
            if (start < stop) {
                std::vector<float> attMel(stop-start, attenuation);
                // attMel[i] = mels[i] + attenuation, i in [start, stop)
                std::transform(mels.begin() + start, mels.begin() + stop, attMel.begin(),
                               attMel.begin(), std::plus<float>());
                std::vector<audio_utils::CsdRecord> newRec =
                        mMelAggregator->aggregateAndAddNewMelRecord(
                                audio_utils::MelRecord(deviceId,
                                                       attMel,
                                                       timestampSec - length + start -
                                                       offset));
                std::copy(newRec.begin(), newRec.end(), std::back_inserter(records));
            }
        }

        currentCsd = mMelAggregator->getCsd();
    }
@@ -655,6 +721,10 @@ void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t
        if (!mEnabledCsd) {
            return;
        }

        if (currentMel < mRs2UpperBound) {
            return;
        }
    }

    auto soundDoseCallback = getSoundDoseCallback();
+10 −1
Original line number Diff line number Diff line
@@ -53,6 +53,13 @@ public:
          mMelAggregator(sp<audio_utils::MelAggregator>::make(kCsdWindowSeconds)),
          mRs2UpperBound(kDefaultRs2UpperBound) {};

    // Used only for testing
    SoundDoseManager(const sp<IMelReporterCallback>& melReporterCallback,
                     const sp<audio_utils::MelAggregator>& melAggregator)
            : mMelReporterCallback(melReporterCallback),
              mMelAggregator(melAggregator),
              mRs2UpperBound(kDefaultRs2UpperBound) {};

    /**
     * \brief Creates or gets the MelProcessor assigned to the streamHandle
     *
@@ -144,7 +151,7 @@ public:

    // ------ Override audio_utils::MelProcessor::MelCallback ------
    void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
                        audio_port_handle_t deviceId) const override;
                        audio_port_handle_t deviceId, bool attenuated) const override;

    void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;

@@ -205,6 +212,8 @@ private:

    sp<media::ISoundDoseCallback> getSoundDoseCallback() const;

    float getAttenuationForDeviceId(audio_port_handle_t id) const;

    void updateAttenuation(float attenuationDB, audio_devices_t deviceType);
    void setCsdEnabled(bool enabled);
    void setUseFrameworkMel(bool useFrameworkMel);
+37 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <SoundDoseManager.h>

#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <audio_utils/MelAggregator.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <media/AidlConversionCppNdk.h>
@@ -45,6 +46,14 @@ public:
    MOCK_METHOD(void, stopMelComputationForDeviceId, (audio_port_handle_t), (override));
};

class MelAggregatorMock : public audio_utils::MelAggregator {
public:
    MelAggregatorMock() : MelAggregator(100) {}

    MOCK_METHOD(std::vector<audio_utils::CsdRecord>, aggregateAndAddNewMelRecord,
                (const audio_utils::MelRecord&), (override));
};

constexpr char kPrimaryModule[] = "primary";
constexpr char kSecondaryModule[] = "secondary";

@@ -52,7 +61,8 @@ class SoundDoseManagerTest : public ::testing::Test {
protected:
    void SetUp() override {
        mMelReporterCallback = sp<MelReporterCallback>::make();
        mSoundDoseManager = sp<SoundDoseManager>::make(mMelReporterCallback);
        mMelAggregator = sp<MelAggregatorMock>::make();
        mSoundDoseManager = sp<SoundDoseManager>::make(mMelReporterCallback, mMelAggregator);
        mHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
        mSecondaryHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();

@@ -69,6 +79,7 @@ protected:
    }

    sp<MelReporterCallback> mMelReporterCallback;
    sp<MelAggregatorMock> mMelAggregator;
    sp<SoundDoseManager> mSoundDoseManager;
    std::shared_ptr<HalSoundDoseMock> mHalSoundDose;
    std::shared_ptr<HalSoundDoseMock> mSecondaryHalSoundDose;
@@ -110,12 +121,33 @@ TEST_F(SoundDoseManagerTest, RemoveExistingStream) {
    EXPECT_NE(processor1, processor2);
}

TEST_F(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
    std::vector<float>mels{1, 1};
TEST_F(SoundDoseManagerTest, NewMelValuesAttenuatedAggregateMels) {
    std::vector<float>mels{1.f, 1.f};

    mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
    EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord)
            .Times(1)
            .WillOnce([&] (const audio_utils::MelRecord& record) {
                EXPECT_THAT(record.mels, ::testing::ElementsAreArray(mels));
                return std::vector<audio_utils::CsdRecord>();
            });

    mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1,
                                      /*attenuated=*/true);
}

TEST_F(SoundDoseManagerTest, NewMelValuesUnattenuatedAreSplit) {
    std::vector<float>mels{79.f, 80.f, 79.f, 80.f, 79.f, 79.f, 80.f};

    EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord)
            .Times(3)
            .WillRepeatedly([&] (const audio_utils::MelRecord& record) {
                EXPECT_EQ(record.mels.size(), size_t {1});
                EXPECT_EQ(record.mels[0], 80.f);
                return std::vector<audio_utils::CsdRecord>();
            });

    EXPECT_EQ(mSoundDoseManager->getCachedMelRecordsSize(), size_t{1});
    mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1,
            /*attenuated=*/false);
}

TEST_F(SoundDoseManagerTest, InvalidHalInterfaceIsNotSet) {