1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "AHAL_DynamicsProcessingLibEffects"
18
19 #include <android-base/logging.h>
20 #include <system/audio_effects/effect_uuid.h>
21
22 #include "DynamicsProcessing.h"
23
24 #include <dsp/DPBase.h>
25 #include <dsp/DPFrequency.h>
26
27 using aidl::android::hardware::audio::effect::Descriptor;
28 using aidl::android::hardware::audio::effect::DynamicsProcessingImpl;
29 using aidl::android::hardware::audio::effect::getEffectImplUuidDynamicsProcessing;
30 using aidl::android::hardware::audio::effect::getEffectTypeUuidDynamicsProcessing;
31 using aidl::android::hardware::audio::effect::IEffect;
32 using aidl::android::hardware::audio::effect::State;
33 using aidl::android::media::audio::common::AudioUuid;
34 using aidl::android::media::audio::common::PcmType;
35
createEffect(const AudioUuid * in_impl_uuid,std::shared_ptr<IEffect> * instanceSpp)36 extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
37 std::shared_ptr<IEffect>* instanceSpp) {
38 if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDynamicsProcessing()) {
39 LOG(ERROR) << __func__ << "uuid not supported";
40 return EX_ILLEGAL_ARGUMENT;
41 }
42 if (instanceSpp) {
43 *instanceSpp = ndk::SharedRefBase::make<DynamicsProcessingImpl>();
44 return EX_NONE;
45 } else {
46 LOG(ERROR) << __func__ << " invalid input parameter!";
47 return EX_ILLEGAL_ARGUMENT;
48 }
49 }
50
queryEffect(const AudioUuid * in_impl_uuid,Descriptor * _aidl_return)51 extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
52 if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDynamicsProcessing()) {
53 LOG(ERROR) << __func__ << "uuid not supported";
54 return EX_ILLEGAL_ARGUMENT;
55 }
56 *_aidl_return = DynamicsProcessingImpl::kDescriptor;
57 return EX_NONE;
58 }
59
60 namespace aidl::android::hardware::audio::effect {
61
62 const std::string DynamicsProcessingImpl::kEffectName = "DynamicsProcessing";
63
64 static const Range::DynamicsProcessingRange kEngineConfigRange = {
65 .min = DynamicsProcessing::make<
66 DynamicsProcessing::engineArchitecture>(DynamicsProcessing::EngineArchitecture(
67 {.resolutionPreference =
68 DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
69 .preferredProcessingDurationMs = 1.0f,
70 .preEqStage = {.inUse = false, .bandCount = 0},
71 .postEqStage = {.inUse = false, .bandCount = 0},
72 .mbcStage = {.inUse = false, .bandCount = 0},
73 .limiterInUse = false})),
74 .max = DynamicsProcessing::make<
75 DynamicsProcessing::engineArchitecture>(DynamicsProcessing::EngineArchitecture(
76 {.resolutionPreference =
77 DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
78 .preferredProcessingDurationMs = 1000.0f,
79 .preEqStage = {.inUse = true, .bandCount = 128},
80 .postEqStage = {.inUse = true, .bandCount = 128},
81 .mbcStage = {.inUse = true, .bandCount = 128},
82 .limiterInUse = true}))};
83
84 static const DynamicsProcessing::ChannelConfig kChannelConfigMin =
85 DynamicsProcessing::ChannelConfig({.channel = 0, .enable = false});
86
87 static const DynamicsProcessing::ChannelConfig kChannelConfigMax =
88 DynamicsProcessing::ChannelConfig(
89 {.channel = std::numeric_limits<int>::max(), .enable = true});
90
91 static const Range::DynamicsProcessingRange kPreEqChannelConfigRange = {
92 .min = DynamicsProcessing::make<DynamicsProcessing::preEq>({kChannelConfigMin}),
93 .max = DynamicsProcessing::make<DynamicsProcessing::preEq>({kChannelConfigMax})};
94
95 static const Range::DynamicsProcessingRange kPostEqChannelConfigRange = {
96 .min = DynamicsProcessing::make<DynamicsProcessing::postEq>({kChannelConfigMin}),
97 .max = DynamicsProcessing::make<DynamicsProcessing::postEq>({kChannelConfigMax})};
98
99 static const Range::DynamicsProcessingRange kMbcChannelConfigRange = {
100 .min = DynamicsProcessing::make<DynamicsProcessing::mbc>({kChannelConfigMin}),
101 .max = DynamicsProcessing::make<DynamicsProcessing::mbc>({kChannelConfigMax})};
102
103 static const DynamicsProcessing::EqBandConfig kEqBandConfigMin =
104 DynamicsProcessing::EqBandConfig({.channel = 0,
105 .band = 0,
106 .enable = false,
107 .cutoffFrequencyHz = 0,
108 .gainDb = -200});
109
110 static const DynamicsProcessing::EqBandConfig kEqBandConfigMax =
111 DynamicsProcessing::EqBandConfig({.channel = std::numeric_limits<int>::max(),
112 .band = std::numeric_limits<int>::max(),
113 .enable = true,
114 .cutoffFrequencyHz = 192000,
115 .gainDb = 200});
116
117 static const Range::DynamicsProcessingRange kPreEqBandConfigRange = {
118 .min = DynamicsProcessing::make<DynamicsProcessing::preEqBand>({kEqBandConfigMin}),
119 .max = DynamicsProcessing::make<DynamicsProcessing::preEqBand>({kEqBandConfigMax})};
120
121 static const Range::DynamicsProcessingRange kPostEqBandConfigRange = {
122 .min = DynamicsProcessing::make<DynamicsProcessing::postEqBand>({kEqBandConfigMin}),
123 .max = DynamicsProcessing::make<DynamicsProcessing::postEqBand>({kEqBandConfigMax})};
124
125 static const Range::DynamicsProcessingRange kMbcBandConfigRange = {
126 .min = DynamicsProcessing::make<DynamicsProcessing::mbcBand>(
127 {DynamicsProcessing::MbcBandConfig(
128 {.channel = 0,
129 .band = 0,
130 .enable = false,
131 .cutoffFrequencyHz = 0,
132 .attackTimeMs = 0,
133 .releaseTimeMs = 0,
134 .ratio = 1,
135 .thresholdDb = -200,
136 .kneeWidthDb = 0,
137 .noiseGateThresholdDb = -200,
138 .expanderRatio = 1,
139 .preGainDb = -200,
140 .postGainDb = -200})}),
141 .max = DynamicsProcessing::make<DynamicsProcessing::mbcBand>(
142 {DynamicsProcessing::MbcBandConfig(
143 {.channel = std::numeric_limits<int>::max(),
144 .band = std::numeric_limits<int>::max(),
145 .enable = true,
146 .cutoffFrequencyHz = 192000,
147 .attackTimeMs = 60000,
148 .releaseTimeMs = 60000,
149 .ratio = 50,
150 .thresholdDb = 200,
151 .kneeWidthDb = 100,
152 .noiseGateThresholdDb = 200,
153 .expanderRatio = 50,
154 .preGainDb = 200,
155 .postGainDb = 200})})};
156
157 static const Range::DynamicsProcessingRange kInputGainRange = {
158 .min = DynamicsProcessing::make<DynamicsProcessing::inputGain>(
159 {DynamicsProcessing::InputGain(
160 {.channel = 0, .gainDb = -200.0f})}),
161 .max = DynamicsProcessing::make<DynamicsProcessing::inputGain>(
162 {DynamicsProcessing::InputGain({.channel = std::numeric_limits<int>::max(),
163 .gainDb = 200.0f})})};
164
165 static const Range::DynamicsProcessingRange kLimiterRange = {
166 .min = DynamicsProcessing::make<DynamicsProcessing::limiter>(
167 {DynamicsProcessing::LimiterConfig(
168 {.channel = 0,
169 .enable = false,
170 .linkGroup = std::numeric_limits<int>::min(),
171 .attackTimeMs = 0,
172 .releaseTimeMs = 0,
173 .ratio = 1,
174 .thresholdDb = -200,
175 .postGainDb = -200})}),
176 .max = DynamicsProcessing::make<DynamicsProcessing::limiter>(
177 {DynamicsProcessing::LimiterConfig(
178 {.channel = std::numeric_limits<int>::max(),
179 .enable = true,
180 .linkGroup = std::numeric_limits<int>::max(),
181 .attackTimeMs = 60000,
182 .releaseTimeMs = 60000,
183 .ratio = 50,
184 .thresholdDb = 200,
185 .postGainDb = 200})})};
186
187 const std::vector<Range::DynamicsProcessingRange> kRanges = {
188 kEngineConfigRange, kPreEqChannelConfigRange, kPostEqChannelConfigRange,
189 kMbcChannelConfigRange, kPreEqBandConfigRange, kPostEqBandConfigRange,
190 kMbcBandConfigRange, kInputGainRange, kLimiterRange};
191
192 const Capability DynamicsProcessingImpl::kCapability = {.range = kRanges};
193
194 const Descriptor DynamicsProcessingImpl::kDescriptor = {
195 .common = {.id = {.type = getEffectTypeUuidDynamicsProcessing(),
196 .uuid = getEffectImplUuidDynamicsProcessing(),
197 .proxy = std::nullopt},
198 .flags = {.type = Flags::Type::INSERT,
199 .insert = Flags::Insert::LAST,
200 .volume = Flags::Volume::CTRL},
201 .name = DynamicsProcessingImpl::kEffectName,
202 .implementor = "The Android Open Source Project"},
203 .capability = DynamicsProcessingImpl::kCapability};
204
open(const Parameter::Common & common,const std::optional<Parameter::Specific> & specific,OpenEffectReturn * ret)205 ndk::ScopedAStatus DynamicsProcessingImpl::open(const Parameter::Common& common,
206 const std::optional<Parameter::Specific>& specific,
207 OpenEffectReturn* ret) {
208 // effect only support 32bits float
209 RETURN_IF(common.input.base.format.pcm != common.output.base.format.pcm ||
210 common.input.base.format.pcm != PcmType::FLOAT_32_BIT,
211 EX_ILLEGAL_ARGUMENT, "dataMustBe32BitsFloat");
212 std::lock_guard lg(mImplMutex);
213 RETURN_OK_IF(mState != State::INIT);
214 mImplContext = createContext(common);
215 RETURN_IF(!mContext || !mImplContext, EX_NULL_POINTER, "createContextFailed");
216 RETURN_IF(!getInterfaceVersion(&mVersion).isOk(), EX_UNSUPPORTED_OPERATION,
217 "FailedToGetInterfaceVersion");
218 mImplContext->setVersion(version);
219 mEventFlag = mImplContext->getStatusEventFlag();
220 mDataMqNotEmptyEf =
221 mVersion >= kReopenSupportedVersion ? kEventFlagDataMqNotEmpty : kEventFlagNotEmpty;
222
223 if (specific.has_value()) {
224 RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr");
225 } else {
226 Parameter::Specific defaultSpecific =
227 Parameter::Specific::make<Parameter::Specific::dynamicsProcessing>(
228 DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
229 mContext->getEngineArchitecture()));
230 RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(defaultSpecific), "setDefaultEngineErr");
231 }
232
233 mState = State::IDLE;
234 mContext->dupeFmq(ret);
235 RETURN_IF(createThread(getEffectNameWithVersion()) != RetCode::SUCCESS,
236 EX_UNSUPPORTED_OPERATION, "FailedToCreateWorker");
237 LOG(INFO) << getEffectNameWithVersion() << __func__;
238 return ndk::ScopedAStatus::ok();
239 }
240
getDescriptor(Descriptor * _aidl_return)241 ndk::ScopedAStatus DynamicsProcessingImpl::getDescriptor(Descriptor* _aidl_return) {
242 RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
243 *_aidl_return = kDescriptor;
244 return ndk::ScopedAStatus::ok();
245 }
246
commandImpl(CommandId command)247 ndk::ScopedAStatus DynamicsProcessingImpl::commandImpl(CommandId command) {
248 RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
249 switch (command) {
250 case CommandId::START:
251 mContext->enable();
252 return ndk::ScopedAStatus::ok();
253 case CommandId::STOP:
254 mContext->disable();
255 return ndk::ScopedAStatus::ok();
256 case CommandId::RESET:
257 mContext->disable();
258 mContext->resetBuffer();
259 return ndk::ScopedAStatus::ok();
260 default:
261 // Need this default handling for vendor extendable CommandId::VENDOR_COMMAND_*
262 LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
263 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
264 "commandIdNotSupported");
265 }
266 }
267
isParamInRange(const Parameter::Specific & specific)268 bool DynamicsProcessingImpl::isParamInRange(const Parameter::Specific& specific) {
269 auto& dp = specific.get<Parameter::Specific::dynamicsProcessing>();
270 return DynamicsProcessingRanges::isParamInRange(dp, kRanges);
271 }
272
setParameterSpecific(const Parameter::Specific & specific)273 ndk::ScopedAStatus DynamicsProcessingImpl::setParameterSpecific(
274 const Parameter::Specific& specific) {
275 RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
276 "EffectNotSupported");
277 RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
278
279 RETURN_IF(!isParamInRange(specific), EX_ILLEGAL_ARGUMENT, "outOfRange");
280 auto& param = specific.get<Parameter::Specific::dynamicsProcessing>();
281 auto tag = param.getTag();
282
283 switch (tag) {
284 case DynamicsProcessing::engineArchitecture: {
285 RETURN_IF(mContext->setEngineArchitecture(
286 param.get<DynamicsProcessing::engineArchitecture>()) !=
287 RetCode::SUCCESS,
288 EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed");
289 return ndk::ScopedAStatus::ok();
290 }
291 case DynamicsProcessing::preEq: {
292 RETURN_IF(
293 mContext->setPreEq(param.get<DynamicsProcessing::preEq>()) != RetCode::SUCCESS,
294 EX_ILLEGAL_ARGUMENT, "setPreEqFailed");
295 return ndk::ScopedAStatus::ok();
296 }
297 case DynamicsProcessing::postEq: {
298 RETURN_IF(mContext->setPostEq(param.get<DynamicsProcessing::postEq>()) !=
299 RetCode::SUCCESS,
300 EX_ILLEGAL_ARGUMENT, "setPostEqFailed");
301 return ndk::ScopedAStatus::ok();
302 }
303 case DynamicsProcessing::preEqBand: {
304 RETURN_IF(mContext->setPreEqBand(param.get<DynamicsProcessing::preEqBand>()) !=
305 RetCode::SUCCESS,
306 EX_ILLEGAL_ARGUMENT, "setPreEqBandFailed");
307 return ndk::ScopedAStatus::ok();
308 }
309 case DynamicsProcessing::postEqBand: {
310 RETURN_IF(mContext->setPostEqBand(param.get<DynamicsProcessing::postEqBand>()) !=
311 RetCode::SUCCESS,
312 EX_ILLEGAL_ARGUMENT, "setPostEqBandFailed");
313 return ndk::ScopedAStatus::ok();
314 }
315 case DynamicsProcessing::mbc: {
316 RETURN_IF(mContext->setMbc(param.get<DynamicsProcessing::mbc>()) != RetCode::SUCCESS,
317 EX_ILLEGAL_ARGUMENT, "setMbcFailed");
318 return ndk::ScopedAStatus::ok();
319 }
320 case DynamicsProcessing::mbcBand: {
321 RETURN_IF(mContext->setMbcBand(param.get<DynamicsProcessing::mbcBand>()) !=
322 RetCode::SUCCESS,
323 EX_ILLEGAL_ARGUMENT, "setMbcBandFailed");
324 return ndk::ScopedAStatus::ok();
325 }
326 case DynamicsProcessing::limiter: {
327 RETURN_IF(mContext->setLimiter(param.get<DynamicsProcessing::limiter>()) !=
328 RetCode::SUCCESS,
329 EX_ILLEGAL_ARGUMENT, "setLimiterFailed");
330 return ndk::ScopedAStatus::ok();
331 }
332 case DynamicsProcessing::inputGain: {
333 RETURN_IF(mContext->setInputGain(param.get<DynamicsProcessing::inputGain>()) !=
334 RetCode::SUCCESS,
335 EX_ILLEGAL_ARGUMENT, "setInputGainFailed");
336 return ndk::ScopedAStatus::ok();
337 }
338 case DynamicsProcessing::vendor: {
339 LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
340 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
341 EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagNotSupported");
342 }
343 }
344 }
345
getParameterSpecific(const Parameter::Id & id,Parameter::Specific * specific)346 ndk::ScopedAStatus DynamicsProcessingImpl::getParameterSpecific(const Parameter::Id& id,
347 Parameter::Specific* specific) {
348 RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
349 auto tag = id.getTag();
350 RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
351 auto dpId = id.get<Parameter::Id::dynamicsProcessingTag>();
352 auto dpIdTag = dpId.getTag();
353 switch (dpIdTag) {
354 case DynamicsProcessing::Id::commonTag:
355 return getParameterDynamicsProcessing(dpId.get<DynamicsProcessing::Id::commonTag>(),
356 specific);
357 case DynamicsProcessing::Id::vendorExtensionTag:
358 LOG(ERROR) << __func__ << " unsupported ID: " << toString(dpIdTag);
359 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
360 EX_ILLEGAL_ARGUMENT, "DPVendorExtensionIdNotSupported");
361 }
362 }
363
getParameterDynamicsProcessing(const DynamicsProcessing::Tag & tag,Parameter::Specific * specific)364 ndk::ScopedAStatus DynamicsProcessingImpl::getParameterDynamicsProcessing(
365 const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) {
366 RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
367
368 switch (tag) {
369 case DynamicsProcessing::engineArchitecture: {
370 specific->set<Parameter::Specific::dynamicsProcessing>(
371 DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
372 mContext->getEngineArchitecture()));
373 return ndk::ScopedAStatus::ok();
374 }
375 case DynamicsProcessing::preEq: {
376 specific->set<Parameter::Specific::dynamicsProcessing>(
377 DynamicsProcessing::make<DynamicsProcessing::preEq>(mContext->getPreEq()));
378 return ndk::ScopedAStatus::ok();
379 }
380 case DynamicsProcessing::postEq: {
381 specific->set<Parameter::Specific::dynamicsProcessing>(
382 DynamicsProcessing::make<DynamicsProcessing::postEq>(mContext->getPostEq()));
383 return ndk::ScopedAStatus::ok();
384 }
385 case DynamicsProcessing::preEqBand: {
386 specific->set<Parameter::Specific::dynamicsProcessing>(
387 DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
388 mContext->getPreEqBand()));
389 return ndk::ScopedAStatus::ok();
390 }
391 case DynamicsProcessing::postEqBand: {
392 specific->set<Parameter::Specific::dynamicsProcessing>(
393 DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
394 mContext->getPostEqBand()));
395 return ndk::ScopedAStatus::ok();
396 }
397 case DynamicsProcessing::mbc: {
398 specific->set<Parameter::Specific::dynamicsProcessing>(
399 DynamicsProcessing::make<DynamicsProcessing::mbc>(mContext->getMbc()));
400 return ndk::ScopedAStatus::ok();
401 }
402 case DynamicsProcessing::mbcBand: {
403 specific->set<Parameter::Specific::dynamicsProcessing>(
404 DynamicsProcessing::make<DynamicsProcessing::mbcBand>(mContext->getMbcBand()));
405 return ndk::ScopedAStatus::ok();
406 }
407 case DynamicsProcessing::limiter: {
408 specific->set<Parameter::Specific::dynamicsProcessing>(
409 DynamicsProcessing::make<DynamicsProcessing::limiter>(mContext->getLimiter()));
410 return ndk::ScopedAStatus::ok();
411 }
412 case DynamicsProcessing::inputGain: {
413 specific->set<Parameter::Specific::dynamicsProcessing>(
414 DynamicsProcessing::make<DynamicsProcessing::inputGain>(
415 mContext->getInputGain()));
416 return ndk::ScopedAStatus::ok();
417 }
418 case DynamicsProcessing::vendor: {
419 LOG(ERROR) << __func__ << " wrong vendor tag in CommonTag: " << toString(tag);
420 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
421 EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagInWrongId");
422 }
423 }
424 }
425
createContext(const Parameter::Common & common)426 std::shared_ptr<EffectContext> DynamicsProcessingImpl::createContext(
427 const Parameter::Common& common) {
428 if (mContext) {
429 LOG(DEBUG) << __func__ << " context already exist";
430 return mContext;
431 }
432
433 mContext = std::make_shared<DynamicsProcessingContext>(1 /* statusFmqDepth */, common);
434 return mContext;
435 }
436
releaseContext()437 RetCode DynamicsProcessingImpl::releaseContext() {
438 if (mContext) {
439 mContext->disable();
440 mContext->resetBuffer();
441 mContext.reset();
442 }
443 return RetCode::SUCCESS;
444 }
445
446 // Processing method running in EffectWorker thread.
effectProcessImpl(float * in,float * out,int samples)447 IEffect::Status DynamicsProcessingImpl::effectProcessImpl(float* in, float* out, int samples) {
448 IEffect::Status status = {EX_NULL_POINTER, 0, 0};
449 RETURN_VALUE_IF(!mContext, status, "nullContext");
450 return mContext->dpeProcess(in, out, samples);
451 }
452
453 } // namespace aidl::android::hardware::audio::effect
454