• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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