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

Commit 8ce28dae authored by Vlad Popa's avatar Vlad Popa
Browse files

CSD: Attenuate the HAL Mel values

In case of absolute volume we now attenuate the MEL values returned also
by the HAL sound dose interfaces. This is just temporary and might need
a better solution in which the audio HAL has access to the attenuation
on a lower level.

Test: atest sounddosemanager_tests + manual dose warning check
Bug: 315218453
Change-Id: I2a2a7743fe0a0fd9bd9fbd5da3d12878360b83c8
parent 69facedd
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) {