/*
 * Copyright (C) 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.
 */

#include <algorithm>
#include <cstddef>
#include <set>
#include <unordered_set>

#define LOG_TAG "AHAL_DynamicsProcessingSw"
#include <android-base/logging.h>
#include <fmq/AidlMessageQueue.h>
#include <system/audio_effects/effect_uuid.h>

#include "DynamicsProcessingSw.h"

using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::DynamicsProcessingSw;
using aidl::android::hardware::audio::effect::getEffectImplUuidDynamicsProcessingSw;
using aidl::android::hardware::audio::effect::getEffectTypeUuidDynamicsProcessing;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::State;
using aidl::android::media::audio::common::AudioUuid;

extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
                                           std::shared_ptr<IEffect>* instanceSpp) {
    if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDynamicsProcessingSw()) {
        LOG(ERROR) << __func__ << "uuid not supported";
        return EX_ILLEGAL_ARGUMENT;
    }
    if (instanceSpp) {
        *instanceSpp = ndk::SharedRefBase::make<DynamicsProcessingSw>();
        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
        return EX_NONE;
    } else {
        LOG(ERROR) << __func__ << " invalid input parameter!";
        return EX_ILLEGAL_ARGUMENT;
    }
}

extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
    if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDynamicsProcessingSw()) {
        LOG(ERROR) << __func__ << "uuid not supported";
        return EX_ILLEGAL_ARGUMENT;
    }
    *_aidl_return = DynamicsProcessingSw::kDescriptor;
    return EX_NONE;
}

namespace aidl::android::hardware::audio::effect {

const std::string DynamicsProcessingSw::kEffectName = "DynamicsProcessingSw";
const DynamicsProcessing::EqBandConfig DynamicsProcessingSw::kEqBandConfigMin =
        DynamicsProcessing::EqBandConfig({.channel = 0,
                                          .band = 0,
                                          .enable = false,
                                          .cutoffFrequencyHz = 220,
                                          .gainDb = std::numeric_limits<float>::min()});
const DynamicsProcessing::EqBandConfig DynamicsProcessingSw::kEqBandConfigMax =
        DynamicsProcessing::EqBandConfig({.channel = std::numeric_limits<int>::max(),
                                          .band = std::numeric_limits<int>::max(),
                                          .enable = true,
                                          .cutoffFrequencyHz = 20000,
                                          .gainDb = std::numeric_limits<float>::max()});
const Range::DynamicsProcessingRange DynamicsProcessingSw::kPreEqBandRange = {
        .min = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
                {DynamicsProcessingSw::kEqBandConfigMin}),
        .max = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
                {DynamicsProcessingSw::kEqBandConfigMax})};
const Range::DynamicsProcessingRange DynamicsProcessingSw::kPostEqBandRange = {
        .min = DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
                {DynamicsProcessingSw::kEqBandConfigMin}),
        .max = DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
                {DynamicsProcessingSw::kEqBandConfigMax})};

const std::vector<Range::DynamicsProcessingRange> DynamicsProcessingSw::kRanges = {
        DynamicsProcessingSw::kPreEqBandRange, DynamicsProcessingSw::kPostEqBandRange};
const Capability DynamicsProcessingSw::kCapability = {.range = DynamicsProcessingSw::kRanges};

const Descriptor DynamicsProcessingSw::kDescriptor = {
        .common = {.id = {.type = getEffectTypeUuidDynamicsProcessing(),
                          .uuid = getEffectImplUuidDynamicsProcessingSw(),
                          .proxy = std::nullopt},
                   .flags = {.type = Flags::Type::POST_PROC,
                             .insert = Flags::Insert::FIRST,
                             .volume = Flags::Volume::CTRL},
                   .name = DynamicsProcessingSw::kEffectName,
                   .implementor = "The Android Open Source Project"},
        .capability = DynamicsProcessingSw::kCapability};

ndk::ScopedAStatus DynamicsProcessingSw::getDescriptor(Descriptor* _aidl_return) {
    LOG(DEBUG) << __func__ << kDescriptor.toString();
    *_aidl_return = kDescriptor;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus DynamicsProcessingSw::setParameterSpecific(const Parameter::Specific& specific) {
    RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
              "EffectNotSupported");

    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::vendor: {
            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");
    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::vendor: {
            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();
}

std::shared_ptr<EffectContext> DynamicsProcessingSw::createContext(
        const Parameter::Common& common) {
    if (mContext) {
        LOG(DEBUG) << __func__ << " context already exist";
    } else {
        mContext = std::make_shared<DynamicsProcessingSwContext>(1 /* statusFmqDepth */, common);
    }
    return mContext;
}

RetCode DynamicsProcessingSw::releaseContext() {
    if (mContext) {
        mContext.reset();
    }
    return RetCode::SUCCESS;
}

// Processing method running in EffectWorker thread.
IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, int samples) {
    // TODO: get data buffer and process.
    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples;
    for (int i = 0; i < samples; i++) {
        *out++ = *in++;
    }
    return {STATUS_OK, samples, samples};
}

RetCode DynamicsProcessingSwContext::setCommon(const Parameter::Common& common) {
    if (auto ret = updateIOFrameSize(common); ret != RetCode::SUCCESS) {
        return ret;
    }
    mCommon = common;
    mChannelCount = ::aidl::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::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;
}

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 && 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
