DynamicsProcessing: Add libeffects DynamicsProcessing AIDL implementation
Bug: 258124419
Test: atest VtsHalDynamicsProcessingTargetTest
Test: atest VtsHalAudioEffectTargetTest
Test: atest VtsHalAudioEffectFactoryTargetTest
Change-Id: Ia2b5c2e5b706e9b0fb47a8c49df5e5ad62e2650d
diff --git a/media/libeffects/dynamicsproc/Android.bp b/media/libeffects/dynamicsproc/Android.bp
index 84131a4..f655c4f 100644
--- a/media/libeffects/dynamicsproc/Android.bp
+++ b/media/libeffects/dynamicsproc/Android.bp
@@ -32,34 +32,68 @@
],
}
+cc_defaults {
+ name : "dynamicsprocessingdefaults",
+ srcs: [
+ "dsp/DPBase.cpp",
+ "dsp/DPFrequency.cpp",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ "libeigen",
+ ],
+ cflags: [
+ "-Wthread-safety",
+ "-Wall",
+ "-Werror",
+ ],
+}
+
cc_library_shared {
name: "libdynproc",
vendor: true,
+ defaults: [
+ "dynamicsprocessingdefaults",
+ ],
+
srcs: [
"EffectDynamicsProcessing.cpp",
- "dsp/DPBase.cpp",
- "dsp/DPFrequency.cpp",
],
cflags: [
"-O2",
"-fvisibility=hidden",
-
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libcutils",
- "liblog",
],
relative_install_path: "soundfx",
+}
- header_libs: [
- "libaudioeffects",
- "libeigen",
+cc_library_shared {
+ name: "libdynamicsprocessingaidl",
+
+ srcs: [
+ "aidl/DynamicsProcessing.cpp",
+ "aidl/DynamicsProcessingContext.cpp",
+ ":effectCommonFile",
+ ],
+
+ defaults: [
+ "aidlaudioservice_defaults",
+ "latest_android_hardware_audio_effect_ndk_shared",
+ "latest_android_media_audio_common_types_ndk_shared",
+ "dynamicsprocessingdefaults",
+ ],
+
+ visibility: [
+ "//hardware/interfaces/audio/aidl/default",
],
}
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
new file mode 100644
index 0000000..203a27b
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "AHAL_DynamicsProcessingLibEffects"
+
+#include <android-base/logging.h>
+
+#include "DynamicsProcessing.h"
+
+#include <dsp/DPBase.h>
+#include <dsp/DPFrequency.h>
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::DynamicsProcessingImpl;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::kDynamicsProcessingImplUUID;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+using aidl::android::media::audio::common::PcmType;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+ std::shared_ptr<IEffect>* instanceSpp) {
+ if (!in_impl_uuid || *in_impl_uuid != kDynamicsProcessingImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ if (instanceSpp) {
+ *instanceSpp = ndk::SharedRefBase::make<DynamicsProcessingImpl>();
+ 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 != kDynamicsProcessingImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ *_aidl_return = DynamicsProcessingImpl::kDescriptor;
+ return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+const std::string DynamicsProcessingImpl::kEffectName = "DynamicsProcessing";
+const DynamicsProcessing::Capability DynamicsProcessingImpl::kCapability = {.minCutOffFreq = 220,
+ .maxCutOffFreq = 20000};
+const Descriptor DynamicsProcessingImpl::kDescriptor = {
+ .common = {.id = {.type = kDynamicsProcessingTypeUUID,
+ .uuid = kDynamicsProcessingImplUUID,
+ .proxy = std::nullopt},
+ .flags = {.type = Flags::Type::INSERT,
+ .insert = Flags::Insert::LAST,
+ .volume = Flags::Volume::CTRL},
+ .name = DynamicsProcessingImpl::kEffectName,
+ .implementor = "The Android Open Source Project"},
+ .capability = Capability::make<Capability::dynamicsProcessing>(
+ DynamicsProcessingImpl::kCapability)};
+
+ndk::ScopedAStatus DynamicsProcessingImpl::open(const Parameter::Common& common,
+ const std::optional<Parameter::Specific>& specific,
+ OpenEffectReturn* ret) {
+ LOG(DEBUG) << __func__;
+ // effect only support 32bits float
+ RETURN_IF(common.input.base.format.pcm != common.output.base.format.pcm ||
+ common.input.base.format.pcm != PcmType::FLOAT_32_BIT,
+ EX_ILLEGAL_ARGUMENT, "dataMustBe32BitsFloat");
+ RETURN_OK_IF(mState != State::INIT);
+ auto context = createContext(common);
+ RETURN_IF(!context, EX_NULL_POINTER, "createContextFailed");
+
+ RETURN_IF_ASTATUS_NOT_OK(setParameterCommon(common), "setCommParamErr");
+ if (specific.has_value()) {
+ RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr");
+ } else {
+ Parameter::Specific defaultSpecific =
+ Parameter::Specific::make<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
+ mContext->getEngineArchitecture()));
+ RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(defaultSpecific), "setDefaultEngineErr");
+ }
+
+ mState = State::IDLE;
+ context->dupeFmq(ret);
+ RETURN_IF(createThread(context, getEffectName()) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+ "FailedToCreateWorker");
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getDescriptor(Descriptor* _aidl_return) {
+ RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
+ LOG(DEBUG) << __func__ << kDescriptor.toString();
+ *_aidl_return = kDescriptor;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::commandImpl(CommandId command) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+ switch (command) {
+ case CommandId::START:
+ mContext->enable();
+ return ndk::ScopedAStatus::ok();
+ case CommandId::STOP:
+ mContext->disable();
+ return ndk::ScopedAStatus::ok();
+ case CommandId::RESET:
+ mContext->disable();
+ mContext->resetBuffer();
+ return ndk::ScopedAStatus::ok();
+ default:
+ // Need this default handling for vendor extendable CommandId::VENDOR_COMMAND_*
+ LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "commandIdNotSupported");
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::setParameterSpecific(
+ const Parameter::Specific& specific) {
+ RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+ "EffectNotSupported");
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ auto& param = specific.get<Parameter::Specific::dynamicsProcessing>();
+ auto tag = param.getTag();
+
+ switch (tag) {
+ case DynamicsProcessing::engineArchitecture: {
+ RETURN_IF(mContext->setEngineArchitecture(
+ param.get<DynamicsProcessing::engineArchitecture>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEq: {
+ RETURN_IF(
+ mContext->setPreEq(param.get<DynamicsProcessing::preEq>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPreEqFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEq: {
+ RETURN_IF(mContext->setPostEq(param.get<DynamicsProcessing::postEq>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPostEqFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEqBand: {
+ RETURN_IF(mContext->setPreEqBand(param.get<DynamicsProcessing::preEqBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPreEqBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEqBand: {
+ RETURN_IF(mContext->setPostEqBand(param.get<DynamicsProcessing::postEqBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPostEqBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbc: {
+ RETURN_IF(mContext->setMbc(param.get<DynamicsProcessing::mbc>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMbcFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbcBand: {
+ RETURN_IF(mContext->setMbcBand(param.get<DynamicsProcessing::mbcBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMbcBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::limiter: {
+ RETURN_IF(mContext->setLimiter(param.get<DynamicsProcessing::limiter>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setLimiterFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::inputGain: {
+ RETURN_IF(mContext->setInputGain(param.get<DynamicsProcessing::inputGain>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setInputGainFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::vendorExtension: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagNotSupported");
+ }
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) {
+ RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
+ 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 ID: " << toString(dpIdTag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionIdNotSupported");
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getParameterDynamicsProcessing(
+ const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ switch (tag) {
+ case DynamicsProcessing::engineArchitecture: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
+ mContext->getEngineArchitecture()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEq: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::preEq>(mContext->getPreEq()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEq: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::postEq>(mContext->getPostEq()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEqBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+ mContext->getPreEqBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEqBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
+ mContext->getPostEqBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbc: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::mbc>(mContext->getMbc()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbcBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::mbcBand>(mContext->getMbcBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::limiter: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::limiter>(mContext->getLimiter()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::inputGain: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::inputGain>(
+ mContext->getInputGain()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::vendorExtension: {
+ LOG(ERROR) << __func__ << " wrong vendor tag in CommonTag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagInWrongId");
+ }
+ }
+}
+
+std::shared_ptr<EffectContext> DynamicsProcessingImpl::createContext(
+ const Parameter::Common& common) {
+ if (mContext) {
+ LOG(DEBUG) << __func__ << " context already exist";
+ return mContext;
+ }
+
+ mContext = std::make_shared<DynamicsProcessingContext>(1 /* statusFmqDepth */, common);
+ return mContext;
+}
+
+RetCode DynamicsProcessingImpl::releaseContext() {
+ if (mContext) {
+ mContext->disable();
+ mContext->resetBuffer();
+ mContext.reset();
+ }
+ return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status DynamicsProcessingImpl::effectProcessImpl(float* in, float* out, int samples) {
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!mContext, status, "nullContext");
+ return mContext->lvmProcess(in, out, samples);
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
new file mode 100644
index 0000000..824ebea
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+#include "DynamicsProcessingContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class DynamicsProcessingImpl final : public EffectImpl {
+ public:
+ static const std::string kEffectName;
+ static const Descriptor kDescriptor;
+ static const DynamicsProcessing::Capability kCapability;
+
+ DynamicsProcessingImpl() { LOG(DEBUG) << __func__; }
+ ~DynamicsProcessingImpl() {
+ cleanUp();
+ LOG(DEBUG) << __func__;
+ }
+
+ ndk::ScopedAStatus open(const Parameter::Common& common,
+ const std::optional<Parameter::Specific>& specific,
+ OpenEffectReturn* ret) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+ RetCode releaseContext() override;
+
+ std::shared_ptr<EffectContext> getContext() override { return mContext; }
+ std::string getEffectName() override { return kEffectName; }
+
+ private:
+ std::shared_ptr<DynamicsProcessingContext> mContext;
+ ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag,
+ Parameter::Specific* specific);
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
new file mode 100644
index 0000000..57a2be9
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "AHAL_DPLibEffectsContext"
+
+#include "DynamicsProcessing.h"
+#include "DynamicsProcessingContext.h"
+
+#include <functional>
+#include <sys/param.h>
+#include <unordered_set>
+
+namespace aidl::android::hardware::audio::effect {
+
+DynamicsProcessingContext::DynamicsProcessingContext(int statusDepth,
+ const Parameter::Common& common)
+ : EffectContext(statusDepth, common) {
+ LOG(DEBUG) << __func__;
+ init();
+}
+
+DynamicsProcessingContext::~DynamicsProcessingContext() {
+ LOG(DEBUG) << __func__;
+}
+
+RetCode DynamicsProcessingContext::enable() {
+ std::lock_guard lg(mMutex);
+ if (mState != DYNAMICS_PROCESSING_STATE_INITIALIZED) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = DYNAMICS_PROCESSING_STATE_ACTIVE;
+ return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingContext::disable() {
+ std::lock_guard lg(mMutex);
+ if (mState != DYNAMICS_PROCESSING_STATE_ACTIVE) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
+ return RetCode::SUCCESS;
+}
+
+void DynamicsProcessingContext::reset() {
+ std::lock_guard lg(mMutex);
+ if (mDpFreq != nullptr) {
+ mDpFreq.reset();
+ }
+}
+
+RetCode DynamicsProcessingContext::setCommon(const Parameter::Common& common) {
+ mCommon = common;
+ init();
+ return RetCode::SUCCESS;
+}
+
+void DynamicsProcessingContext::dpSetFreqDomainVariant_l(
+ const DynamicsProcessing::EngineArchitecture& engine) {
+ mDpFreq.reset(new dp_fx::DPFrequency());
+ mDpFreq->init(mChannelCount, engine.preEqStage.inUse, engine.preEqStage.bandCount,
+ engine.mbcStage.inUse, engine.mbcStage.bandCount, engine.postEqStage.inUse,
+ engine.postEqStage.bandCount, engine.limiterInUse);
+
+ int32_t sampleRate = mCommon.input.base.sampleRate;
+ int32_t minBlockSize = (int32_t)dp_fx::DPFrequency::getMinBockSize();
+ int32_t block = engine.preferredProcessingDurationMs * sampleRate / 1000.0f;
+ LOG(INFO) << __func__ << " sampleRate " << sampleRate << " block length "
+ << engine.preferredProcessingDurationMs << " ms (" << block << "samples)";
+ if (block < minBlockSize) {
+ block = minBlockSize;
+ } else if (!powerof2(block)) {
+ //find next highest power of 2.
+ block = 1 << (32 - __builtin_clz(block));
+ }
+ mDpFreq->configure(block, block >> 1, sampleRate);
+}
+
+RetCode DynamicsProcessingContext::setEngineArchitecture(
+ const DynamicsProcessing::EngineArchitecture& engineArchitecture) {
+ RETURN_VALUE_IF(!validateEngineConfig(engineArchitecture), RetCode::ERROR_ILLEGAL_PARAMETER,
+ "illegalEngineConfig");
+
+ std::lock_guard lg(mMutex);
+ if (!mEngineInited || mEngineArchitecture != engineArchitecture) {
+ if (engineArchitecture.resolutionPreference ==
+ DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION) {
+ dpSetFreqDomainVariant_l(engineArchitecture);
+ } else {
+ LOG(WARNING) << __func__ << toString(engineArchitecture.resolutionPreference)
+ << " not available now";
+ }
+ mEngineInited = true;
+ mEngineArchitecture = engineArchitecture;
+ }
+ LOG(INFO) << __func__ << engineArchitecture.toString();
+ return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingContext::setPreEq(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPEq>(channels, mEngineArchitecture.preEqStage.inUse,
+ StageType::PREEQ);
+}
+
+RetCode DynamicsProcessingContext::setPostEq(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPEq>(channels, mEngineArchitecture.postEqStage.inUse,
+ StageType::POSTEQ);
+}
+
+RetCode DynamicsProcessingContext::setMbc(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPMbc>(channels, mEngineArchitecture.mbcStage.inUse,
+ StageType::MBC);
+}
+
+RetCode DynamicsProcessingContext::setPreEqBand(
+ const std::vector<DynamicsProcessing::EqBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.postEqStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "postEqNotInUse");
+ return setBands_l<DynamicsProcessing::EqBandConfig>(
+ bands, mEngineArchitecture.preEqStage.bandCount, StageType::PREEQ);
+}
+
+RetCode DynamicsProcessingContext::setPostEqBand(
+ const std::vector<DynamicsProcessing::EqBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.postEqStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "postEqNotInUse");
+ return setBands_l<DynamicsProcessing::EqBandConfig>(
+ bands, mEngineArchitecture.postEqStage.bandCount, StageType::POSTEQ);
+}
+
+RetCode DynamicsProcessingContext::setMbcBand(
+ const std::vector<DynamicsProcessing::MbcBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.mbcStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "mbcNotInUse");
+ return setBands_l<DynamicsProcessing::MbcBandConfig>(
+ bands, mEngineArchitecture.preEqStage.bandCount, StageType::MBC);
+}
+
+RetCode DynamicsProcessingContext::setLimiter(
+ const std::vector<DynamicsProcessing::LimiterConfig>& limiters) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.limiterInUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "limiterNotInUse");
+ return setBands_l<DynamicsProcessing::LimiterConfig>(limiters, -1, StageType::LIMITER);
+}
+
+RetCode DynamicsProcessingContext::setInputGain(
+ const std::vector<DynamicsProcessing::InputGain>& inputGains) {
+ std::lock_guard lg(mMutex);
+ return setBands_l<DynamicsProcessing::InputGain>(inputGains, -1, StageType::INPUTGAIN);
+}
+
+DynamicsProcessing::EngineArchitecture DynamicsProcessingContext::getEngineArchitecture() {
+ std::lock_guard lg(mMutex);
+ LOG(INFO) << __func__ << mEngineArchitecture.toString();
+ return mEngineArchitecture;
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getPreEq() {
+ return getChannelConfig(StageType::PREEQ);
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getPostEq() {
+ return getChannelConfig(StageType::POSTEQ);
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getPreEqBand() {
+ return getEqBandConfigs(StageType::PREEQ);
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getPostEqBand() {
+ return getEqBandConfigs(StageType::POSTEQ);
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getMbc() {
+ return getChannelConfig(StageType::MBC);
+}
+
+std::vector<DynamicsProcessing::MbcBandConfig> DynamicsProcessingContext::getMbcBand() {
+ std::vector<DynamicsProcessing::MbcBandConfig> bands;
+
+ std::lock_guard lg(mMutex);
+ auto maxBand = mEngineArchitecture.mbcStage.bandCount;
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto mbc = getMbc_l(ch);
+ if (!mbc) {
+ continue;
+ }
+ for (int32_t bandId = 0; bandId < maxBand; bandId++) {
+ auto band = mbc->getBand(bandId);
+ if (!band) {
+ continue;
+ }
+ bands.push_back({.channel = ch,
+ .band = bandId,
+ .enable = band->isEnabled(),
+ .cutoffFrequencyHz = band->getCutoffFrequency(),
+ .attackTimeMs = band->getAttackTime(),
+ .releaseTimeMs = band->getReleaseTime(),
+ .ratio = band->getRatio(),
+ .thresholdDb = band->getThreshold(),
+ .kneeWidthDb = band->getKneeWidth(),
+ .noiseGateThresholdDb = band->getNoiseGateThreshold(),
+ .expanderRatio = band->getExpanderRatio(),
+ .preGainDb = band->getPreGain(),
+ .postGainDb = band->getPostGain()});
+ }
+ }
+ return bands;
+}
+
+std::vector<DynamicsProcessing::LimiterConfig> DynamicsProcessingContext::getLimiter() {
+ std::vector<DynamicsProcessing::LimiterConfig> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto limiter = getLimiter_l(ch);
+ if (!limiter) {
+ continue;
+ }
+ ret.push_back({.channel = ch,
+ .enable = limiter->isEnabled(),
+ .linkGroup = static_cast<int32_t>(limiter->getLinkGroup()),
+ .attackTimeMs = limiter->getAttackTime(),
+ .releaseTimeMs = limiter->getReleaseTime(),
+ .ratio = limiter->getRatio(),
+ .thresholdDb = limiter->getThreshold(),
+ .postGainDb = limiter->getPostGain()});
+ }
+ return ret;
+}
+
+std::vector<DynamicsProcessing::InputGain> DynamicsProcessingContext::getInputGain() {
+ std::vector<DynamicsProcessing::InputGain> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto channel = getChannel_l(ch);
+ if (!channel) {
+ continue;
+ }
+ ret.push_back({.channel = ch, .gainDb = channel->getInputGain()});
+ }
+ return ret;
+}
+
+IEffect::Status DynamicsProcessingContext::lvmProcess(float* in, float* out, int samples) {
+ LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
+
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!in, status, "nullInput");
+ RETURN_VALUE_IF(!out, status, "nullOutput");
+ status = {EX_ILLEGAL_STATE, 0, 0};
+
+ LOG(DEBUG) << __func__ << " start processing";
+ {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(mState != DynamicsProcessingState::DYNAMICS_PROCESSING_STATE_ACTIVE, status,
+ "notInActiveState");
+ RETURN_VALUE_IF(!mDpFreq, status, "engineNotInited");
+ mDpFreq->processSamples(in, out, samples);
+ }
+ return {STATUS_OK, samples, samples};
+}
+
+void DynamicsProcessingContext::init() {
+ std::lock_guard lg(mMutex);
+ mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
+ mChannelCount =
+ ::android::hardware::audio::common::getChannelCount(mCommon.input.base.channelMask);
+}
+
+dp_fx::DPChannel* DynamicsProcessingContext::getChannel_l(int channel) {
+ RETURN_VALUE_IF(mDpFreq == nullptr, nullptr, "DPFreqNotInited");
+
+ return mDpFreq->getChannel(channel);
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getPreEq_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getPreEq();
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getPostEq_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getPostEq();
+}
+
+dp_fx::DPMbc* DynamicsProcessingContext::getMbc_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getMbc();
+}
+
+dp_fx::DPLimiter* DynamicsProcessingContext::getLimiter_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getLimiter();
+}
+
+dp_fx::DPBandStage* DynamicsProcessingContext::getStageWithType_l(
+ DynamicsProcessingContext::StageType type, int ch) {
+ switch (type) {
+ case StageType::PREEQ: {
+ return getEqWithType_l(type, ch);
+ }
+ case StageType::POSTEQ: {
+ return getEqWithType_l(type, ch);
+ }
+ case StageType::MBC: {
+ return getMbc_l(ch);
+ }
+ case StageType::LIMITER:
+ FALLTHROUGH_INTENDED;
+ case StageType::INPUTGAIN: {
+ return nullptr;
+ }
+ }
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getEqWithType_l(DynamicsProcessingContext::StageType type,
+ int ch) {
+ switch (type) {
+ case StageType::PREEQ: {
+ return getPreEq_l(ch);
+ }
+ case StageType::POSTEQ: {
+ return getPostEq_l(ch);
+ }
+ case StageType::MBC:
+ FALLTHROUGH_INTENDED;
+ case StageType::LIMITER:
+ FALLTHROUGH_INTENDED;
+ case StageType::INPUTGAIN: {
+ return nullptr;
+ }
+ }
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getChannelConfig(
+ StageType type) {
+ std::vector<DynamicsProcessing::ChannelConfig> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto stage = getStageWithType_l(type, ch);
+ if (!stage) {
+ continue;
+ }
+ ret.push_back({.channel = ch, .enable = stage->isEnabled()});
+ }
+ return ret;
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getEqBandConfigs(
+ StageType type) {
+ std::vector<DynamicsProcessing::EqBandConfig> eqBands;
+
+ std::lock_guard lg(mMutex);
+ auto maxBand = mEngineArchitecture.preEqStage.bandCount;
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto eq = getEqWithType_l(type, ch);
+ if (!eq) {
+ continue;
+ }
+ for (int32_t bandId = 0; bandId < maxBand; bandId++) {
+ auto band = eq->getBand(bandId);
+ if (!band) {
+ continue;
+ }
+ eqBands.push_back({.channel = ch,
+ .band = bandId,
+ .enable = band->isEnabled(),
+ .cutoffFrequencyHz = band->getCutoffFrequency(),
+ .gainDb = band->getGain()});
+ }
+ }
+ return eqBands;
+}
+
+/**
+ * When StageEnablement is in use, bandCount needs to be positive.
+ */
+bool DynamicsProcessingContext::validateStageEnablement(
+ const DynamicsProcessing::StageEnablement& enablement) {
+ return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0);
+}
+
+bool DynamicsProcessingContext::validateEngineConfig(
+ const DynamicsProcessing::EngineArchitecture& engine) {
+ return engine.preferredProcessingDurationMs >= 0 &&
+ validateStageEnablement(engine.preEqStage) &&
+ validateStageEnablement(engine.postEqStage) && validateStageEnablement(engine.mbcStage);
+}
+
+inline bool DynamicsProcessingContext::validateCutoffFrequency(float freq) {
+ return freq >= DynamicsProcessingImpl::kCapability.minCutOffFreq &&
+ freq <= DynamicsProcessingImpl::kCapability.maxCutOffFreq;
+}
+
+bool DynamicsProcessingContext::validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band,
+ int maxChannel, int maxBand) {
+ return validateChannel(band.channel, maxChannel) && validateBand(band.band, maxBand) &&
+ validateCutoffFrequency(band.cutoffFrequencyHz);
+}
+
+bool DynamicsProcessingContext::validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band,
+ int maxChannel, int maxBand) {
+ return validateChannel(band.channel, maxChannel) && validateBand(band.band, maxBand) &&
+ validateCutoffFrequency(band.cutoffFrequencyHz) && validateTime(band.attackTimeMs) &&
+ validateTime(band.releaseTimeMs) && validateRatio(band.ratio) &&
+ validateBandDb(band.thresholdDb) && validateBandDb(band.kneeWidthDb) &&
+ validateBandDb(band.noiseGateThresholdDb) && validateRatio(band.expanderRatio);
+}
+
+bool DynamicsProcessingContext::validateLimiterConfig(
+ const DynamicsProcessing::LimiterConfig& limiter, int maxChannel) {
+ return validateChannel(limiter.channel, maxChannel) && validateTime(limiter.attackTimeMs) &&
+ validateTime(limiter.releaseTimeMs) && validateRatio(limiter.ratio) &&
+ validateBandDb(limiter.thresholdDb);
+}
+
+bool DynamicsProcessingContext::validateInputGainConfig(const DynamicsProcessing::InputGain& gain,
+ int maxChannel) {
+ return validateChannel(gain.channel, maxChannel);
+}
+
+template <typename D>
+RetCode DynamicsProcessingContext::setDpChannels_l(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels, bool stageInUse,
+ StageType type) {
+ RetCode ret = RetCode::SUCCESS;
+ std::unordered_set<int> channelSet;
+
+ RETURN_VALUE_IF(!stageInUse, RetCode::ERROR_ILLEGAL_PARAMETER, "stageNotInUse");
+ for (auto& it : channels) {
+ if (0 != channelSet.count(it.channel)) {
+ LOG(WARNING) << __func__ << " duplicated channel " << it.channel;
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ } else {
+ channelSet.insert(it.channel);
+ }
+ if (it.channel < 0 || it.channel >= mChannelCount) {
+ LOG(WARNING) << __func__ << " skip illegal ChannelConfig " << it.toString() << " max "
+ << mChannelCount;
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ auto dp = getStageWithType_l(type, it.channel);
+ if (!dp) {
+ LOG(WARNING) << __func__ << " channel " << it.channel << " not exist";
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ if (dp->isEnabled() != it.enable) {
+ LOG(INFO) << __func__ << it.toString();
+ dp->setEnabled(it.enable);
+ }
+ }
+ return ret;
+}
+
+RetCode DynamicsProcessingContext::setDpChannelBand_l(const std::any& anyConfig, StageType type,
+ int maxCh, int maxBand,
+ std::set<std::pair<int, int>>& chBandSet) {
+ RETURN_VALUE_IF(!anyConfig.has_value(), RetCode::ERROR_ILLEGAL_PARAMETER, "bandInvalid");
+ RetCode ret = RetCode::SUCCESS;
+ std::pair<int, int> chBandKey;
+ switch (type) {
+ case StageType::PREEQ:
+ FALLTHROUGH_INTENDED;
+ case StageType::POSTEQ: {
+ dp_fx::DPEq* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::EqBandConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateEqBandConfig(config, maxCh, maxBand),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "eqBandNotValid");
+ RETURN_VALUE_IF(
+ nullptr == (dp = getEqWithType_l(type, config.channel)) || !dp->isEnabled(),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpEqNotExist");
+ dp_fx::DPEqBand band;
+ band.init(config.enable, config.cutoffFrequencyHz, config.gainDb);
+ dp->setBand(config.band, band);
+ chBandKey = {config.channel, config.band};
+ break;
+ }
+ case StageType::MBC: {
+ dp_fx::DPMbc* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::MbcBandConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateMbcBandConfig(config, maxCh, maxBand),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "mbcBandNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getMbc_l(config.channel)) || !dp->isEnabled(),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpMbcNotExist");
+ dp_fx::DPMbcBand band;
+ band.init(config.enable, config.cutoffFrequencyHz, config.attackTimeMs,
+ config.releaseTimeMs, config.ratio, config.thresholdDb, config.kneeWidthDb,
+ config.noiseGateThresholdDb, config.expanderRatio, config.preGainDb,
+ config.postGainDb);
+ dp->setBand(config.band, band);
+ chBandKey = {config.channel, config.band};
+ break;
+ }
+ case StageType::LIMITER: {
+ dp_fx::DPChannel* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::LimiterConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateLimiterConfig(config, maxCh),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "limiterBandNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getChannel_l(config.channel)),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpChNotExist");
+ dp_fx::DPLimiter limiter;
+ limiter.init(mEngineArchitecture.limiterInUse, config.enable, config.linkGroup,
+ config.attackTimeMs, config.releaseTimeMs, config.ratio,
+ config.thresholdDb, config.postGainDb);
+ dp->setLimiter(limiter);
+ chBandKey = {config.channel, 0};
+ break;
+ }
+ case StageType::INPUTGAIN: {
+ dp_fx::DPChannel* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::InputGain>(anyConfig);
+ RETURN_VALUE_IF(!validateInputGainConfig(config, maxCh),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "inputGainNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getChannel_l(config.channel)),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpChNotExist");
+ dp->setInputGain(config.gainDb);
+ chBandKey = {config.channel, 0};
+ break;
+ }
+ }
+ RETURN_VALUE_IF(0 != chBandSet.count(chBandKey), RetCode::ERROR_ILLEGAL_PARAMETER,
+ "duplicatedBand");
+ chBandSet.insert(chBandKey);
+ return ret;
+}
+
+template <typename T /* BandConfig */>
+RetCode DynamicsProcessingContext::setBands_l(
+ const std::vector<T>& bands, int maxBand, StageType type) {
+ RetCode ret = RetCode::SUCCESS;
+ std::set<std::pair<int /* channel */, int /* band */>> bandSet;
+
+ for (const auto& it : bands) {
+ if (RetCode::SUCCESS !=
+ setDpChannelBand_l(std::make_any<T>(it), type, mChannelCount, maxBand, bandSet)) {
+ LOG(WARNING) << __func__ << " skipping band " << it.toString();
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ LOG(INFO) << __func__ << it.toString();
+ }
+ return ret;
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
new file mode 100644
index 0000000..8be784e
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <audio_effects/effect_dynamicsprocessing.h>
+
+#include "effect-impl/EffectContext.h"
+
+#include <any>
+#include <cstddef>
+#include <dsp/DPBase.h>
+#include <dsp/DPFrequency.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+enum DynamicsProcessingState {
+ DYNAMICS_PROCESSING_STATE_UNINITIALIZED,
+ DYNAMICS_PROCESSING_STATE_INITIALIZED,
+ DYNAMICS_PROCESSING_STATE_ACTIVE,
+};
+
+class DynamicsProcessingContext final : public EffectContext {
+ public:
+ DynamicsProcessingContext(int statusDepth, const Parameter::Common& common);
+ ~DynamicsProcessingContext();
+
+ RetCode enable();
+ RetCode disable();
+ void reset();
+
+ // override EffectContext::setCommon to update mChannelCount
+ RetCode setCommon(const Parameter::Common& common) override;
+
+ RetCode setEngineArchitecture(const DynamicsProcessing::EngineArchitecture& engineArchitecture);
+ RetCode setPreEq(const std::vector<DynamicsProcessing::ChannelConfig>& eqChannels);
+ RetCode setPostEq(const std::vector<DynamicsProcessing::ChannelConfig>& eqChannels);
+ RetCode setPreEqBand(const std::vector<DynamicsProcessing::EqBandConfig>& eqBands);
+ RetCode setPostEqBand(const std::vector<DynamicsProcessing::EqBandConfig>& eqBands);
+ RetCode setMbc(const std::vector<DynamicsProcessing::ChannelConfig>& mbcChannels);
+ RetCode setMbcBand(const std::vector<DynamicsProcessing::MbcBandConfig>& eqBands);
+ RetCode setLimiter(const std::vector<DynamicsProcessing::LimiterConfig>& limiters);
+ RetCode setInputGain(const std::vector<DynamicsProcessing::InputGain>& gain);
+
+ DynamicsProcessing::EngineArchitecture getEngineArchitecture();
+ std::vector<DynamicsProcessing::ChannelConfig> getPreEq();
+ std::vector<DynamicsProcessing::ChannelConfig> getPostEq();
+ std::vector<DynamicsProcessing::EqBandConfig> getPreEqBand();
+ std::vector<DynamicsProcessing::EqBandConfig> getPostEqBand();
+ std::vector<DynamicsProcessing::ChannelConfig> getMbc();
+ std::vector<DynamicsProcessing::MbcBandConfig> getMbcBand();
+ std::vector<DynamicsProcessing::LimiterConfig> getLimiter();
+ std::vector<DynamicsProcessing::InputGain> getInputGain();
+
+ IEffect::Status lvmProcess(float* in, float* out, int samples);
+
+ private:
+ static constexpr float kPreferredProcessingDurationMs = 10.0f;
+ static constexpr int kBandCount = 5;
+ std::mutex mMutex;
+ size_t mChannelCount GUARDED_BY(mMutex) = 0;
+ DynamicsProcessingState mState GUARDED_BY(mMutex) = DYNAMICS_PROCESSING_STATE_UNINITIALIZED;
+ std::unique_ptr<dp_fx::DPFrequency> mDpFreq GUARDED_BY(mMutex) = nullptr;
+ bool mEngineInited GUARDED_BY(mMutex) = false;
+ DynamicsProcessing::EngineArchitecture mEngineArchitecture GUARDED_BY(mMutex) = {
+ .resolutionPreference =
+ DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
+ .preferredProcessingDurationMs = kPreferredProcessingDurationMs,
+ .preEqStage = {.inUse = true, .bandCount = kBandCount},
+ .postEqStage = {.inUse = true, .bandCount = kBandCount},
+ .mbcStage = {.inUse = true, .bandCount = kBandCount},
+ .limiterInUse = true,
+ };
+
+ enum class StageType { PREEQ, POSTEQ, MBC, LIMITER, INPUTGAIN };
+
+ void init();
+
+ void dpSetFreqDomainVariant_l(const DynamicsProcessing::EngineArchitecture& engine)
+ REQUIRES(mMutex);
+ dp_fx::DPChannel* getChannel_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getPreEq_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getPostEq_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPMbc* getMbc_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPLimiter* getLimiter_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPBandStage* getStageWithType_l(StageType type, int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getEqWithType_l(StageType type, int ch) REQUIRES(mMutex);
+ template <typename D>
+ RetCode setDpChannels_l(const std::vector<DynamicsProcessing::ChannelConfig>& channels,
+ bool stageInUse, StageType type) REQUIRES(mMutex);
+ template <typename T /* BandConfig */>
+ RetCode setBands_l(const std::vector<T>& bands, int maxBand, StageType type) REQUIRES(mMutex);
+ RetCode setDpChannelBand_l(const std::any& anyConfig, StageType type, int maxCh, int maxBand,
+ std::set<std::pair<int, int>>& chBandSet) REQUIRES(mMutex);
+
+ std::vector<DynamicsProcessing::EqBandConfig> getEqBandConfigs(StageType type);
+ std::vector<DynamicsProcessing::ChannelConfig> getChannelConfig(StageType type);
+
+ bool validateStageEnablement(const DynamicsProcessing::StageEnablement& enablement);
+ bool validateEngineConfig(const DynamicsProcessing::EngineArchitecture& engine);
+ bool validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band, int maxChannel,
+ int maxBand);
+ bool validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band, int maxChannel,
+ int maxBand);
+ bool validateLimiterConfig(const DynamicsProcessing::LimiterConfig& limiter, int maxChannel);
+ bool validateInputGainConfig(const DynamicsProcessing::InputGain& gain, int maxChannel);
+
+ inline bool validateCutoffFrequency(float freq);
+ inline bool validateChannel(int ch, int maxCh) { return ch >= 0 && ch < maxCh; }
+ inline bool validateBand(int band, int maxBand) { return band >= 0 && band < maxBand; }
+ inline bool validateTime(int time) { return time >= 0; }
+ inline bool validateRatio(int ratio) { return ratio >= 0; }
+ inline bool validateBandDb(int db) { return db <= 0; }
+};
+
+} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file