CSD: notify when RS2 momentary exposure is reached
RS2 is defined in IEC 62368-1 3rd edition as 100dBA. Added however the
possibility to further restrict this value as part of the momentary
exposure API.
Test: UT and manual tests
Bug: 256616352
Change-Id: I004a620e5e7915824d390f7ddc41c16f9675d45a
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index 88500b8..6fc756b 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -65,9 +65,12 @@
std::lock_guard _lAf(mAudioFlinger.mLock);
auto thread = mAudioFlinger.checkPlaybackThread_l(streamHandle);
if (thread != nullptr) {
- thread->startMelComputation(mSoundDoseManager.getOrCreateCallbackForDevice(
+ thread->startMelComputation(mSoundDoseManager.getOrCreateProcessorForDevice(
deviceId,
- newPatch.streamHandle));
+ newPatch.streamHandle,
+ thread->mSampleRate,
+ thread->mChannelCount,
+ thread->mFormat));
}
}
}
@@ -97,11 +100,11 @@
// Stop MEL calculation for the PlaybackThread
std::lock_guard _lAf(mAudioFlinger.mLock);
+ mSoundDoseManager.removeStreamProcessor(melPatch.streamHandle);
auto thread = mAudioFlinger.checkPlaybackThread_l(melPatch.streamHandle);
if (thread != nullptr) {
thread->stopMelComputation();
}
- mSoundDoseManager.removeStreamCallback(melPatch.streamHandle);
}
std::string AudioFlinger::MelReporter::dump() {
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index f35944b..acbbe48 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3405,14 +3405,11 @@
return bytesWritten;
}
-void AudioFlinger::PlaybackThread::startMelComputation(const sp<
- audio_utils::MelProcessor::MelCallback>& callback)
+void AudioFlinger::PlaybackThread::startMelComputation(
+ const sp<audio_utils::MelProcessor>& processor)
{
- ALOGV("%s: creating new mel processor for thread %d", __func__, id());
- mMelProcessor = sp<audio_utils::MelProcessor>::make(mSampleRate,
- mChannelCount,
- mFormat,
- callback);
+ ALOGV("%s: starting mel processor for thread %d", __func__, id());
+ mMelProcessor = processor;
}
void AudioFlinger::PlaybackThread::stopMelComputation() {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a7028ee..8bf629b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1091,8 +1091,7 @@
return INVALID_OPERATION;
}
- void startMelComputation(const sp
- <audio_utils::MelProcessor::MelCallback>& callback);
+ void startMelComputation(const sp<audio_utils::MelProcessor>& processor);
void stopMelComputation();
protected:
diff --git a/services/audioflinger/sounddose/Android.bp b/services/audioflinger/sounddose/Android.bp
index eb9517e..5c72fba 100644
--- a/services/audioflinger/sounddose/Android.bp
+++ b/services/audioflinger/sounddose/Android.bp
@@ -10,9 +10,7 @@
cc_library {
name: "libsounddose",
- product_available: true,
double_loadable: true,
- host_supported: true,
srcs: [
"SoundDoseManager.cpp",
diff --git a/services/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 780d796..0a69c52 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -40,58 +40,95 @@
} // namespace
-sp<audio_utils::MelProcessor::MelCallback> SoundDoseManager::getOrCreateCallbackForDevice(
+sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice(
audio_port_handle_t deviceId,
- audio_io_handle_t streamHandle)
+ audio_io_handle_t streamHandle,
+ uint32_t sampleRate,
+ size_t channelCount,
+ audio_format_t format)
{
std::lock_guard _l(mLock);
- auto streamHandleCallback = mActiveCallbacks.find(streamHandle);
- if (streamHandleCallback != mActiveCallbacks.end()) {
+ auto streamProcessor = mActiveProcessors.find(streamHandle);
+ sp<audio_utils::MelProcessor> processor;
+ if (streamProcessor != mActiveProcessors.end()
+ && (processor = streamProcessor->second.promote())) {
ALOGV("%s: found callback for stream %d", __func__, streamHandle);
- auto callback = streamHandleCallback->second;
- callback->mDeviceHandle = deviceId;
- return callback;
+ processor->setDeviceId(deviceId);
+ return processor;
} else {
ALOGV("%s: creating new callback for device %d", __func__, streamHandle);
- sp<Callback> melCallback = sp<Callback>::make(*this, deviceId);
- mActiveCallbacks[streamHandle] = melCallback;
- return melCallback;
+ sp<audio_utils::MelProcessor> melProcessor =
+ sp<audio_utils::MelProcessor>::make(sampleRate,
+ channelCount,
+ format,
+ *this,
+ deviceId,
+ mRs2Value);
+ mActiveProcessors[streamHandle] = melProcessor;
+ return melProcessor;
}
}
-void SoundDoseManager::removeStreamCallback(audio_io_handle_t streamHandle)
-{
- std::unordered_map<audio_io_handle_t, sp<Callback>>::iterator callbackToRemove;
- std::lock_guard _l(mLock);
- callbackToRemove = mActiveCallbacks.find(streamHandle);
- if (callbackToRemove != mActiveCallbacks.end()) {
- mActiveCallbacks.erase(callbackToRemove);
- }
-}
-
-void SoundDoseManager::Callback::onNewMelValues(const std::vector<float>& mels,
- size_t offset,
- size_t length) const
+void SoundDoseManager::setOutputRs2(float rs2Value)
{
ALOGV("%s", __func__);
- std::lock_guard _l(mSoundDoseManager.mLock);
+ std::lock_guard _l(mLock);
+
+ for (auto& streamProcessor : mActiveProcessors) {
+ sp<audio_utils::MelProcessor> processor = streamProcessor.second.promote();
+ 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,
+ streamProcessor.first);
+ }
+ }
+ }
+}
+
+void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle)
+{
+ std::lock_guard _l(mLock);
+ auto callbackToRemove = mActiveProcessors.find(streamHandle);
+ if(callbackToRemove != mActiveProcessors.end()) {
+ mActiveProcessors.erase(callbackToRemove);
+ }
+}
+
+void SoundDoseManager::onNewMelValues(const std::vector<float>& mels,
+ size_t offset,
+ size_t length,
+ audio_port_handle_t deviceId) const
+{
+ ALOGV("%s", __func__);
+ std::lock_guard _l(mLock);
int64_t timestampSec = getMonotonicSecond();
// only for internal callbacks
- mSoundDoseManager.mMelAggregator.aggregateAndAddNewMelRecord(
- audio_utils::MelRecord(mDeviceHandle, std::vector<float>(
+ mMelAggregator->aggregateAndAddNewMelRecord(
+ audio_utils::MelRecord(deviceId, std::vector<float>(
mels.begin() + offset,
mels.begin() + offset + length),
timestampSec - length));
}
+void SoundDoseManager::onMomentaryExposure(float currentMel,
+ audio_port_handle_t deviceId) const {
+ ALOGV("%s: Momentary exposure for device %d triggered: %f MEL",
+ __func__,
+ deviceId,
+ currentMel);
+}
+
std::string SoundDoseManager::dump() const
{
std::string output;
- mMelAggregator.foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
+ mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
base::StringAppendF(&output,
"CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]",
csdRecord.value,
@@ -102,7 +139,7 @@
});
base::StringAppendF(&output, "\nCached Mel Records:\n");
- mMelAggregator.foreachCachedMel([&output](const audio_utils::MelRecord& melRecord) {
+ mMelAggregator->foreachCachedMel([&output](const audio_utils::MelRecord& melRecord) {
base::StringAppendF(&output, "Continuous MELs for portId=%d, ", melRecord.portId);
base::StringAppendF(&output, "starting at timestamp %" PRId64 ": ", melRecord.timestamp);
@@ -116,7 +153,7 @@
}
size_t SoundDoseManager::getCachedMelRecordsSize() const {
- return mMelAggregator.getCachedMelRecordsSize();
+ return mMelAggregator->getCachedMelRecordsSize();
}
} // namespace android
diff --git a/services/audioflinger/sounddose/SoundDoseManager.h b/services/audioflinger/sounddose/SoundDoseManager.h
index fae5ba7..5934d2e 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.h
+++ b/services/audioflinger/sounddose/SoundDoseManager.h
@@ -21,60 +21,76 @@
#include <audio_utils/MelAggregator.h>
#include <mutex>
#include <unordered_map>
+#include <utils/Errors.h>
namespace android {
-/** CSD is computed with a rolling window of 7 days. */
-constexpr int64_t kCsdWindowSeconds = 604800; // 60 * 60 * 24 * 7
-
-class SoundDoseManager {
+class SoundDoseManager : public audio_utils::MelProcessor::MelCallback {
public:
- SoundDoseManager() : mMelAggregator(kCsdWindowSeconds) {};
+ /** CSD is computed with a rolling window of 7 days. */
+ static constexpr int64_t kCsdWindowSeconds = 604800; // 60s * 60m * 24h * 7d
+ /** Default RS2 value in dBA as defined in IEC 62368-1 3rd edition. */
+ static constexpr float kDefaultRs2Value = 100.f;
+
+ SoundDoseManager()
+ : mMelAggregator(sp<audio_utils::MelAggregator>::make(kCsdWindowSeconds)),
+ mRs2Value(kDefaultRs2Value) {};
/**
- * \brief Creates or gets the callback assigned to the streamHandle
+ * \brief Creates or gets the MelProcessor assigned to the streamHandle
*
* \param deviceId id for the devices where the stream is active.
* \param streanHandle handle to the stream
+ * \param sampleRate sample rate for the processor
+ * \param channelCount number of channels to be processed.
+ * \param format format of the input samples.
+ *
+ * \return MelProcessor assigned to the stream and device id.
*/
- sp<audio_utils::MelProcessor::MelCallback> getOrCreateCallbackForDevice(
+ sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(
audio_port_handle_t deviceId,
- audio_io_handle_t streamHandle);
+ audio_io_handle_t streamHandle,
+ uint32_t sampleRate,
+ size_t channelCount,
+ audio_format_t format);
/**
- * \brief Removes stream callback when MEL computation is not needed anymore
+ * \brief Removes stream processor when MEL computation is not needed anymore
*
* \param streanHandle handle to the stream
*/
- void removeStreamCallback(audio_io_handle_t streamHandle);
+ void removeStreamProcessor(audio_io_handle_t streamHandle);
+
+ /**
+ * Sets the output RS2 value for momentary exposure warnings. Must not be
+ * higher than 100dBA and not lower than 80dBA.
+ *
+ * \param rs2Value value to use for momentary exposure
+ */
+ void setOutputRs2(float rs2Value);
std::string dump() const;
// used for testing
size_t getCachedMelRecordsSize() const;
+
+ // ------ 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;
+
+ void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;
private:
- /**
- * An implementation of the MelProcessor::MelCallback that is assigned to a
- * specific device.
- */
- class Callback : public audio_utils::MelProcessor::MelCallback {
- public:
- Callback(SoundDoseManager& soundDoseManager, audio_port_handle_t deviceHandle)
- : mSoundDoseManager(soundDoseManager), mDeviceHandle(deviceHandle) {}
-
- void onNewMelValues(const std::vector<float>& mels,
- size_t offset,
- size_t length) const override;
-
- SoundDoseManager& mSoundDoseManager;
- audio_port_handle_t mDeviceHandle;
- };
+ mutable std::mutex mLock;
// no need for lock since MelAggregator is thread-safe
- audio_utils::MelAggregator mMelAggregator;
+ const sp<audio_utils::MelAggregator> mMelAggregator;
- std::mutex mLock;
- std::unordered_map<audio_io_handle_t, sp<Callback>> mActiveCallbacks GUARDED_BY(mLock);
+ std::unordered_map<audio_io_handle_t,
+ wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock);
+
+ float mRs2Value GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/audioflinger/sounddose/tests/Android.bp b/services/audioflinger/sounddose/tests/Android.bp
index cb10950..d1779c2 100644
--- a/services/audioflinger/sounddose/tests/Android.bp
+++ b/services/audioflinger/sounddose/tests/Android.bp
@@ -9,7 +9,6 @@
cc_test {
name: "sounddosemanager_tests",
- host_supported: true,
srcs: [
"sounddosemanager_tests.cpp"
diff --git a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
index 3fa7d6c..0aa5a20 100644
--- a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
+++ b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
@@ -24,35 +24,49 @@
namespace android {
namespace {
-TEST(SoundDoseManagerTest, GetCallbackForExistingStream) {
+TEST(SoundDoseManagerTest, GetProcessorForExistingStream) {
SoundDoseManager soundDoseManager;
- sp<audio_utils::MelProcessor::MelCallback> callback1 =
- soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
- sp<audio_utils::MelProcessor::MelCallback> callback2 =
- soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/2, /*streamHandle=*/1);
+ sp<audio_utils::MelProcessor> processor1 =
+ soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+ /*streamHandle=*/1,
+ /*sampleRate*/44100,
+ /*channelCount*/2,
+ /*format*/AUDIO_FORMAT_PCM_FLOAT);
+ sp<audio_utils::MelProcessor> processor2 =
+ soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+ /*streamHandle=*/1,
+ /*sampleRate*/44100,
+ /*channelCount*/2,
+ /*format*/AUDIO_FORMAT_PCM_FLOAT);
- EXPECT_EQ(callback1, callback2);
+ EXPECT_EQ(processor1, processor2);
}
TEST(SoundDoseManagerTest, RemoveExistingStream) {
SoundDoseManager soundDoseManager;
- sp<audio_utils::MelProcessor::MelCallback> callback1 =
- soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
+ sp<audio_utils::MelProcessor> processor1 =
+ soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+ /*streamHandle=*/1,
+ /*sampleRate*/44100,
+ /*channelCount*/2,
+ /*format*/AUDIO_FORMAT_PCM_FLOAT);
- soundDoseManager.removeStreamCallback(1);
- sp<audio_utils::MelProcessor::MelCallback> callback2 =
- soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/2, /*streamHandle=*/1);
+ soundDoseManager.removeStreamProcessor(1);
+ sp<audio_utils::MelProcessor> processor2 =
+ soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+ /*streamHandle=*/1,
+ /*sampleRate*/44100,
+ /*channelCount*/2,
+ /*format*/AUDIO_FORMAT_PCM_FLOAT);
- EXPECT_NE(callback1, callback2);
+ EXPECT_NE(processor1, processor2);
}
TEST(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
SoundDoseManager soundDoseManager;
std::vector<float>mels{1, 1};
- sp<audio_utils::MelProcessor::MelCallback> callback =
- soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
- callback->onNewMelValues(mels, 0, mels.size());
+ soundDoseManager.onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
EXPECT_EQ(soundDoseManager.getCachedMelRecordsSize(), size_t{1});
}