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

Commit e4064ceb authored by Ram Mohan's avatar Ram Mohan Committed by Shunkai Yao
Browse files

DynamicsProcessing: Add AIDL placeholder implementation and its unit test

Update audio_effects_config.xml to use libeffect dynamicsProcessing
implementation.

Bug: 258124419
Test: atest VtsHalDynamicsProcessingTargetTest
Test: atest VtsHalAudioEffectTargetTest

Change-Id: If93d084be383b716ff950faf1c3e23d6c1edaa66
parent 5bfe81fb
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@
    {
      "name": "VtsHalDownmixTargetTest"
    },
    {
      "name": "VtsHalDynamicProcessingTargetTest"
    },
    {
      "name": "VtsHalEnvironmentalReverbTargetTest"
    },
+1 −1
Original line number Diff line number Diff line
@@ -156,7 +156,7 @@ cc_binary {
        "libbassboostsw",
        "libbundleaidl",
        "libdownmixaidl",
        "libdynamicsprocessingsw",
        "libdynamicsprocessingaidl",
        "libenvreverbsw",
        "libequalizersw",
        "libhapticgeneratoraidl",
+0 −1
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descrip
        return EX_ILLEGAL_ARGUMENT;
    }
    *_aidl_return = AcousticEchoCancelerSw::kDescriptor;
    LOG(ERROR) << __func__ << "xxx " << _aidl_return->toString();
    return EX_NONE;
}

+2 −2
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@
        <library name="bassboostsw" path="libbassboostsw.so"/>
        <library name="bundle" path="libbundleaidl.so"/>
        <library name="downmix" path="libdownmixaidl.so"/>
        <library name="dynamics_processingsw" path="libdynamicsprocessingsw.so"/>
        <library name="dynamics_processing" path="libdynamicsprocessingaidl.so"/>
        <library name="equalizersw" path="libequalizersw.so"/>
        <library name="haptic_generator" path="libhapticgeneratoraidl.so"/>
        <library name="loudness_enhancer" path="libloudnessenhanceraidl.so"/>
@@ -72,7 +72,7 @@
            <libsw library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
        </effectProxy>
        <effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
        <effect name="dynamics_processing" library="dynamics_processingsw" uuid="fa818d78-588b-11ed-9b6a-0242ac120002"/>
        <effect name="dynamics_processing" library="dynamics_processing" uuid="e0e6539b-1781-7261-676f-6d7573696340"/>
        <effect name="haptic_generator" library="haptic_generator" uuid="97c4acd1-8b82-4f2f-832e-c2fe5d7a9931"/>
        <effect name="loudness_enhancer" library="loudness_enhancer" uuid="fa415329-2034-4bea-b5dc-5b381c8d1e2c"/>
        <effect name="env_reverb" library="env_reverbsw" uuid="fa819886-588b-11ed-9b6a-0242ac120002"/>
+382 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define LOG_TAG "AHAL_DynamicsProcessingSw"
#include <Utils.h>
#include <algorithm>
#include <set>
#include <unordered_set>

