Loading services/audioflinger/sounddose/SoundDoseManager.cpp +81 −11 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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( Loading Loading @@ -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. Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -655,6 +721,10 @@ void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t if (!mEnabledCsd) { return; } if (currentMel < mRs2UpperBound) { return; } } auto soundDoseCallback = getSoundDoseCallback(); Loading services/audioflinger/sounddose/SoundDoseManager.h +10 −1 Original line number Diff line number Diff line Loading @@ -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 * Loading Loading @@ -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; Loading Loading @@ -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); Loading services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp +37 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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"; Loading @@ -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>(); Loading @@ -69,6 +79,7 @@ protected: } sp<MelReporterCallback> mMelReporterCallback; sp<MelAggregatorMock> mMelAggregator; sp<SoundDoseManager> mSoundDoseManager; std::shared_ptr<HalSoundDoseMock> mHalSoundDose; std::shared_ptr<HalSoundDoseMock> mSecondaryHalSoundDose; Loading Loading @@ -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) { Loading Loading
services/audioflinger/sounddose/SoundDoseManager.cpp +81 −11 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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( Loading Loading @@ -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. Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -655,6 +721,10 @@ void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t if (!mEnabledCsd) { return; } if (currentMel < mRs2UpperBound) { return; } } auto soundDoseCallback = getSoundDoseCallback(); Loading
services/audioflinger/sounddose/SoundDoseManager.h +10 −1 Original line number Diff line number Diff line Loading @@ -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 * Loading Loading @@ -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; Loading Loading @@ -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); Loading
services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp +37 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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"; Loading @@ -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>(); Loading @@ -69,6 +79,7 @@ protected: } sp<MelReporterCallback> mMelReporterCallback; sp<MelAggregatorMock> mMelAggregator; sp<SoundDoseManager> mSoundDoseManager; std::shared_ptr<HalSoundDoseMock> mHalSoundDose; std::shared_ptr<HalSoundDoseMock> mSecondaryHalSoundDose; Loading Loading @@ -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) { Loading