| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 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 LOG_TAG "AHAL_VisualizerLibEffects" | 
|  | 18 |  | 
|  | 19 | #include <android-base/logging.h> | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 20 | #include <system/audio_effects/effect_uuid.h> | 
|  | 21 |  | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 22 | #include "Visualizer.h" | 
|  | 23 |  | 
|  | 24 | using aidl::android::hardware::audio::effect::Descriptor; | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 25 | using aidl::android::hardware::audio::effect::getEffectImplUuidVisualizer; | 
|  | 26 | using aidl::android::hardware::audio::effect::getEffectTypeUuidVisualizer; | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 27 | using aidl::android::hardware::audio::effect::IEffect; | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 28 | using aidl::android::hardware::audio::effect::State; | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 29 | using aidl::android::hardware::audio::effect::VisualizerImpl; | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 30 | using aidl::android::media::audio::common::AudioUuid; | 
|  | 31 |  | 
|  | 32 | extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid, | 
|  | 33 | std::shared_ptr<IEffect>* instanceSpp) { | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 34 | if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidVisualizer()) { | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 35 | LOG(ERROR) << __func__ << "uuid not supported"; | 
|  | 36 | return EX_ILLEGAL_ARGUMENT; | 
|  | 37 | } | 
|  | 38 | if (instanceSpp) { | 
|  | 39 | *instanceSpp = ndk::SharedRefBase::make<VisualizerImpl>(); | 
|  | 40 | LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created"; | 
|  | 41 | return EX_NONE; | 
|  | 42 | } else { | 
|  | 43 | LOG(ERROR) << __func__ << " invalid input parameter!"; | 
|  | 44 | return EX_ILLEGAL_ARGUMENT; | 
|  | 45 | } | 
|  | 46 | } | 
|  | 47 |  | 
|  | 48 | extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) { | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 49 | if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidVisualizer()) { | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 50 | LOG(ERROR) << __func__ << "uuid not supported"; | 
|  | 51 | return EX_ILLEGAL_ARGUMENT; | 
|  | 52 | } | 
|  | 53 | *_aidl_return = VisualizerImpl::kDescriptor; | 
|  | 54 | return EX_NONE; | 
|  | 55 | } | 
|  | 56 |  | 
|  | 57 | namespace aidl::android::hardware::audio::effect { | 
|  | 58 |  | 
|  | 59 | const std::string VisualizerImpl::kEffectName = "Visualizer"; | 
| Shunkai Yao | 6b857c9 | 2023-02-13 17:44:52 +0000 | [diff] [blame] | 60 | const std::vector<Range::VisualizerRange> VisualizerImpl::kRanges = { | 
|  | 61 | MAKE_RANGE(Visualizer, latencyMs, 0, VisualizerContext::kMaxLatencyMs), | 
|  | 62 | MAKE_RANGE(Visualizer, captureSamples, 0, VisualizerContext::kMaxCaptureBufSize), | 
|  | 63 | /* get only parameters, set invalid range (min > max) to indicate not support set */ | 
|  | 64 | MAKE_RANGE(Visualizer, measurement, Visualizer::Measurement({.peak = 1, .rms = 1}), | 
|  | 65 | Visualizer::Measurement({.peak = 0, .rms = 0})), | 
|  | 66 | MAKE_RANGE(Visualizer, captureSampleBuffer, std::vector<uint8_t>({1}), | 
|  | 67 | std::vector<uint8_t>({0}))}; | 
|  | 68 | const Capability VisualizerImpl::kCapability = { | 
|  | 69 | .range = Range::make<Range::visualizer>(VisualizerImpl::kRanges)}; | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 70 | const Descriptor VisualizerImpl::kDescriptor = { | 
| Shunkai Yao | 399be68 | 2023-03-06 18:54:18 +0000 | [diff] [blame] | 71 | .common = {.id = {.type = getEffectTypeUuidVisualizer(), | 
|  | 72 | .uuid = getEffectImplUuidVisualizer(), | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 73 | .proxy = std::nullopt}, | 
|  | 74 | .flags = {.type = Flags::Type::INSERT, | 
|  | 75 | .insert = Flags::Insert::LAST, | 
|  | 76 | .volume = Flags::Volume::CTRL}, | 
|  | 77 | .name = VisualizerImpl::kEffectName, | 
|  | 78 | .implementor = "The Android Open Source Project"}, | 
| Shunkai Yao | 6b857c9 | 2023-02-13 17:44:52 +0000 | [diff] [blame] | 79 | .capability = VisualizerImpl::kCapability}; | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 80 |  | 
|  | 81 | ndk::ScopedAStatus VisualizerImpl::getDescriptor(Descriptor* _aidl_return) { | 
|  | 82 | RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr"); | 
|  | 83 | LOG(DEBUG) << __func__ << kDescriptor.toString(); | 
|  | 84 | *_aidl_return = kDescriptor; | 
|  | 85 | return ndk::ScopedAStatus::ok(); | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | ndk::ScopedAStatus VisualizerImpl::commandImpl(CommandId command) { | 
|  | 89 | RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); | 
|  | 90 | switch (command) { | 
|  | 91 | case CommandId::START: | 
|  | 92 | mContext->enable(); | 
|  | 93 | break; | 
|  | 94 | case CommandId::STOP: | 
|  | 95 | mContext->disable(); | 
|  | 96 | break; | 
|  | 97 | case CommandId::RESET: | 
|  | 98 | mContext->disable(); | 
|  | 99 | mContext->resetBuffer(); | 
|  | 100 | break; | 
|  | 101 | default: | 
|  | 102 | LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported"; | 
|  | 103 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, | 
|  | 104 | "commandIdNotSupported"); | 
|  | 105 | } | 
|  | 106 | return ndk::ScopedAStatus::ok(); | 
|  | 107 | } | 
|  | 108 |  | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 109 | ndk::ScopedAStatus VisualizerImpl::setParameterSpecific(const Parameter::Specific& specific) { | 
|  | 110 | RETURN_IF(Parameter::Specific::visualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT, | 
|  | 111 | "EffectNotSupported"); | 
|  | 112 | RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); | 
|  | 113 |  | 
|  | 114 | auto& param = specific.get<Parameter::Specific::visualizer>(); | 
| Shunkai Yao | 6b857c9 | 2023-02-13 17:44:52 +0000 | [diff] [blame] | 115 | RETURN_IF(!inRange(param, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange"); | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 116 | const auto tag = param.getTag(); | 
|  | 117 | switch (tag) { | 
|  | 118 | case Visualizer::captureSamples: { | 
|  | 119 | RETURN_IF(mContext->setCaptureSamples(param.get<Visualizer::captureSamples>()) != | 
|  | 120 | RetCode::SUCCESS, | 
|  | 121 | EX_ILLEGAL_ARGUMENT, "setCaptureSizeFailed"); | 
|  | 122 | return ndk::ScopedAStatus::ok(); | 
|  | 123 | } | 
|  | 124 | case Visualizer::scalingMode: { | 
|  | 125 | RETURN_IF(mContext->setScalingMode(param.get<Visualizer::scalingMode>()) != | 
|  | 126 | RetCode::SUCCESS, | 
|  | 127 | EX_ILLEGAL_ARGUMENT, "setScalingModeFailed"); | 
|  | 128 | return ndk::ScopedAStatus::ok(); | 
|  | 129 | } | 
|  | 130 | case Visualizer::measurementMode: { | 
|  | 131 | RETURN_IF(mContext->setMeasurementMode(param.get<Visualizer::measurementMode>()) != | 
|  | 132 | RetCode::SUCCESS, | 
|  | 133 | EX_ILLEGAL_ARGUMENT, "setMeasurementModeFailed"); | 
|  | 134 | return ndk::ScopedAStatus::ok(); | 
|  | 135 | } | 
| Shunkai Yao | 6b857c9 | 2023-02-13 17:44:52 +0000 | [diff] [blame] | 136 | case Visualizer::latencyMs: { | 
|  | 137 | RETURN_IF(mContext->setDownstreamLatency(param.get<Visualizer::latencyMs>()) != | 
|  | 138 | RetCode::SUCCESS, | 
|  | 139 | EX_ILLEGAL_ARGUMENT, "setLatencyFailed"); | 
|  | 140 | return ndk::ScopedAStatus::ok(); | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 141 | } | 
|  | 142 | default: { | 
|  | 143 | LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); | 
|  | 144 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage( | 
|  | 145 | EX_ILLEGAL_ARGUMENT, "VisualizerTagNotSupported"); | 
|  | 146 | } | 
|  | 147 | } | 
|  | 148 | } | 
|  | 149 |  | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 150 | ndk::ScopedAStatus VisualizerImpl::getParameterSpecific(const Parameter::Id& id, | 
|  | 151 | Parameter::Specific* specific) { | 
|  | 152 | RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr"); | 
|  | 153 | auto tag = id.getTag(); | 
|  | 154 | RETURN_IF(Parameter::Id::visualizerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag"); | 
|  | 155 | auto specificId = id.get<Parameter::Id::visualizerTag>(); | 
|  | 156 | auto specificTag = specificId.getTag(); | 
|  | 157 | switch (specificTag) { | 
|  | 158 | case Visualizer::Id::commonTag: { | 
|  | 159 | return getParameterVisualizer(specificId.get<Visualizer::Id::commonTag>(), specific); | 
|  | 160 | } | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 161 | default: { | 
|  | 162 | LOG(ERROR) << __func__ << " unsupported tag: " << toString(specificTag); | 
|  | 163 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, | 
|  | 164 | "VisualizerTagNotSupported"); | 
|  | 165 | } | 
|  | 166 | } | 
|  | 167 | return ndk::ScopedAStatus::ok(); | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | ndk::ScopedAStatus VisualizerImpl::getParameterVisualizer(const Visualizer::Tag& tag, | 
|  | 171 | Parameter::Specific* specific) { | 
|  | 172 | RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); | 
|  | 173 |  | 
|  | 174 | Visualizer param; | 
|  | 175 | switch (tag) { | 
|  | 176 | case Visualizer::captureSamples: { | 
|  | 177 | param.set<Visualizer::captureSamples>(mContext->getCaptureSamples()); | 
|  | 178 | break; | 
|  | 179 | } | 
|  | 180 | case Visualizer::scalingMode: { | 
|  | 181 | param.set<Visualizer::scalingMode>(mContext->getScalingMode()); | 
|  | 182 | break; | 
|  | 183 | } | 
|  | 184 | case Visualizer::measurementMode: { | 
|  | 185 | param.set<Visualizer::measurementMode>(mContext->getMeasurementMode()); | 
|  | 186 | break; | 
|  | 187 | } | 
| Shunkai Yao | 6b857c9 | 2023-02-13 17:44:52 +0000 | [diff] [blame] | 188 | case Visualizer::measurement: { | 
|  | 189 | param.set<Visualizer::measurement>(mContext->getMeasure()); | 
|  | 190 | break; | 
|  | 191 | } | 
|  | 192 | case Visualizer::captureSampleBuffer: { | 
|  | 193 | param.set<Visualizer::captureSampleBuffer>(mContext->capture()); | 
|  | 194 | break; | 
|  | 195 | } | 
|  | 196 | case Visualizer::latencyMs: { | 
|  | 197 | param.set<Visualizer::latencyMs>(mContext->getDownstreamLatency()); | 
|  | 198 | break; | 
|  | 199 | } | 
| Shunkai Yao | 05b190a | 2022-12-22 00:21:31 +0000 | [diff] [blame] | 200 | default: { | 
|  | 201 | LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); | 
|  | 202 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage( | 
|  | 203 | EX_ILLEGAL_ARGUMENT, "VisualizerTagNotSupported"); | 
|  | 204 | } | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | specific->set<Parameter::Specific::visualizer>(param); | 
|  | 208 | return ndk::ScopedAStatus::ok(); | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | std::shared_ptr<EffectContext> VisualizerImpl::createContext(const Parameter::Common& common) { | 
|  | 212 | if (mContext) { | 
|  | 213 | LOG(DEBUG) << __func__ << " context already exist"; | 
|  | 214 | return mContext; | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 | mContext = std::make_shared<VisualizerContext>(1 /* statusFmqDepth */, common); | 
|  | 218 | mContext->initParams(common); | 
|  | 219 | return mContext; | 
|  | 220 | } | 
|  | 221 |  | 
|  | 222 | RetCode VisualizerImpl::releaseContext() { | 
|  | 223 | if (mContext) { | 
|  | 224 | mContext->disable(); | 
|  | 225 | mContext->resetBuffer(); | 
|  | 226 | } | 
|  | 227 | return RetCode::SUCCESS; | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | // Processing method running in EffectWorker thread. | 
|  | 231 | IEffect::Status VisualizerImpl::effectProcessImpl(float* in, float* out, int samples) { | 
|  | 232 | IEffect::Status status = {EX_NULL_POINTER, 0, 0}; | 
|  | 233 | RETURN_VALUE_IF(!mContext, status, "nullContext"); | 
|  | 234 | return mContext->process(in, out, samples); | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | }  // namespace aidl::android::hardware::audio::effect |