Loading media/libaudioclient/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -453,6 +453,7 @@ aidl_interface { "aidl/android/media/IAudioTrackCallback.aidl", "aidl/android/media/ISoundDoseCallback.aidl", "aidl/android/media/SoundDoseRecord.aidl", ], imports: [ "android.media.audio.common.types-V2", Loading Loading @@ -551,6 +552,7 @@ aidl_interface { local_include_dir: "aidl", srcs: [ "aidl/android/media/ISoundDoseCallback.aidl", "aidl/android/media/SoundDoseRecord.aidl", ], double_loadable: true, Loading media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl +9 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.media; import android.media.SoundDoseRecord; /** * Interface used to push the sound dose related information from the audio * server to the AudioService#SoundDoseHelper. Loading @@ -23,4 +25,11 @@ package android.media; interface ISoundDoseCallback { /** Called whenever the momentary exposure exceeds the RS2 value. */ oneway void onMomentaryExposure(float currentMel, int deviceId); /** * Notifies that the CSD value has changed. The currentCsd is normalized * with value 1 representing 100% of sound dose. SoundDoseRecord represents * the newest record that lead to the new currentCsd. */ oneway void onNewCsdValue(float currentCsd, in SoundDoseRecord[] records); } media/libaudioclient/aidl/android/media/SoundDoseRecord.aidl 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright 2022 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. */ package android.media; /** Record containing information about the computed sound dose. */ parcelable SoundDoseRecord { /** * Corresponds to the time in seconds when the CSD value is calculated from. * Values should be consistent and referenced from the same clock (e.g.: monotonic) */ long timestamp; /** Corresponds to the duration that leads to the CSD value. */ int duration; /** The actual contribution to the CSD computation normalized: 1.f is 100%CSD. */ float value; /** The average MEL value in this time frame that lead to this CSD value. */ float averageMel; } services/audioflinger/sounddose/SoundDoseManager.cpp +59 −50 Original line number Diff line number Diff line Loading @@ -21,9 +21,10 @@ #include "SoundDoseManager.h" #include <android-base/stringprintf.h> #include <cinttypes> #include <utils/Log.h> #include <time.h> #include <utils/Log.h> #include <cinttypes> #include "android/media/SoundDoseRecord.h" namespace android { Loading @@ -41,38 +42,27 @@ int64_t getMonotonicSecond() { } // namespace sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice( audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, audio_format_t format) { audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, audio_format_t format) { std::lock_guard _l(mLock); auto streamProcessor = mActiveProcessors.find(streamHandle); sp<audio_utils::MelProcessor> processor; if (streamProcessor != mActiveProcessors.end() && (processor = streamProcessor->second.promote())) { if (streamProcessor != mActiveProcessors.end() && (processor = streamProcessor->second.promote())) { ALOGV("%s: found callback for stream %d", __func__, streamHandle); processor->setDeviceId(deviceId); return processor; } else { ALOGV("%s: creating new callback for device %d", __func__, streamHandle); sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make(sampleRate, channelCount, format, *this, deviceId, mRs2Value); sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make( sampleRate, channelCount, format, *this, deviceId, mRs2Value); mActiveProcessors[streamHandle] = melProcessor; return melProcessor; } } void SoundDoseManager::setOutputRs2(float rs2Value) { void SoundDoseManager::setOutputRs2(float rs2Value) { ALOGV("%s", __func__); std::lock_guard _l(mLock); Loading @@ -81,17 +71,14 @@ void SoundDoseManager::setOutputRs2(float rs2Value) if (processor != nullptr) { status_t result = processor->setOutputRs2(rs2Value); if (result != NO_ERROR) { ALOGW("%s: could not set RS2 value %f for stream %d", __func__, rs2Value, ALOGW("%s: could not set RS2 value %f for stream %d", __func__, rs2Value, streamProcessor.first); } } } } void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) { void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) { std::lock_guard _l(mLock); auto callbackToRemove = mActiveProcessors.find(streamHandle); if (callbackToRemove != mActiveProcessors.end()) { Loading @@ -99,30 +86,45 @@ void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) } } void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const { void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const { ALOGV("%s", __func__); sp<media::ISoundDoseCallback> soundDoseCallback; std::vector<audio_utils::CsdRecord> records; float currentCsd; { std::lock_guard _l(mLock); int64_t timestampSec = getMonotonicSecond(); // only for internal callbacks mMelAggregator->aggregateAndAddNewMelRecord( audio_utils::MelRecord(deviceId, std::vector<float>( mels.begin() + offset, mels.begin() + offset + length), records = mMelAggregator->aggregateAndAddNewMelRecord(audio_utils::MelRecord( deviceId, std::vector<float>(mels.begin() + offset, mels.begin() + offset + length), timestampSec - length)); currentCsd = mMelAggregator->getCsd(); } void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const { ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel); soundDoseCallback = getSoundDoseCallback(); if (records.size() > 0 && soundDoseCallback != nullptr) { std::vector<media::SoundDoseRecord> newRecordsToReport; for (const auto& record : records) { newRecordsToReport.emplace_back(csdRecordToSoundDoseRecord(record)); } soundDoseCallback->onNewCsdValue(currentCsd, newRecordsToReport); } } sp<media::ISoundDoseCallback> SoundDoseManager::getSoundDoseCallback() const { std::lock_guard _l(mLock); return mSoundDoseCallback; } void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const { ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel); sp<media::ISoundDoseCallback> soundDoseCallback; { Loading @@ -142,15 +144,12 @@ void SoundDoseManager::registerSoundDoseCallback(const sp<media::ISoundDoseCallb mSoundDoseCallback = callback; } std::string SoundDoseManager::dump() const { std::string SoundDoseManager::dump() const { std::string output; mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) { base::StringAppendF(&output, "CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]", csdRecord.value, csdRecord.averageMel, csdRecord.timestamp, csdRecord.value, csdRecord.averageMel, csdRecord.timestamp, csdRecord.timestamp + csdRecord.duration); base::StringAppendF(&output, "\n"); }); Loading @@ -173,4 +172,14 @@ size_t SoundDoseManager::getCachedMelRecordsSize() const { return mMelAggregator->getCachedMelRecordsSize(); } media::SoundDoseRecord SoundDoseManager::csdRecordToSoundDoseRecord( const audio_utils::CsdRecord& legacy) { media::SoundDoseRecord soundDoseRecord{}; soundDoseRecord.timestamp = legacy.timestamp; soundDoseRecord.duration = legacy.duration; soundDoseRecord.value = legacy.value; soundDoseRecord.averageMel = legacy.averageMel; return soundDoseRecord; } } // namespace android services/audioflinger/sounddose/SoundDoseManager.h +19 −16 Original line number Diff line number Diff line Loading @@ -18,11 +18,11 @@ #pragma once #include <android/media/ISoundDoseCallback.h> #include <audio_utils/MelProcessor.h> #include <audio_utils/MelAggregator.h> #include <audio_utils/MelProcessor.h> #include <utils/Errors.h> #include <mutex> #include <unordered_map> #include <utils/Errors.h> namespace android { Loading @@ -48,8 +48,7 @@ public: * * \return MelProcessor assigned to the stream and device id. */ sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice( audio_port_handle_t deviceId, sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, Loading Loading @@ -78,21 +77,25 @@ public: // used for testing size_t getCachedMelRecordsSize() const; /** Method for converting from audio_utils::CsdRecord to media::SoundDoseRecord. */ static media::SoundDoseRecord csdRecordToSoundDoseRecord(const audio_utils::CsdRecord& legacy); // ------ Override audio_utils::MelProcessor::MelCallback ------ void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const override; void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override; private: sp<media::ISoundDoseCallback> getSoundDoseCallback() const; mutable std::mutex mLock; // no need for lock since MelAggregator is thread-safe const sp<audio_utils::MelAggregator> mMelAggregator; std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock); std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock); float mRs2Value GUARDED_BY(mLock); Loading Loading
media/libaudioclient/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -453,6 +453,7 @@ aidl_interface { "aidl/android/media/IAudioTrackCallback.aidl", "aidl/android/media/ISoundDoseCallback.aidl", "aidl/android/media/SoundDoseRecord.aidl", ], imports: [ "android.media.audio.common.types-V2", Loading Loading @@ -551,6 +552,7 @@ aidl_interface { local_include_dir: "aidl", srcs: [ "aidl/android/media/ISoundDoseCallback.aidl", "aidl/android/media/SoundDoseRecord.aidl", ], double_loadable: true, Loading
media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl +9 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.media; import android.media.SoundDoseRecord; /** * Interface used to push the sound dose related information from the audio * server to the AudioService#SoundDoseHelper. Loading @@ -23,4 +25,11 @@ package android.media; interface ISoundDoseCallback { /** Called whenever the momentary exposure exceeds the RS2 value. */ oneway void onMomentaryExposure(float currentMel, int deviceId); /** * Notifies that the CSD value has changed. The currentCsd is normalized * with value 1 representing 100% of sound dose. SoundDoseRecord represents * the newest record that lead to the new currentCsd. */ oneway void onNewCsdValue(float currentCsd, in SoundDoseRecord[] records); }
media/libaudioclient/aidl/android/media/SoundDoseRecord.aidl 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright 2022 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. */ package android.media; /** Record containing information about the computed sound dose. */ parcelable SoundDoseRecord { /** * Corresponds to the time in seconds when the CSD value is calculated from. * Values should be consistent and referenced from the same clock (e.g.: monotonic) */ long timestamp; /** Corresponds to the duration that leads to the CSD value. */ int duration; /** The actual contribution to the CSD computation normalized: 1.f is 100%CSD. */ float value; /** The average MEL value in this time frame that lead to this CSD value. */ float averageMel; }
services/audioflinger/sounddose/SoundDoseManager.cpp +59 −50 Original line number Diff line number Diff line Loading @@ -21,9 +21,10 @@ #include "SoundDoseManager.h" #include <android-base/stringprintf.h> #include <cinttypes> #include <utils/Log.h> #include <time.h> #include <utils/Log.h> #include <cinttypes> #include "android/media/SoundDoseRecord.h" namespace android { Loading @@ -41,38 +42,27 @@ int64_t getMonotonicSecond() { } // namespace sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice( audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, audio_format_t format) { audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, audio_format_t format) { std::lock_guard _l(mLock); auto streamProcessor = mActiveProcessors.find(streamHandle); sp<audio_utils::MelProcessor> processor; if (streamProcessor != mActiveProcessors.end() && (processor = streamProcessor->second.promote())) { if (streamProcessor != mActiveProcessors.end() && (processor = streamProcessor->second.promote())) { ALOGV("%s: found callback for stream %d", __func__, streamHandle); processor->setDeviceId(deviceId); return processor; } else { ALOGV("%s: creating new callback for device %d", __func__, streamHandle); sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make(sampleRate, channelCount, format, *this, deviceId, mRs2Value); sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make( sampleRate, channelCount, format, *this, deviceId, mRs2Value); mActiveProcessors[streamHandle] = melProcessor; return melProcessor; } } void SoundDoseManager::setOutputRs2(float rs2Value) { void SoundDoseManager::setOutputRs2(float rs2Value) { ALOGV("%s", __func__); std::lock_guard _l(mLock); Loading @@ -81,17 +71,14 @@ void SoundDoseManager::setOutputRs2(float rs2Value) if (processor != nullptr) { status_t result = processor->setOutputRs2(rs2Value); if (result != NO_ERROR) { ALOGW("%s: could not set RS2 value %f for stream %d", __func__, rs2Value, ALOGW("%s: could not set RS2 value %f for stream %d", __func__, rs2Value, streamProcessor.first); } } } } void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) { void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) { std::lock_guard _l(mLock); auto callbackToRemove = mActiveProcessors.find(streamHandle); if (callbackToRemove != mActiveProcessors.end()) { Loading @@ -99,30 +86,45 @@ void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) } } void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const { void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const { ALOGV("%s", __func__); sp<media::ISoundDoseCallback> soundDoseCallback; std::vector<audio_utils::CsdRecord> records; float currentCsd; { std::lock_guard _l(mLock); int64_t timestampSec = getMonotonicSecond(); // only for internal callbacks mMelAggregator->aggregateAndAddNewMelRecord( audio_utils::MelRecord(deviceId, std::vector<float>( mels.begin() + offset, mels.begin() + offset + length), records = mMelAggregator->aggregateAndAddNewMelRecord(audio_utils::MelRecord( deviceId, std::vector<float>(mels.begin() + offset, mels.begin() + offset + length), timestampSec - length)); currentCsd = mMelAggregator->getCsd(); } void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const { ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel); soundDoseCallback = getSoundDoseCallback(); if (records.size() > 0 && soundDoseCallback != nullptr) { std::vector<media::SoundDoseRecord> newRecordsToReport; for (const auto& record : records) { newRecordsToReport.emplace_back(csdRecordToSoundDoseRecord(record)); } soundDoseCallback->onNewCsdValue(currentCsd, newRecordsToReport); } } sp<media::ISoundDoseCallback> SoundDoseManager::getSoundDoseCallback() const { std::lock_guard _l(mLock); return mSoundDoseCallback; } void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const { ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel); sp<media::ISoundDoseCallback> soundDoseCallback; { Loading @@ -142,15 +144,12 @@ void SoundDoseManager::registerSoundDoseCallback(const sp<media::ISoundDoseCallb mSoundDoseCallback = callback; } std::string SoundDoseManager::dump() const { std::string SoundDoseManager::dump() const { std::string output; mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) { base::StringAppendF(&output, "CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]", csdRecord.value, csdRecord.averageMel, csdRecord.timestamp, csdRecord.value, csdRecord.averageMel, csdRecord.timestamp, csdRecord.timestamp + csdRecord.duration); base::StringAppendF(&output, "\n"); }); Loading @@ -173,4 +172,14 @@ size_t SoundDoseManager::getCachedMelRecordsSize() const { return mMelAggregator->getCachedMelRecordsSize(); } media::SoundDoseRecord SoundDoseManager::csdRecordToSoundDoseRecord( const audio_utils::CsdRecord& legacy) { media::SoundDoseRecord soundDoseRecord{}; soundDoseRecord.timestamp = legacy.timestamp; soundDoseRecord.duration = legacy.duration; soundDoseRecord.value = legacy.value; soundDoseRecord.averageMel = legacy.averageMel; return soundDoseRecord; } } // namespace android
services/audioflinger/sounddose/SoundDoseManager.h +19 −16 Original line number Diff line number Diff line Loading @@ -18,11 +18,11 @@ #pragma once #include <android/media/ISoundDoseCallback.h> #include <audio_utils/MelProcessor.h> #include <audio_utils/MelAggregator.h> #include <audio_utils/MelProcessor.h> #include <utils/Errors.h> #include <mutex> #include <unordered_map> #include <utils/Errors.h> namespace android { Loading @@ -48,8 +48,7 @@ public: * * \return MelProcessor assigned to the stream and device id. */ sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice( audio_port_handle_t deviceId, sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate, size_t channelCount, Loading Loading @@ -78,21 +77,25 @@ public: // used for testing size_t getCachedMelRecordsSize() const; /** Method for converting from audio_utils::CsdRecord to media::SoundDoseRecord. */ static media::SoundDoseRecord csdRecordToSoundDoseRecord(const audio_utils::CsdRecord& legacy); // ------ Override audio_utils::MelProcessor::MelCallback ------ void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length, audio_port_handle_t deviceId) const override; void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override; private: sp<media::ISoundDoseCallback> getSoundDoseCallback() const; mutable std::mutex mLock; // no need for lock since MelAggregator is thread-safe const sp<audio_utils::MelAggregator> mMelAggregator; std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock); std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock); float mRs2Value GUARDED_BY(mLock); Loading