Loading media/libeffects/downmix/Android.bp +26 −0 Original line number Diff line number Diff line Loading @@ -47,3 +47,29 @@ cc_library { "libhardware_headers", ], } cc_library_shared { name: "libdownmixaidl", srcs: [ "aidl/EffectDownmix.cpp", "aidl/DownmixContext.cpp", ":effectCommonFile", ], defaults: [ "aidlaudioservice_defaults", "latest_android_hardware_audio_effect_ndk_shared", "latest_android_media_audio_common_types_ndk_shared", ], header_libs: [ "libaudioeffects", "libhardware_headers" ], shared_libs: [ "libaudioutils", "libcutils", "liblog", ], visibility: [ "//hardware/interfaces/audio/aidl/default", ], } media/libeffects/downmix/aidl/DownmixContext.cpp 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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_DownmixContext" #include <android-base/logging.h> #include "DownmixContext.h" using aidl::android::hardware::audio::effect::IEffect; using ::android::hardware::audio::common::getChannelCount; namespace aidl::android::hardware::audio::effect { DownmixContext::DownmixContext(int statusDepth, const Parameter::Common& common) : EffectContext(statusDepth, common) { LOG(DEBUG) << __func__; mState = DOWNMIX_STATE_UNINITIALIZED; init_params(common); } DownmixContext::~DownmixContext() { LOG(DEBUG) << __func__; mState = DOWNMIX_STATE_UNINITIALIZED; } RetCode DownmixContext::enable() { LOG(DEBUG) << __func__; if (mState != DOWNMIX_STATE_INITIALIZED) { return RetCode::ERROR_EFFECT_LIB_ERROR; } mState = DOWNMIX_STATE_ACTIVE; return RetCode::SUCCESS; } RetCode DownmixContext::disable() { LOG(DEBUG) << __func__; if (mState != DOWNMIX_STATE_ACTIVE) { return RetCode::ERROR_EFFECT_LIB_ERROR; } mState = DOWNMIX_STATE_INITIALIZED; return RetCode::SUCCESS; } void DownmixContext::reset() { LOG(DEBUG) << __func__; disable(); resetBuffer(); } IEffect::Status DownmixContext::lvmProcess(float* in, float* out, int samples) { LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples; IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0}; if (in == nullptr || out == nullptr || getInputFrameSize() != getOutputFrameSize() || getInputFrameSize() == 0) { return status; } status = {EX_ILLEGAL_STATE, 0, 0}; if (mState == DOWNMIX_STATE_UNINITIALIZED) { LOG(ERROR) << __func__ << "Trying to use an uninitialized downmixer"; return status; } else if (mState == DOWNMIX_STATE_INITIALIZED) { LOG(ERROR) << __func__ << "Trying to use a non-configured downmixer"; return status; } LOG(DEBUG) << __func__ << " start processing"; bool accumulate = false; int frames = samples * sizeof(float) / getInputFrameSize(); if (mType == Downmix::Type::STRIP) { int inputChannelCount = getChannelCount(mChMask); while (frames) { if (accumulate) { out[0] = std::clamp(out[0] + in[0], -1.f, 1.f); out[1] = std::clamp(out[1] + in[1], -1.f, 1.f); } else { out[0] = in[0]; out[1] = in[1]; } in += inputChannelCount; out += 2; frames--; } } else { int chMask = mChMask.get<AudioChannelLayout::layoutMask>(); if (!mChannelMix.process(in, out, frames, accumulate, (audio_channel_mask_t)chMask)) { LOG(ERROR) << "Multichannel configuration " << mChMask.toString() << " is not supported"; return status; } } LOG(DEBUG) << __func__ << " done processing"; return {STATUS_OK, samples, samples}; } void DownmixContext::init_params(const Parameter::Common& common) { // when configuring the effect, do not allow a blank or unsupported channel mask AudioChannelLayout channelMask = common.input.base.channelMask; if (isChannelMaskValid(channelMask)) { LOG(ERROR) << "Downmix_Configure error: input channel mask " << channelMask.toString() << " not supported"; } else { mType = Downmix::Type::FOLD; mChMask = channelMask; mState = DOWNMIX_STATE_INITIALIZED; } } bool DownmixContext::isChannelMaskValid(AudioChannelLayout channelMask) { if (channelMask.getTag() == AudioChannelLayout::layoutMask) return false; int chMask = channelMask.get<AudioChannelLayout::layoutMask>(); // check against unsupported channels (up to FCC_26) constexpr uint32_t MAXIMUM_CHANNEL_MASK = AudioChannelLayout::LAYOUT_22POINT2 | AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT | AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT; if (chMask & ~MAXIMUM_CHANNEL_MASK) { LOG(ERROR) << "Unsupported channels in " << (chMask & ~MAXIMUM_CHANNEL_MASK); return false; } return true; } } // namespace aidl::android::hardware::audio::effect media/libeffects/downmix/aidl/DownmixContext.h 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 "effect-impl/EffectContext.h" #include <audio_utils/ChannelMix.h> namespace aidl::android::hardware::audio::effect { using media::audio::common::AudioChannelLayout; using media::audio::common::AudioDeviceDescription; enum DownmixState { DOWNMIX_STATE_UNINITIALIZED, DOWNMIX_STATE_INITIALIZED, DOWNMIX_STATE_ACTIVE, }; class DownmixContext final : public EffectContext { public: DownmixContext(int statusDepth, const Parameter::Common& common); ~DownmixContext(); RetCode enable(); RetCode disable(); void reset(); RetCode setDmType(Downmix::Type type) { mType = type; return RetCode::SUCCESS; } Downmix::Type getDmType() const { return mType; } RetCode setVolumeStereo(const Parameter::VolumeStereo& volumeStereo) override { // FIXME change volume mVolumeStereo = volumeStereo; return RetCode::SUCCESS; } Parameter::VolumeStereo getVolumeStereo() override { return mVolumeStereo; } RetCode setOutputDevice(const AudioDeviceDescription& device) override { // FIXME change type if playing on headset vs speaker mOutputDevice = device; return RetCode::SUCCESS; } AudioDeviceDescription getOutputDevice() { return mOutputDevice; } IEffect::Status lvmProcess(float* in, float* out, int samples); private: DownmixState mState; Downmix::Type mType; AudioChannelLayout mChMask; ::android::audio_utils::channels::ChannelMix mChannelMix; // Common Params AudioDeviceDescription mOutputDevice; Parameter::VolumeStereo mVolumeStereo; void init_params(const Parameter::Common& common); bool isChannelMaskValid(AudioChannelLayout channelMask); }; } // namespace aidl::android::hardware::audio::effect media/libeffects/downmix/aidl/EffectDownmix.cpp 0 → 100644 +214 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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_DownmixImpl" #include <android-base/logging.h> #include "EffectDownmix.h" using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::DownmixImpl; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::kDownmixImplUUID; using aidl::android::hardware::audio::effect::kDownmixTypeUUID; using aidl::android::media::audio::common::AudioUuid; extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid, std::shared_ptr<IEffect>* instanceSpp) { if (!in_impl_uuid || *in_impl_uuid != kDownmixImplUUID) { LOG(ERROR) << __func__ << "uuid not supported"; return EX_ILLEGAL_ARGUMENT; } if (instanceSpp) { *instanceSpp = ndk::SharedRefBase::make<DownmixImpl>(); 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 != kDownmixImplUUID) { LOG(ERROR) << __func__ << "uuid not supported"; return EX_ILLEGAL_ARGUMENT; } *_aidl_return = DownmixImpl::kDescriptor; return EX_NONE; } namespace aidl::android::hardware::audio::effect { const std::string DownmixImpl::kEffectName = "Multichannel Downmix To Stereo"; const Descriptor DownmixImpl::kDescriptor = { .common = { .id = {.type = kDownmixTypeUUID, .uuid = kDownmixImplUUID, .proxy = std::nullopt}, .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST}, .name = DownmixImpl::kEffectName, .implementor = "The Android Open Source Project"}}; ndk::ScopedAStatus DownmixImpl::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 DownmixImpl::setParameterCommon(const Parameter& param) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto tag = param.getTag(); switch (tag) { case Parameter::common: RETURN_IF(mContext->setCommon(param.get<Parameter::common>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setCommFailed"); break; case Parameter::deviceDescription: RETURN_IF(mContext->setOutputDevice(param.get<Parameter::deviceDescription>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setDeviceFailed"); break; case Parameter::mode: RETURN_IF(mContext->setAudioMode(param.get<Parameter::mode>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setModeFailed"); break; case Parameter::source: RETURN_IF(mContext->setAudioSource(param.get<Parameter::source>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setSourceFailed"); break; case Parameter::volumeStereo: RETURN_IF(mContext->setVolumeStereo(param.get<Parameter::volumeStereo>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setVolumeStereoFailed"); break; default: { LOG(ERROR) << __func__ << " unsupportedParameterTag " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "commonParamNotSupported"); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus DownmixImpl::commandImpl(CommandId command) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); switch (command) { case CommandId::START: mContext->enable(); break; case CommandId::STOP: mContext->disable(); break; case CommandId::RESET: mContext->reset(); break; default: LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported"; return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "commandIdNotSupported"); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus DownmixImpl::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::downmix != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto& dmParam = specific.get<Parameter::Specific::downmix>(); auto tag = dmParam.getTag(); switch (tag) { case Downmix::type: { RETURN_IF(mContext->setDmType(dmParam.get<Downmix::type>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setTypeFailed"); return ndk::ScopedAStatus::ok(); } default: { LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } } ndk::ScopedAStatus DownmixImpl::getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific) { RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr"); auto tag = id.getTag(); RETURN_IF(Parameter::Id::downmixTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag"); auto dmId = id.get<Parameter::Id::downmixTag>(); auto dmIdTag = dmId.getTag(); switch (dmIdTag) { case Downmix::Id::commonTag: return getParameterDownmix(dmId.get<Downmix::Id::commonTag>(), specific); default: LOG(ERROR) << __func__ << " unsupported tag: " << toString(dmIdTag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } ndk::ScopedAStatus DownmixImpl::getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); Downmix dmParam; switch (tag) { case Downmix::type: { dmParam.set<Downmix::type>(mContext->getDmType()); break; } default: { LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } specific->set<Parameter::Specific::downmix>(dmParam); return ndk::ScopedAStatus::ok(); } std::shared_ptr<EffectContext> DownmixImpl::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; return mContext; } mContext = std::make_shared<DownmixContext>(1 /* statusFmqDepth */, common); return mContext; } RetCode DownmixImpl::releaseContext() { if (mContext) { mContext.reset(); } return RetCode::SUCCESS; } // Processing method running in EffectWorker thread. IEffect::Status DownmixImpl::effectProcessImpl(float* in, float* out, int sampleToProcess) { if (!mContext) { LOG(ERROR) << __func__ << " nullContext"; return {EX_NULL_POINTER, 0, 0}; } return mContext->lvmProcess(in, out, sampleToProcess); } } // namespace aidl::android::hardware::audio::effect media/libeffects/downmix/aidl/EffectDownmix.h 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 <audio_effects/effect_downmix.h> #include "DownmixContext.h" #include "effect-impl/EffectImpl.h" #include "effect-impl/EffectUUID.h" namespace aidl::android::hardware::audio::effect { class DownmixImpl final : public EffectImpl { public: static const std::string kEffectName; static const Descriptor kDescriptor; DownmixImpl() { LOG(DEBUG) << __func__; } ~DownmixImpl() { cleanUp(); LOG(DEBUG) << __func__; } ndk::ScopedAStatus commandImpl(CommandId command) override; ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override; ndk::ScopedAStatus setParameterCommon(const Parameter& param) 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<DownmixContext> mContext; ndk::ScopedAStatus getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific); }; } // namespace aidl::android::hardware::audio::effect Loading
media/libeffects/downmix/Android.bp +26 −0 Original line number Diff line number Diff line Loading @@ -47,3 +47,29 @@ cc_library { "libhardware_headers", ], } cc_library_shared { name: "libdownmixaidl", srcs: [ "aidl/EffectDownmix.cpp", "aidl/DownmixContext.cpp", ":effectCommonFile", ], defaults: [ "aidlaudioservice_defaults", "latest_android_hardware_audio_effect_ndk_shared", "latest_android_media_audio_common_types_ndk_shared", ], header_libs: [ "libaudioeffects", "libhardware_headers" ], shared_libs: [ "libaudioutils", "libcutils", "liblog", ], visibility: [ "//hardware/interfaces/audio/aidl/default", ], }
media/libeffects/downmix/aidl/DownmixContext.cpp 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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_DownmixContext" #include <android-base/logging.h> #include "DownmixContext.h" using aidl::android::hardware::audio::effect::IEffect; using ::android::hardware::audio::common::getChannelCount; namespace aidl::android::hardware::audio::effect { DownmixContext::DownmixContext(int statusDepth, const Parameter::Common& common) : EffectContext(statusDepth, common) { LOG(DEBUG) << __func__; mState = DOWNMIX_STATE_UNINITIALIZED; init_params(common); } DownmixContext::~DownmixContext() { LOG(DEBUG) << __func__; mState = DOWNMIX_STATE_UNINITIALIZED; } RetCode DownmixContext::enable() { LOG(DEBUG) << __func__; if (mState != DOWNMIX_STATE_INITIALIZED) { return RetCode::ERROR_EFFECT_LIB_ERROR; } mState = DOWNMIX_STATE_ACTIVE; return RetCode::SUCCESS; } RetCode DownmixContext::disable() { LOG(DEBUG) << __func__; if (mState != DOWNMIX_STATE_ACTIVE) { return RetCode::ERROR_EFFECT_LIB_ERROR; } mState = DOWNMIX_STATE_INITIALIZED; return RetCode::SUCCESS; } void DownmixContext::reset() { LOG(DEBUG) << __func__; disable(); resetBuffer(); } IEffect::Status DownmixContext::lvmProcess(float* in, float* out, int samples) { LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples; IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0}; if (in == nullptr || out == nullptr || getInputFrameSize() != getOutputFrameSize() || getInputFrameSize() == 0) { return status; } status = {EX_ILLEGAL_STATE, 0, 0}; if (mState == DOWNMIX_STATE_UNINITIALIZED) { LOG(ERROR) << __func__ << "Trying to use an uninitialized downmixer"; return status; } else if (mState == DOWNMIX_STATE_INITIALIZED) { LOG(ERROR) << __func__ << "Trying to use a non-configured downmixer"; return status; } LOG(DEBUG) << __func__ << " start processing"; bool accumulate = false; int frames = samples * sizeof(float) / getInputFrameSize(); if (mType == Downmix::Type::STRIP) { int inputChannelCount = getChannelCount(mChMask); while (frames) { if (accumulate) { out[0] = std::clamp(out[0] + in[0], -1.f, 1.f); out[1] = std::clamp(out[1] + in[1], -1.f, 1.f); } else { out[0] = in[0]; out[1] = in[1]; } in += inputChannelCount; out += 2; frames--; } } else { int chMask = mChMask.get<AudioChannelLayout::layoutMask>(); if (!mChannelMix.process(in, out, frames, accumulate, (audio_channel_mask_t)chMask)) { LOG(ERROR) << "Multichannel configuration " << mChMask.toString() << " is not supported"; return status; } } LOG(DEBUG) << __func__ << " done processing"; return {STATUS_OK, samples, samples}; } void DownmixContext::init_params(const Parameter::Common& common) { // when configuring the effect, do not allow a blank or unsupported channel mask AudioChannelLayout channelMask = common.input.base.channelMask; if (isChannelMaskValid(channelMask)) { LOG(ERROR) << "Downmix_Configure error: input channel mask " << channelMask.toString() << " not supported"; } else { mType = Downmix::Type::FOLD; mChMask = channelMask; mState = DOWNMIX_STATE_INITIALIZED; } } bool DownmixContext::isChannelMaskValid(AudioChannelLayout channelMask) { if (channelMask.getTag() == AudioChannelLayout::layoutMask) return false; int chMask = channelMask.get<AudioChannelLayout::layoutMask>(); // check against unsupported channels (up to FCC_26) constexpr uint32_t MAXIMUM_CHANNEL_MASK = AudioChannelLayout::LAYOUT_22POINT2 | AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT | AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT; if (chMask & ~MAXIMUM_CHANNEL_MASK) { LOG(ERROR) << "Unsupported channels in " << (chMask & ~MAXIMUM_CHANNEL_MASK); return false; } return true; } } // namespace aidl::android::hardware::audio::effect
media/libeffects/downmix/aidl/DownmixContext.h 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 "effect-impl/EffectContext.h" #include <audio_utils/ChannelMix.h> namespace aidl::android::hardware::audio::effect { using media::audio::common::AudioChannelLayout; using media::audio::common::AudioDeviceDescription; enum DownmixState { DOWNMIX_STATE_UNINITIALIZED, DOWNMIX_STATE_INITIALIZED, DOWNMIX_STATE_ACTIVE, }; class DownmixContext final : public EffectContext { public: DownmixContext(int statusDepth, const Parameter::Common& common); ~DownmixContext(); RetCode enable(); RetCode disable(); void reset(); RetCode setDmType(Downmix::Type type) { mType = type; return RetCode::SUCCESS; } Downmix::Type getDmType() const { return mType; } RetCode setVolumeStereo(const Parameter::VolumeStereo& volumeStereo) override { // FIXME change volume mVolumeStereo = volumeStereo; return RetCode::SUCCESS; } Parameter::VolumeStereo getVolumeStereo() override { return mVolumeStereo; } RetCode setOutputDevice(const AudioDeviceDescription& device) override { // FIXME change type if playing on headset vs speaker mOutputDevice = device; return RetCode::SUCCESS; } AudioDeviceDescription getOutputDevice() { return mOutputDevice; } IEffect::Status lvmProcess(float* in, float* out, int samples); private: DownmixState mState; Downmix::Type mType; AudioChannelLayout mChMask; ::android::audio_utils::channels::ChannelMix mChannelMix; // Common Params AudioDeviceDescription mOutputDevice; Parameter::VolumeStereo mVolumeStereo; void init_params(const Parameter::Common& common); bool isChannelMaskValid(AudioChannelLayout channelMask); }; } // namespace aidl::android::hardware::audio::effect
media/libeffects/downmix/aidl/EffectDownmix.cpp 0 → 100644 +214 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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_DownmixImpl" #include <android-base/logging.h> #include "EffectDownmix.h" using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::DownmixImpl; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::kDownmixImplUUID; using aidl::android::hardware::audio::effect::kDownmixTypeUUID; using aidl::android::media::audio::common::AudioUuid; extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid, std::shared_ptr<IEffect>* instanceSpp) { if (!in_impl_uuid || *in_impl_uuid != kDownmixImplUUID) { LOG(ERROR) << __func__ << "uuid not supported"; return EX_ILLEGAL_ARGUMENT; } if (instanceSpp) { *instanceSpp = ndk::SharedRefBase::make<DownmixImpl>(); 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 != kDownmixImplUUID) { LOG(ERROR) << __func__ << "uuid not supported"; return EX_ILLEGAL_ARGUMENT; } *_aidl_return = DownmixImpl::kDescriptor; return EX_NONE; } namespace aidl::android::hardware::audio::effect { const std::string DownmixImpl::kEffectName = "Multichannel Downmix To Stereo"; const Descriptor DownmixImpl::kDescriptor = { .common = { .id = {.type = kDownmixTypeUUID, .uuid = kDownmixImplUUID, .proxy = std::nullopt}, .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST}, .name = DownmixImpl::kEffectName, .implementor = "The Android Open Source Project"}}; ndk::ScopedAStatus DownmixImpl::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 DownmixImpl::setParameterCommon(const Parameter& param) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto tag = param.getTag(); switch (tag) { case Parameter::common: RETURN_IF(mContext->setCommon(param.get<Parameter::common>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setCommFailed"); break; case Parameter::deviceDescription: RETURN_IF(mContext->setOutputDevice(param.get<Parameter::deviceDescription>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setDeviceFailed"); break; case Parameter::mode: RETURN_IF(mContext->setAudioMode(param.get<Parameter::mode>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setModeFailed"); break; case Parameter::source: RETURN_IF(mContext->setAudioSource(param.get<Parameter::source>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setSourceFailed"); break; case Parameter::volumeStereo: RETURN_IF(mContext->setVolumeStereo(param.get<Parameter::volumeStereo>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setVolumeStereoFailed"); break; default: { LOG(ERROR) << __func__ << " unsupportedParameterTag " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "commonParamNotSupported"); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus DownmixImpl::commandImpl(CommandId command) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); switch (command) { case CommandId::START: mContext->enable(); break; case CommandId::STOP: mContext->disable(); break; case CommandId::RESET: mContext->reset(); break; default: LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported"; return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "commandIdNotSupported"); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus DownmixImpl::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::downmix != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto& dmParam = specific.get<Parameter::Specific::downmix>(); auto tag = dmParam.getTag(); switch (tag) { case Downmix::type: { RETURN_IF(mContext->setDmType(dmParam.get<Downmix::type>()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setTypeFailed"); return ndk::ScopedAStatus::ok(); } default: { LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } } ndk::ScopedAStatus DownmixImpl::getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific) { RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr"); auto tag = id.getTag(); RETURN_IF(Parameter::Id::downmixTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag"); auto dmId = id.get<Parameter::Id::downmixTag>(); auto dmIdTag = dmId.getTag(); switch (dmIdTag) { case Downmix::Id::commonTag: return getParameterDownmix(dmId.get<Downmix::Id::commonTag>(), specific); default: LOG(ERROR) << __func__ << " unsupported tag: " << toString(dmIdTag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } ndk::ScopedAStatus DownmixImpl::getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific) { RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); Downmix dmParam; switch (tag) { case Downmix::type: { dmParam.set<Downmix::type>(mContext->getDmType()); break; } default: { LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "DownmixTagNotSupported"); } } specific->set<Parameter::Specific::downmix>(dmParam); return ndk::ScopedAStatus::ok(); } std::shared_ptr<EffectContext> DownmixImpl::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; return mContext; } mContext = std::make_shared<DownmixContext>(1 /* statusFmqDepth */, common); return mContext; } RetCode DownmixImpl::releaseContext() { if (mContext) { mContext.reset(); } return RetCode::SUCCESS; } // Processing method running in EffectWorker thread. IEffect::Status DownmixImpl::effectProcessImpl(float* in, float* out, int sampleToProcess) { if (!mContext) { LOG(ERROR) << __func__ << " nullContext"; return {EX_NULL_POINTER, 0, 0}; } return mContext->lvmProcess(in, out, sampleToProcess); } } // namespace aidl::android::hardware::audio::effect
media/libeffects/downmix/aidl/EffectDownmix.h 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 <audio_effects/effect_downmix.h> #include "DownmixContext.h" #include "effect-impl/EffectImpl.h" #include "effect-impl/EffectUUID.h" namespace aidl::android::hardware::audio::effect { class DownmixImpl final : public EffectImpl { public: static const std::string kEffectName; static const Descriptor kDescriptor; DownmixImpl() { LOG(DEBUG) << __func__; } ~DownmixImpl() { cleanUp(); LOG(DEBUG) << __func__; } ndk::ScopedAStatus commandImpl(CommandId command) override; ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override; ndk::ScopedAStatus setParameterCommon(const Parameter& param) 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<DownmixContext> mContext; ndk::ScopedAStatus getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific); }; } // namespace aidl::android::hardware::audio::effect