xref: /aosp_15_r20/frameworks/av/media/libeffects/downmix/aidl/EffectDownmix.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2022 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 ATRACE_TAG ATRACE_TAG_AUDIO
18 #define LOG_TAG "AHAL_DownmixImpl"
19 
20 #include <android-base/logging.h>
21 #include <system/audio_effects/effect_uuid.h>
22 #include <utils/Trace.h>
23 
24 #include "EffectDownmix.h"
25 
26 using aidl::android::hardware::audio::effect::Descriptor;
27 using aidl::android::hardware::audio::effect::DownmixImpl;
28 using aidl::android::hardware::audio::effect::getEffectImplUuidDownmix;
29 using aidl::android::hardware::audio::effect::getEffectTypeUuidDownmix;
30 using aidl::android::hardware::audio::effect::IEffect;
31 using aidl::android::media::audio::common::AudioUuid;
32 
createEffect(const AudioUuid * in_impl_uuid,std::shared_ptr<IEffect> * instanceSpp)33 extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
34                                            std::shared_ptr<IEffect>* instanceSpp) {
35     if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDownmix()) {
36         LOG(ERROR) << __func__ << "uuid not supported";
37         return EX_ILLEGAL_ARGUMENT;
38     }
39     if (instanceSpp) {
40         *instanceSpp = ndk::SharedRefBase::make<DownmixImpl>();
41         return EX_NONE;
42     } else {
43         LOG(ERROR) << __func__ << " invalid input parameter!";
44         return EX_ILLEGAL_ARGUMENT;
45     }
46 }
47 
queryEffect(const AudioUuid * in_impl_uuid,Descriptor * _aidl_return)48 extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
49     if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidDownmix()) {
50         LOG(ERROR) << __func__ << "uuid not supported";
51         return EX_ILLEGAL_ARGUMENT;
52     }
53     *_aidl_return = DownmixImpl::kDescriptor;
54     return EX_NONE;
55 }
56 
57 namespace aidl::android::hardware::audio::effect {
58 
59 const std::string DownmixImpl::kEffectName = "Multichannel Downmix To Stereo";
60 const Descriptor DownmixImpl::kDescriptor = {
61         .common = {.id = {.type = getEffectTypeUuidDownmix(),
62                           .uuid = getEffectImplUuidDownmix(),
63                           .proxy = std::nullopt},
64                    .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST},
65                    .name = DownmixImpl::kEffectName,
66                    .implementor = "The Android Open Source Project"}};
67 
getDescriptor(Descriptor * _aidl_return)68 ndk::ScopedAStatus DownmixImpl::getDescriptor(Descriptor* _aidl_return) {
69     RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
70     *_aidl_return = kDescriptor;
71     return ndk::ScopedAStatus::ok();
72 }
73 
setParameterSpecific(const Parameter::Specific & specific)74 ndk::ScopedAStatus DownmixImpl::setParameterSpecific(const Parameter::Specific& specific) {
75     RETURN_IF(Parameter::Specific::downmix != specific.getTag(), EX_ILLEGAL_ARGUMENT,
76               "EffectNotSupported");
77     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
78 
79     auto& dmParam = specific.get<Parameter::Specific::downmix>();
80     auto tag = dmParam.getTag();
81 
82     switch (tag) {
83         case Downmix::type: {
84             RETURN_IF(mContext->setDmType(dmParam.get<Downmix::type>()) != RetCode::SUCCESS,
85                       EX_ILLEGAL_ARGUMENT, "setTypeFailed");
86             return ndk::ScopedAStatus::ok();
87         }
88         default: {
89             LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
90             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
91                                                                     "DownmixTagNotSupported");
92         }
93     }
94 }
95 
getParameterSpecific(const Parameter::Id & id,Parameter::Specific * specific)96 ndk::ScopedAStatus DownmixImpl::getParameterSpecific(const Parameter::Id& id,
97                                                      Parameter::Specific* specific) {
98     RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
99     auto tag = id.getTag();
100     RETURN_IF(Parameter::Id::downmixTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
101     auto dmId = id.get<Parameter::Id::downmixTag>();
102     auto dmIdTag = dmId.getTag();
103     switch (dmIdTag) {
104         case Downmix::Id::commonTag:
105             return getParameterDownmix(dmId.get<Downmix::Id::commonTag>(), specific);
106         default:
107             LOG(ERROR) << __func__ << " unsupported tag: " << toString(dmIdTag);
108             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
109                                                                     "DownmixTagNotSupported");
110     }
111 }
112 
getParameterDownmix(const Downmix::Tag & tag,Parameter::Specific * specific)113 ndk::ScopedAStatus DownmixImpl::getParameterDownmix(const Downmix::Tag& tag,
114                                                     Parameter::Specific* specific) {
115     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
116 
117     Downmix dmParam;
118     switch (tag) {
119         case Downmix::type: {
120             dmParam.set<Downmix::type>(mContext->getDmType());
121             break;
122         }
123         default: {
124             LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
125             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
126                                                                     "DownmixTagNotSupported");
127         }
128     }
129 
130     specific->set<Parameter::Specific::downmix>(dmParam);
131     return ndk::ScopedAStatus::ok();
132 }
133 
createContext(const Parameter::Common & common)134 std::shared_ptr<EffectContext> DownmixImpl::createContext(const Parameter::Common& common) {
135     if (mContext) {
136         LOG(DEBUG) << __func__ << " context already exist";
137         return mContext;
138     }
139 
140     if (!DownmixContext::validateCommonConfig(common)) return nullptr;
141 
142     mContext = std::make_shared<DownmixContext>(1 /* statusFmqDepth */, common);
143     return mContext;
144 }
145 
releaseContext()146 RetCode DownmixImpl::releaseContext() {
147     if (mContext) {
148         mContext.reset();
149     }
150     return RetCode::SUCCESS;
151 }
152 
process()153 void DownmixImpl::process() {
154     ATRACE_NAME("Downmix::process");
155     /**
156      * wait for the EventFlag without lock, it's ok because the mEfGroup pointer will not change
157      * in the life cycle of workerThread (threadLoop).
158      */
159     uint32_t efState = 0;
160     if (!mEventFlag ||
161         ::android::OK != mEventFlag->wait(mDataMqNotEmptyEf, &efState, 0 /* no timeout */,
162                                           true /* retry */) ||
163         !(efState & mDataMqNotEmptyEf)) {
164         LOG(ERROR) << getEffectName() << __func__ << ": StatusEventFlag invalid";
165     }
166 
167     {
168         std::lock_guard lg(mImplMutex);
169         RETURN_VALUE_IF(!mImplContext, void(), "nullContext");
170         auto statusMQ = mImplContext->getStatusFmq();
171         auto inputMQ = mImplContext->getInputDataFmq();
172         auto outputMQ = mImplContext->getOutputDataFmq();
173         auto buffer = mImplContext->getWorkBuffer();
174         if (!inputMQ || !outputMQ) {
175             return;
176         }
177 
178         const auto availableToRead = inputMQ->availableToRead();
179         const auto availableToWrite = outputMQ->availableToWrite() *
180                                       mImplContext->getInputFrameSize() /
181                                       mImplContext->getOutputFrameSize();
182         assert(mImplContext->getWorkBufferSize() >=
183                std::max(availableToRead(), availableToWrite));
184         auto processSamples = std::min(availableToRead, availableToWrite);
185         if (processSamples) {
186             inputMQ->read(buffer, processSamples);
187             IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
188             outputMQ->write(buffer, status.fmqProduced);
189             statusMQ->writeBlocking(&status, 1);
190         }
191     }
192 }
193 
194 // Processing method running in EffectWorker thread.
effectProcessImpl(float * in,float * out,int sampleToProcess)195 IEffect::Status DownmixImpl::effectProcessImpl(float* in, float* out, int sampleToProcess) {
196     if (!mContext) {
197         LOG(ERROR) << __func__ << " nullContext";
198         return {EX_NULL_POINTER, 0, 0};
199     }
200     return mContext->downmixProcess(in, out, sampleToProcess);
201 }
202 
203 }  // namespace aidl::android::hardware::audio::effect
204