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

Commit 4defd0b8 authored by Vlad Popa's avatar Vlad Popa
Browse files

CSD: report the new csd values to AudioService

We are reporting with every new callback from the MelProcessor the
values that lead to a change of at least 1% of CSD.

Test: manual, logs
Bug: 257238734
Change-Id: I3f0c39c9fda62bfccd3401e065586ca8621627ca
parent 63f047e4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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",
@@ -551,6 +552,7 @@ aidl_interface {
    local_include_dir: "aidl",
    srcs: [
        "aidl/android/media/ISoundDoseCallback.aidl",
        "aidl/android/media/SoundDoseRecord.aidl",
    ],

    double_loadable: true,
+9 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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);
}
+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;
}
+59 −50
Original line number Diff line number Diff line
@@ -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 {

@@ -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);

@@ -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()) {
@@ -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;
    {
@@ -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");
    });
@@ -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
+19 −16
Original line number Diff line number Diff line
@@ -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 {

@@ -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,
@@ -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);