#include <android-base/logging.h>
@@ -60,7 +61,8 @@ extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descrip
namespace aidl::android::hardware::audio::effect {

const std::string DynamicsProcessingSw::kEffectName = "DynamicsProcessingSw";
const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability;
const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability = {.minCutOffFreq = 220,
                                                                          .maxCutOffFreq = 20000};
const Descriptor DynamicsProcessingSw::kDescriptor = {
        .common = {.id = {.type = kDynamicsProcessingTypeUUID,
                          .uuid = kDynamicsProcessingSwImplUUID,
@@ -83,16 +85,143 @@ ndk::ScopedAStatus DynamicsProcessingSw::setParameterSpecific(const Parameter::S
    RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
              "EffectNotSupported");

    mSpecificParam = specific.get<Parameter::Specific::dynamicsProcessing>();
    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");

    LOG(INFO) << __func__ << specific.toString();
    auto& dpParam = specific.get<Parameter::Specific::dynamicsProcessing>();
    auto tag = dpParam.getTag();
    switch (tag) {
        case DynamicsProcessing::engineArchitecture: {
            RETURN_IF(mContext->setEngineArchitecture(
                              dpParam.get<DynamicsProcessing::engineArchitecture>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::preEq: {
            RETURN_IF(mContext->setPreEqChannelCfgs(dpParam.get<DynamicsProcessing::preEq>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setPreEqChannelCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::postEq: {
            RETURN_IF(mContext->setPostEqChannelCfgs(dpParam.get<DynamicsProcessing::postEq>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setPostEqChannelCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::mbc: {
            RETURN_IF(mContext->setMbcChannelCfgs(dpParam.get<DynamicsProcessing::mbc>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setMbcChannelCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::preEqBand: {
            RETURN_IF(mContext->setPreEqBandCfgs(dpParam.get<DynamicsProcessing::preEqBand>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setPreEqBandCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::postEqBand: {
            RETURN_IF(mContext->setPostEqBandCfgs(dpParam.get<DynamicsProcessing::postEqBand>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setPostEqBandCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::mbcBand: {
            RETURN_IF(mContext->setMbcBandCfgs(dpParam.get<DynamicsProcessing::mbcBand>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "setMbcBandCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::limiter: {
            RETURN_IF(mContext->setLimiterCfgs(dpParam.get<DynamicsProcessing::limiter>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "limiterCfgsFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::inputGain: {
            RETURN_IF(mContext->setInputGainCfgs(dpParam.get<DynamicsProcessing::inputGain>()) !=
                              RetCode::SUCCESS,
                      EX_ILLEGAL_ARGUMENT, "inputGainCfgFailed");
            return ndk::ScopedAStatus::ok();
        }
        case DynamicsProcessing::vendorExtension: {
            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
        }
    }
}

ndk::ScopedAStatus DynamicsProcessingSw::getParameterSpecific(const Parameter::Id& id,
                                                              Parameter::Specific* specific) {
    auto tag = id.getTag();
    RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
    specific->set<Parameter::Specific::dynamicsProcessing>(mSpecificParam);
    auto dpId = id.get<Parameter::Id::dynamicsProcessingTag>();
    auto dpIdTag = dpId.getTag();
    switch (dpIdTag) {
        case DynamicsProcessing::Id::commonTag:
            return getParameterDynamicsProcessing(dpId.get<DynamicsProcessing::Id::commonTag>(),
                                                  specific);
        case DynamicsProcessing::Id::vendorExtensionTag:
            LOG(ERROR) << __func__ << " unsupported tag: " << toString(dpIdTag);
            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
    }
}

ndk::ScopedAStatus DynamicsProcessingSw::getParameterDynamicsProcessing(
        const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) {
    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");

    DynamicsProcessing dpParam;
    switch (tag) {
        case DynamicsProcessing::Tag::engineArchitecture: {
            dpParam.set<DynamicsProcessing::engineArchitecture>(mContext->getEngineArchitecture());
            break;
        }
        case DynamicsProcessing::Tag::preEq: {
            dpParam.set<DynamicsProcessing::preEq>(mContext->getPreEqChannelCfgs());
            break;
        }
        case DynamicsProcessing::Tag::postEq: {
            dpParam.set<DynamicsProcessing::postEq>(mContext->getPostEqChannelCfgs());
            break;
        }
        case DynamicsProcessing::Tag::mbc: {
            dpParam.set<DynamicsProcessing::mbc>(mContext->getMbcChannelCfgs());
            break;
        }
        case DynamicsProcessing::Tag::preEqBand: {
            dpParam.set<DynamicsProcessing::preEqBand>(mContext->getPreEqBandCfgs());
            break;
        }
        case DynamicsProcessing::Tag::postEqBand: {
            dpParam.set<DynamicsProcessing::postEqBand>(mContext->getPostEqBandCfgs());
            break;
        }
        case DynamicsProcessing::Tag::mbcBand: {
            dpParam.set<DynamicsProcessing::mbcBand>(mContext->getMbcBandCfgs());
            break;
        }
        case DynamicsProcessing::Tag::limiter: {
            dpParam.set<DynamicsProcessing::limiter>(mContext->getLimiterCfgs());
            break;
        }
        case DynamicsProcessing::Tag::inputGain: {
            dpParam.set<DynamicsProcessing::inputGain>(mContext->getInputGainCfgs());
            break;
        }
        case DynamicsProcessing::vendorExtension: {
            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
        }
    }

    specific->set<Parameter::Specific::dynamicsProcessing>(dpParam);
    LOG(INFO) << __func__ << specific->toString();
    return ndk::ScopedAStatus::ok();
}

@@ -127,4 +256,252 @@ IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, i
    return {STATUS_OK, samples, samples};
}

RetCode DynamicsProcessingSwContext::setCommon(const Parameter::Common& common) {
    mCommon = common;
    mChannelCount =
            ::android::hardware::audio::common::getChannelCount(common.input.base.channelMask);
    resizeChannels();
    resizeBands();
    LOG(INFO) << __func__ << mCommon.toString();
    return RetCode::SUCCESS;
}

RetCode DynamicsProcessingSwContext::setEngineArchitecture(
        const DynamicsProcessing::EngineArchitecture& cfg) {
    RETURN_VALUE_IF(!validateEngineConfig(cfg), RetCode::ERROR_ILLEGAL_PARAMETER,
                    "illegalEngineConfig");

    if (mEngineSettings == cfg) {
        LOG(INFO) << __func__ << " not change in engine, do nothing";
        return RetCode::SUCCESS;
    }
    mEngineSettings = cfg;
    resizeBands();
    return RetCode::SUCCESS;
}

RetCode DynamicsProcessingSwContext::setChannelCfgs(
        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,
        std::vector<DynamicsProcessing::ChannelConfig>& targetCfgs,
        const DynamicsProcessing::StageEnablement& stage) {
    RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "stageNotInUse");

    RetCode ret = RetCode::SUCCESS;
    std::unordered_set<int> channelSet;
    for (auto& cfg : cfgs) {
        if (cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount) {
            LOG(ERROR) << __func__ << " skip illegal channel config " << cfg.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
            continue;
        }
        if (0 != channelSet.count(cfg.channel)) {
            LOG(WARNING) << __func__ << " duplicated channel " << cfg.channel;
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
        } else {
            channelSet.insert(cfg.channel);
        }
        targetCfgs[cfg.channel] = cfg;
    }
    return ret;
}

RetCode DynamicsProcessingSwContext::setPreEqChannelCfgs(
        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
    return setChannelCfgs(cfgs, mPreEqChCfgs, mEngineSettings.preEqStage);
}

RetCode DynamicsProcessingSwContext::setPostEqChannelCfgs(
        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
    return setChannelCfgs(cfgs, mPostEqChCfgs, mEngineSettings.postEqStage);
}

RetCode DynamicsProcessingSwContext::setMbcChannelCfgs(
        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
    return setChannelCfgs(cfgs, mMbcChCfgs, mEngineSettings.mbcStage);
}

RetCode DynamicsProcessingSwContext::setEqBandCfgs(
        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
        std::vector<DynamicsProcessing::EqBandConfig>& targetCfgs,
        const DynamicsProcessing::StageEnablement& stage,
        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
    RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "eqStageNotInUse");

    RetCode ret = RetCode::SUCCESS;
    std::set<std::pair<int /* channel */, int /* band */>> bandSet;

    for (auto& cfg : cfgs) {
        if (0 != bandSet.count({cfg.channel, cfg.band})) {
            LOG(WARNING) << __func__ << " duplicated band " << cfg.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
        } else {
            bandSet.insert({cfg.channel, cfg.band});
        }
        if (!validateEqBandConfig(cfg, mChannelCount, stage.bandCount, channelConfig)) {
            LOG(WARNING) << __func__ << " skip invalid band " << cfg.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
            continue;
            ;
        }
        targetCfgs[cfg.channel * stage.bandCount + cfg.band] = cfg;
    }
    return ret;
}

RetCode DynamicsProcessingSwContext::setPreEqBandCfgs(
        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
    return setEqBandCfgs(cfgs, mPreEqChBands, mEngineSettings.preEqStage, mPreEqChCfgs);
}

RetCode DynamicsProcessingSwContext::setPostEqBandCfgs(
        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
    return setEqBandCfgs(cfgs, mPostEqChBands, mEngineSettings.postEqStage, mPostEqChCfgs);
}

RetCode DynamicsProcessingSwContext::setMbcBandCfgs(
        const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs) {
    RETURN_VALUE_IF(!mEngineSettings.mbcStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
                    "mbcNotInUse");

    RetCode ret = RetCode::SUCCESS;
    std::set<std::pair<int /* channel */, int /* band */>> bandSet;

    int bandCount = mEngineSettings.mbcStage.bandCount;
    std::vector<bool> filled(mChannelCount * bandCount, false);
    for (auto& it : cfgs) {
        if (0 != bandSet.count({it.channel, it.band})) {
            LOG(WARNING) << __func__ << " duplicated band " << it.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
        } else {
            bandSet.insert({it.channel, it.band});
        }
        if (!validateMbcBandConfig(it, mChannelCount, mEngineSettings.mbcStage.bandCount,
                                   mMbcChCfgs)) {
            LOG(WARNING) << __func__ << " skip invalid band " << it.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
            continue;
            ;
        }
        mMbcChBands[it.channel * bandCount + it.band] = it;
    }
    return ret;
}

RetCode DynamicsProcessingSwContext::setLimiterCfgs(
        const std::vector<DynamicsProcessing::LimiterConfig>& cfgs) {
    RETURN_VALUE_IF(!mEngineSettings.limiterInUse, RetCode::ERROR_ILLEGAL_PARAMETER,
                    "limiterNotInUse");

    RetCode ret = RetCode::SUCCESS;
    std::unordered_set<int> channelSet;

    for (auto& it : cfgs) {
        if (0 != channelSet.count(it.channel)) {
            LOG(WARNING) << __func__ << " duplicated channel " << it.channel;
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
        } else {
            channelSet.insert(it.channel);
        }
        if (!validateLimiterConfig(it, mChannelCount)) {
            LOG(WARNING) << __func__ << " skip invalid limiter " << it.toString();
            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
            continue;
        }
        mLimiterCfgs[it.channel] = it;
    }
    return ret;
}

void DynamicsProcessingSwContext::resizeChannels() {
    if (mPreEqChCfgs.size() != mChannelCount) {
        mPreEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
    }
    if (mPostEqChCfgs.size() != mChannelCount) {
        mPostEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
    }
    if (mMbcChCfgs.size() != mChannelCount) {
        mMbcChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
    }
    if (mLimiterCfgs.size() != mChannelCount) {
        mLimiterCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
    }
    if (mInputGainCfgs.size() != mChannelCount) {
        mInputGainCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
    }
}

void DynamicsProcessingSwContext::resizeBands() {
    if (mPreEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.preEqStage.bandCount)) {
        mPreEqChBands.resize(mChannelCount * mEngineSettings.preEqStage.bandCount,
                             {.channel = kInvalidChannelId});
    }
    if (mPostEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.postEqStage.bandCount)) {
        mPostEqChBands.resize(mChannelCount * mEngineSettings.postEqStage.bandCount,
                              {.channel = kInvalidChannelId});
    }
    if (mMbcChBands.size() != (size_t)(mChannelCount * mEngineSettings.mbcStage.bandCount)) {
        mMbcChBands.resize(mChannelCount * mEngineSettings.mbcStage.bandCount,
                           {.channel = kInvalidChannelId});
    }
}

RetCode DynamicsProcessingSwContext::setInputGainCfgs(
        const std::vector<DynamicsProcessing::InputGain>& cfgs) {
    for (const auto& cfg : cfgs) {
        RETURN_VALUE_IF(cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount,
                        RetCode::ERROR_ILLEGAL_PARAMETER, "invalidChannel");
        mInputGainCfgs[cfg.channel] = cfg;
    }
    return RetCode::SUCCESS;
}

std::vector<DynamicsProcessing::InputGain> DynamicsProcessingSwContext::getInputGainCfgs() {
    std::vector<DynamicsProcessing::InputGain> ret;
    std::copy_if(mInputGainCfgs.begin(), mInputGainCfgs.end(), std::back_inserter(ret),
                 [&](const auto& gain) { return gain.channel != kInvalidChannelId; });
    return ret;
}

bool DynamicsProcessingSwContext::validateCutoffFrequency(float freq) {
    return freq >= DynamicsProcessingSw::kCapability.minCutOffFreq &&
           freq <= DynamicsProcessingSw::kCapability.maxCutOffFreq;
}

bool DynamicsProcessingSwContext::validateStageEnablement(
        const DynamicsProcessing::StageEnablement& enablement) {
    return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0);
}

bool DynamicsProcessingSwContext::validateEngineConfig(
        const DynamicsProcessing::EngineArchitecture& engine) {
    return engine.preferredProcessingDurationMs >= 0 &&
           validateStageEnablement(engine.preEqStage) &&
           validateStageEnablement(engine.postEqStage) && validateStageEnablement(engine.mbcStage);
}

bool DynamicsProcessingSwContext::validateEqBandConfig(
        const DynamicsProcessing::EqBandConfig& band, int maxChannel, int maxBand,
        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
    return band.channel >= 0 && band.channel < maxChannel &&
           (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable &&
           band.band >= 0 && band.band < maxBand && validateCutoffFrequency(band.cutoffFrequencyHz);
}

bool DynamicsProcessingSwContext::validateMbcBandConfig(
        const DynamicsProcessing::MbcBandConfig& band, int maxChannel, int maxBand,
        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
    return band.channel >= 0 && band.channel < maxChannel &&
           (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable &&
           band.band >= 0 && band.band < maxBand &&
           validateCutoffFrequency(band.cutoffFrequencyHz) && band.attackTimeMs >= 0 &&
           band.releaseTimeMs >= 0 && band.ratio >= 0 && band.thresholdDb <= 0 &&
           band.kneeWidthDb <= 0 && band.noiseGateThresholdDb <= 0 && band.expanderRatio >= 0;
}

bool DynamicsProcessingSwContext::validateLimiterConfig(
        const DynamicsProcessing::LimiterConfig& limiter, int maxChannel) {
    return limiter.channel >= 0 && limiter.channel < maxChannel && limiter.attackTimeMs >= 0 &&
           limiter.releaseTimeMs >= 0 && limiter.ratio >= 0 && limiter.thresholdDb <= 0;
}

}  // namespace aidl::android::hardware::audio::effect
Loading