From f627d80d37a729264af633e67b5c342dcac12466 Mon Sep 17 00:00:00 2001 From: Shraddha Basantwani Date: Tue, 8 Nov 2022 14:45:07 +0530 Subject: [PATCH] AIDL Effect: Update locking in EffectImpl Avoid locking for all EffectContext calls. Locking inside context seems not necessary for now as all binder calls already serialized. Add BassBoost AIDL placeholder implementation. Bug: 261646550 Test: atest VtsHalAudioEffectTargetTest Change-Id: Iaa41f013d5756801553e20b995aab5ddc845cf32 --- audio/aidl/default/EffectImpl.cpp | 103 +++++----- audio/aidl/default/EffectThread.cpp | 59 +++++- audio/aidl/default/bassboost/BassBoostSw.cpp | 71 +++++-- audio/aidl/default/bassboost/BassBoostSw.h | 32 ++- .../DynamicsProcessingSw.cpp | 18 +- .../dynamicProcessing/DynamicsProcessingSw.h | 9 +- audio/aidl/default/envReverb/EnvReverbSw.cpp | 19 +- audio/aidl/default/envReverb/EnvReverbSw.h | 9 +- audio/aidl/default/equalizer/EqualizerSw.cpp | 19 +- audio/aidl/default/equalizer/EqualizerSw.h | 9 +- .../hapticGenerator/HapticGeneratorSw.cpp | 20 +- .../hapticGenerator/HapticGeneratorSw.h | 9 +- .../include/effect-impl/EffectContext.h | 23 +-- .../default/include/effect-impl/EffectImpl.h | 51 ++--- .../include/effect-impl/EffectThread.h | 43 +++- .../include/effect-impl/EffectWorker.h | 2 +- .../loudnessEnhancer/LoudnessEnhancerSw.cpp | 19 +- .../loudnessEnhancer/LoudnessEnhancerSw.h | 9 +- .../default/presetReverb/PresetReverbSw.cpp | 19 +- .../default/presetReverb/PresetReverbSw.h | 9 +- .../default/virtualizer/VirtualizerSw.cpp | 19 +- .../aidl/default/virtualizer/VirtualizerSw.h | 9 +- .../aidl/default/visualizer/VisualizerSw.cpp | 19 +- audio/aidl/default/visualizer/VisualizerSw.h | 9 +- audio/aidl/default/volume/VolumeSw.cpp | 19 +- audio/aidl/default/volume/VolumeSw.h | 9 +- audio/aidl/vts/VtsHalBassBoostTargetTest.cpp | 185 ++++++++++++++++++ 27 files changed, 599 insertions(+), 222 deletions(-) create mode 100644 audio/aidl/vts/VtsHalBassBoostTargetTest.cpp diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp index 2754bb62e9..0d40cce8eb 100644 --- a/audio/aidl/default/EffectImpl.cpp +++ b/audio/aidl/default/EffectImpl.cpp @@ -25,41 +25,33 @@ ndk::ScopedAStatus EffectImpl::open(const Parameter::Common& common, const std::optional& specific, OpenEffectReturn* ret) { LOG(DEBUG) << __func__; - { - std::lock_guard lg(mMutex); - RETURN_OK_IF(mState != State::INIT); - mContext = createContext(common); - RETURN_IF(!mContext, EX_ILLEGAL_ARGUMENT, "createContextFailed"); - setContext(mContext); - } + RETURN_OK_IF(mState != State::INIT); + auto context = createContext(common); + RETURN_IF(!context, EX_NULL_POINTER, "createContextFailed"); RETURN_IF_ASTATUS_NOT_OK(setParameterCommon(common), "setCommParamErr"); if (specific.has_value()) { RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr"); } - RETURN_IF(createThread(LOG_TAG) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION, + mState = State::IDLE; + context->dupeFmq(ret); + RETURN_IF(createThread(context, getEffectName()) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION, "FailedToCreateWorker"); - - { - std::lock_guard lg(mMutex); - mContext->dupeFmq(ret); - mState = State::IDLE; - } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus EffectImpl::close() { - std::lock_guard lg(mMutex); RETURN_OK_IF(mState == State::INIT); RETURN_IF(mState == State::PROCESSING, EX_ILLEGAL_STATE, "closeAtProcessing"); // stop the worker thread, ignore the return code RETURN_IF(destroyThread() != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION, "FailedToDestroyWorker"); + mState = State::INIT; RETURN_IF(releaseContext() != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION, "FailedToCreateWorker"); - mState = State::INIT; + LOG(DEBUG) << __func__; return ndk::ScopedAStatus::ok(); } @@ -113,29 +105,30 @@ ndk::ScopedAStatus EffectImpl::getParameter(const Parameter::Id& id, Parameter* } ndk::ScopedAStatus EffectImpl::setParameterCommon(const Parameter& param) { - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); + auto context = getContext(); + RETURN_IF(!context, EX_NULL_POINTER, "nullContext"); + auto tag = param.getTag(); switch (tag) { case Parameter::common: - RETURN_IF(mContext->setCommon(param.get()) != RetCode::SUCCESS, + RETURN_IF(context->setCommon(param.get()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setCommFailed"); break; case Parameter::deviceDescription: - RETURN_IF(mContext->setOutputDevice(param.get()) != + RETURN_IF(context->setOutputDevice(param.get()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setDeviceFailed"); break; case Parameter::mode: - RETURN_IF(mContext->setAudioMode(param.get()) != RetCode::SUCCESS, + RETURN_IF(context->setAudioMode(param.get()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setModeFailed"); break; case Parameter::source: - RETURN_IF(mContext->setAudioSource(param.get()) != RetCode::SUCCESS, + RETURN_IF(context->setAudioSource(param.get()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setSourceFailed"); break; case Parameter::volumeStereo: - RETURN_IF(mContext->setVolumeStereo(param.get()) != + RETURN_IF(context->setVolumeStereo(param.get()) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT, "setVolumeStereoFailed"); break; @@ -149,27 +142,28 @@ ndk::ScopedAStatus EffectImpl::setParameterCommon(const Parameter& param) { } ndk::ScopedAStatus EffectImpl::getParameterCommon(const Parameter::Tag& tag, Parameter* param) { - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); + auto context = getContext(); + RETURN_IF(!context, EX_NULL_POINTER, "nullContext"); + switch (tag) { case Parameter::common: { - param->set(mContext->getCommon()); + param->set(context->getCommon()); break; } case Parameter::deviceDescription: { - param->set(mContext->getOutputDevice()); + param->set(context->getOutputDevice()); break; } case Parameter::mode: { - param->set(mContext->getAudioMode()); + param->set(context->getAudioMode()); break; } case Parameter::source: { - param->set(mContext->getAudioSource()); + param->set(context->getAudioSource()); break; } case Parameter::volumeStereo: { - param->set(mContext->getVolumeStereo()); + param->set(context->getVolumeStereo()); break; } default: { @@ -182,39 +176,30 @@ ndk::ScopedAStatus EffectImpl::getParameterCommon(const Parameter::Tag& tag, Par } ndk::ScopedAStatus EffectImpl::getState(State* state) { - std::lock_guard lg(mMutex); *state = mState; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus EffectImpl::command(CommandId command) { - std::lock_guard lg(mMutex); + RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "CommandStateError"); LOG(DEBUG) << __func__ << ": receive command: " << toString(command) << " at state " << toString(mState); - RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "CommandStateError"); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); switch (command) { case CommandId::START: RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "instanceNotOpen"); RETURN_OK_IF(mState == State::PROCESSING); - RETURN_IF_ASTATUS_NOT_OK(commandStart(), "commandStartFailed"); - mState = State::PROCESSING; + RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed"); startThread(); - return ndk::ScopedAStatus::ok(); + mState = State::PROCESSING; + break; case CommandId::STOP: - RETURN_OK_IF(mState == State::IDLE); - mState = State::IDLE; - RETURN_IF_ASTATUS_NOT_OK(commandStop(), "commandStopFailed"); - stopThread(); - return ndk::ScopedAStatus::ok(); case CommandId::RESET: RETURN_OK_IF(mState == State::IDLE); - mState = State::IDLE; - RETURN_IF_ASTATUS_NOT_OK(commandStop(), "commandStopFailed"); stopThread(); - mContext->resetBuffer(); - return ndk::ScopedAStatus::ok(); + RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed"); + mState = State::IDLE; + break; default: LOG(ERROR) << __func__ << " instance still processing"; return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, @@ -224,6 +209,15 @@ ndk::ScopedAStatus EffectImpl::command(CommandId command) { return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus EffectImpl::commandImpl(CommandId command) { + auto context = getContext(); + RETURN_IF(!context, EX_NULL_POINTER, "nullContext"); + if (command == CommandId::RESET) { + context->resetBuffer(); + } + return ndk::ScopedAStatus::ok(); +} + void EffectImpl::cleanUp() { command(CommandId::STOP); close(); @@ -238,19 +232,12 @@ IEffect::Status EffectImpl::status(binder_status_t status, size_t consumed, size } // A placeholder processing implementation to copy samples from input to output -IEffect::Status EffectImpl::effectProcessImpl(float* in, float* out, int processSamples) { - // lock before access context/parameters - std::lock_guard lg(mMutex); - IEffect::Status status = {EX_NULL_POINTER, 0, 0}; - RETURN_VALUE_IF(!mContext, status, "nullContext"); - auto frameSize = mContext->getInputFrameSize(); - RETURN_VALUE_IF(0 == frameSize, status, "frameSizeIs0"); - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << processSamples - << " frames " << processSamples * sizeof(float) / frameSize; - for (int i = 0; i < processSamples; i++) { +IEffect::Status EffectImpl::effectProcessImpl(float* in, float* out, int samples) { + for (int i = 0; i < samples; i++) { *out++ = *in++; } - LOG(DEBUG) << __func__ << " done processing " << processSamples << " samples"; - return {STATUS_OK, processSamples, processSamples}; + LOG(DEBUG) << __func__ << " done processing " << samples << " samples"; + return {STATUS_OK, samples, samples}; } + } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp index 80f120b0f0..2b3513d619 100644 --- a/audio/aidl/default/EffectThread.cpp +++ b/audio/aidl/default/EffectThread.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #define LOG_TAG "AHAL_EffectThread" #include #include @@ -32,13 +33,18 @@ EffectThread::~EffectThread() { LOG(DEBUG) << __func__ << " done"; }; -RetCode EffectThread::createThread(const std::string& name, const int priority) { +RetCode EffectThread::createThread(std::shared_ptr context, const std::string& name, + const int priority) { if (mThread.joinable()) { LOG(WARNING) << __func__ << " thread already created, no-op"; return RetCode::SUCCESS; } mName = name; mPriority = priority; + { + std::lock_guard lg(mThreadMutex); + mThreadContext = std::move(context); + } mThread = std::thread(&EffectThread::threadLoop, this); LOG(DEBUG) << __func__ << " " << name << " priority " << mPriority << " done"; return RetCode::SUCCESS; @@ -46,7 +52,7 @@ RetCode EffectThread::createThread(const std::string& name, const int priority) RetCode EffectThread::destroyThread() { { - std::lock_guard lg(mMutex); + std::lock_guard lg(mThreadMutex); mStop = mExit = true; } mCv.notify_one(); @@ -54,6 +60,11 @@ RetCode EffectThread::destroyThread() { if (mThread.joinable()) { mThread.join(); } + + { + std::lock_guard lg(mThreadMutex); + mThreadContext.reset(); + } LOG(DEBUG) << __func__ << " done"; return RetCode::SUCCESS; } @@ -65,7 +76,7 @@ RetCode EffectThread::startThread() { } { - std::lock_guard lg(mMutex); + std::lock_guard lg(mThreadMutex); if (!mStop) { LOG(WARNING) << __func__ << " already start"; return RetCode::SUCCESS; @@ -85,7 +96,7 @@ RetCode EffectThread::stopThread() { } { - std::lock_guard lg(mMutex); + std::lock_guard lg(mThreadMutex); if (mStop) { LOG(WARNING) << __func__ << " already stop"; return RetCode::SUCCESS; @@ -97,13 +108,13 @@ RetCode EffectThread::stopThread() { } void EffectThread::threadLoop() { - pthread_setname_np(pthread_self(), mName.substr(0, MAX_TASK_COMM_LEN - 1).c_str()); + pthread_setname_np(pthread_self(), mName.substr(0, kMaxTaskNameLen - 1).c_str()); setpriority(PRIO_PROCESS, 0, mPriority); while (true) { bool needExit = false; { - std::unique_lock l(mMutex); - mCv.wait(l, [&]() REQUIRES(mMutex) { + std::unique_lock l(mThreadMutex); + mCv.wait(l, [&]() REQUIRES(mThreadMutex) { needExit = mExit; return mExit || !mStop; }); @@ -112,9 +123,41 @@ void EffectThread::threadLoop() { LOG(WARNING) << __func__ << " EXIT!"; return; } - // process without lock + process(); } } +void EffectThread::process() { + std::shared_ptr context; + { + std::lock_guard lg(mThreadMutex); + context = mThreadContext; + RETURN_VALUE_IF(!context, void(), "nullContext"); + } + std::shared_ptr statusMQ = context->getStatusFmq(); + std::shared_ptr inputMQ = context->getInputDataFmq(); + std::shared_ptr outputMQ = context->getOutputDataFmq(); + auto buffer = context->getWorkBuffer(); + + // Only this worker will read from input data MQ and write to output data MQ. + auto readSamples = inputMQ->availableToRead(), writeSamples = outputMQ->availableToWrite(); + if (readSamples && writeSamples) { + auto processSamples = std::min(readSamples, writeSamples); + LOG(DEBUG) << __func__ << " available to read " << readSamples << " available to write " + << writeSamples << " process " << processSamples; + + inputMQ->read(buffer, processSamples); + + // call effectProcessImpl without lock + IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples); + outputMQ->write(buffer, status.fmqProduced); + statusMQ->writeBlocking(&status, 1); + LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqConsumed + << " produced " << status.fmqProduced; + } else { + // TODO: maybe add some sleep here to avoid busy waiting + } +} + } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/bassboost/BassBoostSw.cpp b/audio/aidl/default/bassboost/BassBoostSw.cpp index c52d16f897..7971dee5bc 100644 --- a/audio/aidl/default/bassboost/BassBoostSw.cpp +++ b/audio/aidl/default/bassboost/BassBoostSw.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ +#include #include +#include #define LOG_TAG "AHAL_BassBoostSw" #include -#include #include #include @@ -73,28 +74,74 @@ ndk::ScopedAStatus BassBoostSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus BassBoostSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::bassBoost != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); - mSpecificParam = specific.get(); - LOG(DEBUG) << __func__ << " success with: " << specific.toString(); - return ndk::ScopedAStatus::ok(); + auto& bbParam = specific.get(); + auto tag = bbParam.getTag(); + + switch (tag) { + case BassBoost::strengthPm: { + RETURN_IF(!mStrengthSupported, EX_ILLEGAL_ARGUMENT, "SettingStrengthNotSupported"); + + RETURN_IF(mContext->setBbStrengthPm(bbParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "strengthPmNotSupported"); + return ndk::ScopedAStatus::ok(); + } + default: { + LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "BassBoostTagNotSupported"); + } + } } ndk::ScopedAStatus BassBoostSw::getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific) { auto tag = id.getTag(); RETURN_IF(Parameter::Id::bassBoostTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag"); - specific->set(mSpecificParam); + auto bbId = id.get(); + auto bbIdTag = bbId.getTag(); + switch (bbIdTag) { + case BassBoost::Id::commonTag: + return getParameterBassBoost(bbId.get(), specific); + default: + LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "BassBoostTagNotSupported"); + } +} + +ndk::ScopedAStatus BassBoostSw::getParameterBassBoost(const BassBoost::Tag& tag, + Parameter::Specific* specific) { + RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); + BassBoost bbParam; + switch (tag) { + case BassBoost::strengthPm: { + bbParam.set(mContext->getBbStrengthPm()); + break; + } + default: { + LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "BassBoostTagNotSupported"); + } + } + + specific->set(bbParam); return ndk::ScopedAStatus::ok(); } std::shared_ptr BassBoostSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + return mContext; +} + +std::shared_ptr BassBoostSw::getContext() { return mContext; } @@ -106,13 +153,13 @@ RetCode BassBoostSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status BassBoostSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status BassBoostSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/bassboost/BassBoostSw.h b/audio/aidl/default/bassboost/BassBoostSw.h index 90a8887264..24ea65258a 100644 --- a/audio/aidl/default/bassboost/BassBoostSw.h +++ b/audio/aidl/default/bassboost/BassBoostSw.h @@ -32,7 +32,21 @@ class BassBoostSwContext final : public EffectContext { : EffectContext(statusDepth, common) { LOG(DEBUG) << __func__; } - // TODO: add specific context here + + RetCode setBbStrengthPm(int strength) { + if (strength < BassBoost::MIN_PER_MILLE_STRENGTH || + strength > BassBoost::MAX_PER_MILLE_STRENGTH) { + LOG(ERROR) << __func__ << " invalid strength: " << strength; + return RetCode::ERROR_ILLEGAL_PARAMETER; + } + // TODO : Add implementation to apply new strength + mStrength = strength; + return RetCode::SUCCESS; + } + int getBbStrengthPm() const { return mStrength; } + + private: + int mStrength; }; class BassBoostSw final : public EffectImpl { @@ -47,14 +61,20 @@ class BassBoostSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + std::string getEffectName() override { return kEffectName; }; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + private: + const std::string kEffectName = "BassBoostSw"; std::shared_ptr mContext; /* capabilities */ - const BassBoost::Capability kCapability; + const bool mStrengthSupported = true; + const BassBoost::Capability kCapability = {.strengthSupported = mStrengthSupported}; /* Effect descriptor */ const Descriptor kDescriptor = { .common = {.id = {.type = kBassBoostTypeUUID, @@ -63,11 +83,11 @@ class BassBoostSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "BassBoostSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; - /* parameters */ - BassBoost mSpecificParam; + ndk::ScopedAStatus getParameterBassBoost(const BassBoost::Tag& tag, + Parameter::Specific* specific); }; } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp index 3920a58ed7..4efd0a5178 100644 --- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp +++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus DynamicsProcessingSw::getDescriptor(Descriptor* _aidl_return) ndk::ScopedAStatus DynamicsProcessingSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -93,9 +91,13 @@ std::shared_ptr DynamicsProcessingSw::createContext( const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + return mContext; +} + +std::shared_ptr DynamicsProcessingSw::getContext() { return mContext; } @@ -107,13 +109,13 @@ RetCode DynamicsProcessingSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h index 2bc2762611..3ad4f77584 100644 --- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h +++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h @@ -47,11 +47,16 @@ class DynamicsProcessingSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; }; + private: + const std::string kEffectName = "DynamicsProcessingSw"; std::shared_ptr mContext; /* capabilities */ const DynamicsProcessing::Capability kCapability; @@ -63,7 +68,7 @@ class DynamicsProcessingSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "DynamicsProcessingSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/envReverb/EnvReverbSw.cpp b/audio/aidl/default/envReverb/EnvReverbSw.cpp index ad447abb76..cb09293e28 100644 --- a/audio/aidl/default/envReverb/EnvReverbSw.cpp +++ b/audio/aidl/default/envReverb/EnvReverbSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus EnvReverbSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus EnvReverbSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::reverb != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -92,9 +90,14 @@ ndk::ScopedAStatus EnvReverbSw::getParameterSpecific(const Parameter::Id& id, std::shared_ptr EnvReverbSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr EnvReverbSw::getContext() { return mContext; } @@ -106,13 +109,13 @@ RetCode EnvReverbSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status EnvReverbSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status EnvReverbSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/envReverb/EnvReverbSw.h b/audio/aidl/default/envReverb/EnvReverbSw.h index 5a9ab2740d..e8629a25d0 100644 --- a/audio/aidl/default/envReverb/EnvReverbSw.h +++ b/audio/aidl/default/envReverb/EnvReverbSw.h @@ -47,11 +47,16 @@ class EnvReverbSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "EnvReverbSw"; std::shared_ptr mContext; /* capabilities */ const Reverb::Capability kCapability; @@ -63,7 +68,7 @@ class EnvReverbSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "EnvReverbSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/equalizer/EqualizerSw.cpp b/audio/aidl/default/equalizer/EqualizerSw.cpp index d61ef97f24..243b061f5e 100644 --- a/audio/aidl/default/equalizer/EqualizerSw.cpp +++ b/audio/aidl/default/equalizer/EqualizerSw.cpp @@ -70,7 +70,6 @@ ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus EqualizerSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::equalizer != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto& eqParam = specific.get(); @@ -117,7 +116,6 @@ ndk::ScopedAStatus EqualizerSw::getParameterSpecific(const Parameter::Id& id, ndk::ScopedAStatus EqualizerSw::getParameterEqualizer(const Equalizer::Tag& tag, Parameter::Specific* specific) { - std::lock_guard lg(mMutex); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); Equalizer eqParam; @@ -144,9 +142,14 @@ ndk::ScopedAStatus EqualizerSw::getParameterEqualizer(const Equalizer::Tag& tag, std::shared_ptr EqualizerSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr EqualizerSw::getContext() { return mContext; } @@ -158,13 +161,13 @@ RetCode EqualizerSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status EqualizerSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status EqualizerSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/equalizer/EqualizerSw.h b/audio/aidl/default/equalizer/EqualizerSw.h index aa4587a4c0..c104a896e9 100644 --- a/audio/aidl/default/equalizer/EqualizerSw.h +++ b/audio/aidl/default/equalizer/EqualizerSw.h @@ -90,11 +90,16 @@ class EqualizerSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "EqualizerSw"; std::shared_ptr mContext; /* capabilities */ const std::vector mBandFrequency = {{0, 30000, 120000}, @@ -115,7 +120,7 @@ class EqualizerSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "EqualizerSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kEqCap)}; diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp index fd5ea34239..7e86657abb 100644 --- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp +++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp @@ -73,9 +73,6 @@ ndk::ScopedAStatus HapticGeneratorSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus HapticGeneratorSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::hapticGenerator != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); - mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); return ndk::ScopedAStatus::ok(); @@ -92,9 +89,14 @@ ndk::ScopedAStatus HapticGeneratorSw::getParameterSpecific(const Parameter::Id& std::shared_ptr HapticGeneratorSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr HapticGeneratorSw::getContext() { return mContext; } @@ -106,13 +108,13 @@ RetCode HapticGeneratorSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status HapticGeneratorSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status HapticGeneratorSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h index 518aa877ed..dbd6c55696 100644 --- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h +++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h @@ -47,11 +47,16 @@ class HapticGeneratorSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "HapticGeneratorSw"; std::shared_ptr mContext; /* capabilities */ const HapticGenerator::Capability kCapability; @@ -63,7 +68,7 @@ class HapticGeneratorSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "HapticGeneratorSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h index f608e12e1b..95645d5448 100644 --- a/audio/aidl/default/include/effect-impl/EffectContext.h +++ b/audio/aidl/default/include/effect-impl/EffectContext.h @@ -16,16 +16,13 @@ #pragma once #include -#include -#include -#include -#include #include -#include #include -#include +#include #include + +#include #include "EffectTypes.h" namespace aidl::android::hardware::audio::effect { @@ -74,13 +71,10 @@ class EffectContext { std::shared_ptr getOutputDataFmq() { return mOutputMQ; } float* getWorkBuffer() { return static_cast(mWorkBuffer.data()); } - // TODO: update with actual available size - size_t availableToRead() { return mWorkBuffer.capacity(); } - size_t availableToWrite() { return mWorkBuffer.capacity(); } // reset buffer status by abandon all data and status in FMQ void resetBuffer() { - auto buffer = getWorkBuffer(); + auto buffer = static_cast(mWorkBuffer.data()); std::vector status(mStatusMQ->availableToRead()); mInputMQ->read(buffer, mInputMQ->availableToRead()); mOutputMQ->read(buffer, mOutputMQ->availableToRead()); @@ -89,9 +83,9 @@ class EffectContext { void dupeFmq(IEffect::OpenEffectReturn* effectRet) { if (effectRet) { - effectRet->statusMQ = getStatusFmq()->dupeDesc(); - effectRet->inputDataMQ = getInputDataFmq()->dupeDesc(); - effectRet->outputDataMQ = getOutputDataFmq()->dupeDesc(); + effectRet->statusMQ = mStatusMQ->dupeDesc(); + effectRet->inputDataMQ = mInputMQ->dupeDesc(); + effectRet->outputDataMQ = mOutputMQ->dupeDesc(); } } size_t getInputFrameSize() { return mInputFrameSize; } @@ -138,7 +132,8 @@ class EffectContext { protected: // common parameters int mSessionId = INVALID_AUDIO_SESSION_ID; - size_t mInputFrameSize, mOutputFrameSize; + size_t mInputFrameSize; + size_t mOutputFrameSize; Parameter::Common mCommon; aidl::android::media::audio::common::AudioDeviceDescription mOutputDevice; aidl::android::media::audio::common::AudioMode mMode; diff --git a/audio/aidl/default/include/effect-impl/EffectImpl.h b/audio/aidl/default/include/effect-impl/EffectImpl.h index d9825da7ad..f5e2aec972 100644 --- a/audio/aidl/default/include/effect-impl/EffectImpl.h +++ b/audio/aidl/default/include/effect-impl/EffectImpl.h @@ -15,45 +15,35 @@ */ #pragma once -#include -#include #include #include -#include +#include +#include + +#include "EffectContext.h" +#include "EffectThread.h" #include "EffectTypes.h" #include "effect-impl/EffectContext.h" +#include "effect-impl/EffectThread.h" #include "effect-impl/EffectTypes.h" -#include "effect-impl/EffectWorker.h" namespace aidl::android::hardware::audio::effect { -class EffectImpl : public BnEffect, public EffectWorker { +class EffectImpl : public BnEffect, public EffectThread { public: EffectImpl() = default; virtual ~EffectImpl() = default; - /** - * Each effect implementation CAN override these methods if necessary - * If you would like implement IEffect::open completely, override EffectImpl::open(), if you - * want to keep most of EffectImpl logic but have a little customize, try override openImpl(). - * openImpl() will be called at the beginning of EffectImpl::open() without lock protection. - * - * Same for closeImpl(). - */ virtual ndk::ScopedAStatus open(const Parameter::Common& common, const std::optional& specific, OpenEffectReturn* ret) override; virtual ndk::ScopedAStatus close() override; virtual ndk::ScopedAStatus command(CommandId id) override; - virtual ndk::ScopedAStatus commandStart() { return ndk::ScopedAStatus::ok(); } - virtual ndk::ScopedAStatus commandStop() { return ndk::ScopedAStatus::ok(); } - virtual ndk::ScopedAStatus commandReset() { return ndk::ScopedAStatus::ok(); } virtual ndk::ScopedAStatus getState(State* state) override; virtual ndk::ScopedAStatus setParameter(const Parameter& param) override; virtual ndk::ScopedAStatus getParameter(const Parameter::Id& id, Parameter* param) override; - virtual IEffect::Status effectProcessImpl(float* in, float* out, int process) override; virtual ndk::ScopedAStatus setParameterCommon(const Parameter& param); virtual ndk::ScopedAStatus getParameterCommon(const Parameter::Tag& tag, Parameter* param); @@ -63,21 +53,32 @@ class EffectImpl : public BnEffect, public EffectWorker { virtual ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) = 0; virtual ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific) = 0; + + virtual std::string getEffectName() = 0; + virtual IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + + /** + * Effect context methods must be implemented by each effect. + * Each effect can derive from EffectContext and define its own context, but must upcast to + * EffectContext for EffectImpl to use. + */ virtual std::shared_ptr createContext(const Parameter::Common& common) = 0; + virtual std::shared_ptr getContext() = 0; virtual RetCode releaseContext() = 0; protected: - /* - * Lock is required if effectProcessImpl (which is running in an independent thread) needs to - * access state and parameters. - */ - std::mutex mMutex; - State mState GUARDED_BY(mMutex) = State::INIT; + State mState = State::INIT; IEffect::Status status(binder_status_t status, size_t consumed, size_t produced); void cleanUp(); - private: - std::shared_ptr mContext GUARDED_BY(mMutex); + /** + * Optional CommandId handling methods for effects to override. + * For CommandId::START, EffectImpl call commandImpl before starting the EffectThread + * processing. + * For CommandId::STOP and CommandId::RESET, EffectImpl call commandImpl after stop the + * EffectThread processing. + */ + virtual ndk::ScopedAStatus commandImpl(CommandId id); }; } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h index 09a0000001..4b6cecdcc3 100644 --- a/audio/aidl/default/include/effect-impl/EffectThread.h +++ b/audio/aidl/default/include/effect-impl/EffectThread.h @@ -22,6 +22,7 @@ #include #include +#include "effect-impl/EffectContext.h" #include "effect-impl/EffectTypes.h" namespace aidl::android::hardware::audio::effect { @@ -33,7 +34,7 @@ class EffectThread { virtual ~EffectThread(); // called by effect implementation. - RetCode createThread(const std::string& name, + RetCode createThread(std::shared_ptr context, const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO); RetCode destroyThread(); RetCode startThread(); @@ -42,15 +43,43 @@ class EffectThread { // Will call process() in a loop if the thread is running. void threadLoop(); - // User of EffectThread must implement the effect processing logic in this method. - virtual void process() = 0; - const int MAX_TASK_COMM_LEN = 15; + /** + * @brief effectProcessImpl is running in worker thread which created in EffectThread. + * + * Effect implementation should think about concurrency in the implementation if necessary. + * Parameter setting usually implemented in context (derived from EffectContext), and some + * parameter maybe used in the processing, then effect implementation should consider using a + * mutex to protect these parameter. + * + * EffectThread will make sure effectProcessImpl only be called after startThread() successful + * and before stopThread() successful. + * + * @param in address of input float buffer. + * @param out address of output float buffer. + * @param samples number of samples to process. + * @return IEffect::Status + */ + virtual IEffect::Status effectProcessImpl(float* in, float* out, int samples) = 0; + + /** + * The default EffectThread::process() implementation doesn't need to lock. It will only + * access the FMQ and mWorkBuffer in EffectContext, since they will only be changed in + * EffectImpl IEffect::open() (in this case EffectThread just created and not running yet) and + * IEffect::command(CommandId::RESET) (in this case EffectThread already stopped). + * + * process() call effectProcessImpl for effect processing, and because effectProcessImpl is + * implemented by effects, process() must not hold lock before call into effectProcessImpl to + * avoid deadlock. + */ + virtual void process(); private: - std::mutex mMutex; + const int kMaxTaskNameLen = 15; + std::mutex mThreadMutex; std::condition_variable mCv; - bool mExit GUARDED_BY(mMutex) = false; - bool mStop GUARDED_BY(mMutex) = true; + bool mExit GUARDED_BY(mThreadMutex) = false; + bool mStop GUARDED_BY(mThreadMutex) = true; + std::shared_ptr mThreadContext GUARDED_BY(mThreadMutex); std::thread mThread; int mPriority; std::string mName; diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h index 6a78eab4a0..b456817b45 100644 --- a/audio/aidl/default/include/effect-impl/EffectWorker.h +++ b/audio/aidl/default/include/effect-impl/EffectWorker.h @@ -63,7 +63,7 @@ class EffectWorker : public EffectThread { // must implement by each effect implementation // TODO: consider if this interface need adjustment to handle in-place processing - virtual IEffect::Status effectProcessImpl(float* in, float* out, int processSamples) = 0; + virtual IEffect::Status effectProcessImpl(float* in, float* out, int samples) = 0; private: // make sure the context only set once. diff --git a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp index 9d2b978fa4..4015e6134a 100644 --- a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp +++ b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp @@ -73,7 +73,6 @@ ndk::ScopedAStatus LoudnessEnhancerSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus LoudnessEnhancerSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::loudnessEnhancer != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); auto& leParam = specific.get(); @@ -113,7 +112,6 @@ ndk::ScopedAStatus LoudnessEnhancerSw::getParameterSpecific(const Parameter::Id& ndk::ScopedAStatus LoudnessEnhancerSw::getParameterLoudnessEnhancer( const LoudnessEnhancer::Tag& tag, Parameter::Specific* specific) { - std::lock_guard lg(mMutex); RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); LoudnessEnhancer leParam; @@ -136,9 +134,14 @@ ndk::ScopedAStatus LoudnessEnhancerSw::getParameterLoudnessEnhancer( std::shared_ptr LoudnessEnhancerSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr LoudnessEnhancerSw::getContext() { return mContext; } @@ -150,13 +153,13 @@ RetCode LoudnessEnhancerSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status LoudnessEnhancerSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status LoudnessEnhancerSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h index 856bf0b5c6..2aa4953853 100644 --- a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h +++ b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h @@ -56,11 +56,16 @@ class LoudnessEnhancerSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "LoudnessEnhancerSw"; std::shared_ptr mContext; /* capabilities */ const LoudnessEnhancer::Capability kCapability; @@ -72,7 +77,7 @@ class LoudnessEnhancerSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "LoudnessEnhancerSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/presetReverb/PresetReverbSw.cpp b/audio/aidl/default/presetReverb/PresetReverbSw.cpp index 069d0ff87a..e1f505ea37 100644 --- a/audio/aidl/default/presetReverb/PresetReverbSw.cpp +++ b/audio/aidl/default/presetReverb/PresetReverbSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus PresetReverbSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus PresetReverbSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::reverb != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -92,9 +90,14 @@ ndk::ScopedAStatus PresetReverbSw::getParameterSpecific(const Parameter::Id& id, std::shared_ptr PresetReverbSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr PresetReverbSw::getContext() { return mContext; } @@ -106,13 +109,13 @@ RetCode PresetReverbSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status PresetReverbSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status PresetReverbSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/presetReverb/PresetReverbSw.h b/audio/aidl/default/presetReverb/PresetReverbSw.h index 75a5a9432a..6fd3a9e1d3 100644 --- a/audio/aidl/default/presetReverb/PresetReverbSw.h +++ b/audio/aidl/default/presetReverb/PresetReverbSw.h @@ -47,11 +47,16 @@ class PresetReverbSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "PresetReverbSw"; std::shared_ptr mContext; /* capabilities */ const Reverb::Capability kCapability; @@ -63,7 +68,7 @@ class PresetReverbSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "PresetReverbSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.cpp b/audio/aidl/default/virtualizer/VirtualizerSw.cpp index 9688fc8e7c..125fbee455 100644 --- a/audio/aidl/default/virtualizer/VirtualizerSw.cpp +++ b/audio/aidl/default/virtualizer/VirtualizerSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus VirtualizerSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus VirtualizerSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::virtualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -92,9 +90,14 @@ ndk::ScopedAStatus VirtualizerSw::getParameterSpecific(const Parameter::Id& id, std::shared_ptr VirtualizerSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr VirtualizerSw::getContext() { return mContext; } @@ -106,13 +109,13 @@ RetCode VirtualizerSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status VirtualizerSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status VirtualizerSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.h b/audio/aidl/default/virtualizer/VirtualizerSw.h index e4de8b321d..e77adef8a1 100644 --- a/audio/aidl/default/virtualizer/VirtualizerSw.h +++ b/audio/aidl/default/virtualizer/VirtualizerSw.h @@ -47,11 +47,16 @@ class VirtualizerSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "VirtualizerSw"; std::shared_ptr mContext; /* capabilities */ const Virtualizer::Capability kCapability; @@ -63,7 +68,7 @@ class VirtualizerSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "VirtualizerSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/visualizer/VisualizerSw.cpp b/audio/aidl/default/visualizer/VisualizerSw.cpp index 24a7bef25c..ffdf3252a2 100644 --- a/audio/aidl/default/visualizer/VisualizerSw.cpp +++ b/audio/aidl/default/visualizer/VisualizerSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus VisualizerSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus VisualizerSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::visualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -92,9 +90,14 @@ ndk::ScopedAStatus VisualizerSw::getParameterSpecific(const Parameter::Id& id, std::shared_ptr VisualizerSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr VisualizerSw::getContext() { return mContext; } @@ -106,13 +109,13 @@ RetCode VisualizerSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status VisualizerSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status VisualizerSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h index bccd6e978d..18bb10cfb5 100644 --- a/audio/aidl/default/visualizer/VisualizerSw.h +++ b/audio/aidl/default/visualizer/VisualizerSw.h @@ -47,11 +47,16 @@ class VisualizerSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "VisualizerSw"; std::shared_ptr mContext; /* capabilities */ const Visualizer::Capability kCapability; @@ -63,7 +68,7 @@ class VisualizerSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "VisualizerSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/default/volume/VolumeSw.cpp b/audio/aidl/default/volume/VolumeSw.cpp index b8af921a8a..4cc4f082b3 100644 --- a/audio/aidl/default/volume/VolumeSw.cpp +++ b/audio/aidl/default/volume/VolumeSw.cpp @@ -73,8 +73,6 @@ ndk::ScopedAStatus VolumeSw::getDescriptor(Descriptor* _aidl_return) { ndk::ScopedAStatus VolumeSw::setParameterSpecific(const Parameter::Specific& specific) { RETURN_IF(Parameter::Specific::volume != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - std::lock_guard lg(mMutex); - RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); mSpecificParam = specific.get(); LOG(DEBUG) << __func__ << " success with: " << specific.toString(); @@ -92,9 +90,14 @@ ndk::ScopedAStatus VolumeSw::getParameterSpecific(const Parameter::Id& id, std::shared_ptr VolumeSw::createContext(const Parameter::Common& common) { if (mContext) { LOG(DEBUG) << __func__ << " context already exist"; - return mContext; + } else { + mContext = std::make_shared(1 /* statusFmqDepth */, common); } - mContext = std::make_shared(1 /* statusFmqDepth */, common); + + return mContext; +} + +std::shared_ptr VolumeSw::getContext() { return mContext; } @@ -106,13 +109,13 @@ RetCode VolumeSw::releaseContext() { } // Processing method running in EffectWorker thread. -IEffect::Status VolumeSw::effectProcessImpl(float* in, float* out, int process) { +IEffect::Status VolumeSw::effectProcessImpl(float* in, float* out, int samples) { // TODO: get data buffer and process. - LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process; - for (int i = 0; i < process; i++) { + LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples; + for (int i = 0; i < samples; i++) { *out++ = *in++; } - return {STATUS_OK, process, process}; + return {STATUS_OK, samples, samples}; } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/volume/VolumeSw.h b/audio/aidl/default/volume/VolumeSw.h index 86e01c10cd..b9e554b141 100644 --- a/audio/aidl/default/volume/VolumeSw.h +++ b/audio/aidl/default/volume/VolumeSw.h @@ -47,11 +47,16 @@ class VolumeSw final : public EffectImpl { 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 createContext(const Parameter::Common& common) override; + std::shared_ptr getContext() override; RetCode releaseContext() override; + IEffect::Status effectProcessImpl(float* in, float* out, int samples) override; + std::string getEffectName() override { return kEffectName; } + private: + const std::string kEffectName = "VolumeSw"; std::shared_ptr mContext; /* capabilities */ const Volume::Capability kCapability; @@ -63,7 +68,7 @@ class VolumeSw final : public EffectImpl { .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, - .name = "VolumeSw", + .name = kEffectName, .implementor = "The Android Open Source Project"}, .capability = Capability::make(kCapability)}; diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp new file mode 100644 index 0000000000..7adf63cb91 --- /dev/null +++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp @@ -0,0 +1,185 @@ +/* + * 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 "VtsHalBassBoostTest" + +#include +#include +#include + +#include "EffectHelper.h" + +using namespace android; + +using aidl::android::hardware::audio::effect::BassBoost; +using aidl::android::hardware::audio::effect::Capability; +using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::IEffect; +using aidl::android::hardware::audio::effect::IFactory; +using aidl::android::hardware::audio::effect::kBassBoostTypeUUID; +using aidl::android::hardware::audio::effect::Parameter; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +enum ParamName { PARAM_INSTANCE_NAME, PARAM_STRENGTH }; +using BassBoostParamTestParam = + std::tuple, Descriptor::Identity>, int>; + +/* + * Testing parameter range, assuming the parameter supported by effect is in this range. + * Parameter should be within the valid range defined in the documentation, + * for any supported value test expects EX_NONE from IEffect.setParameter(), + * otherwise expect EX_ILLEGAL_ARGUMENT. + */ + +const std::vector kStrengthValues = { + std::numeric_limits::min(), + BassBoost::MIN_PER_MILLE_STRENGTH - 1, + BassBoost::MIN_PER_MILLE_STRENGTH, + (BassBoost::MIN_PER_MILLE_STRENGTH + BassBoost::MAX_PER_MILLE_STRENGTH) >> 1, + BassBoost::MAX_PER_MILLE_STRENGTH, + BassBoost::MAX_PER_MILLE_STRENGTH + 2, + std::numeric_limits::max()}; + +class BassBoostParamTest : public ::testing::TestWithParam, + public EffectHelper { + public: + BassBoostParamTest() : mParamStrength(std::get(GetParam())) { + std::tie(mFactory, mIdentity) = std::get(GetParam()); + } + + void SetUp() override { + ASSERT_NE(nullptr, mFactory); + ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mIdentity)); + + Parameter::Specific specific = getDefaultParamSpecific(); + Parameter::Common common = EffectHelper::createParamCommon( + 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, + kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */); + IEffect::OpenEffectReturn ret; + ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); + ASSERT_NE(nullptr, mEffect); + } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(close(mEffect)); + ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); + } + + Parameter::Specific getDefaultParamSpecific() { + BassBoost bb = BassBoost::make(BassBoost::MIN_PER_MILLE_STRENGTH); + Parameter::Specific specific = + Parameter::Specific::make(bb); + return specific; + } + + static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; + std::shared_ptr mFactory; + std::shared_ptr mEffect; + Descriptor::Identity mIdentity; + int mParamStrength = BassBoost::MIN_PER_MILLE_STRENGTH; + + void SetAndGetBassBoostParameters() { + for (auto& it : mTags) { + auto& tag = it.first; + auto& bb = it.second; + + // validate parameter + Descriptor desc; + ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc)); + const bool valid = isTagInRange(it.first, it.second, desc); + const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT; + + // set parameter + Parameter expectParam; + Parameter::Specific specific; + specific.set(bb); + expectParam.set(specific); + EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString(); + + // only get if parameter in range and set success + if (expected == EX_NONE) { + Parameter getParam; + Parameter::Id id; + BassBoost::Id bbId; + bbId.set(tag); + id.set(bbId); + // if set success, then get should match + EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam)); + EXPECT_EQ(expectParam, getParam); + } + } + } + + void addStrengthParam(int strength) { + BassBoost bb; + bb.set(strength); + mTags.push_back({BassBoost::strengthPm, bb}); + } + + bool isTagInRange(const BassBoost::Tag& tag, const BassBoost& bb, + const Descriptor& desc) const { + const BassBoost::Capability& bbCap = desc.capability.get(); + switch (tag) { + case BassBoost::strengthPm: { + int strength = bb.get(); + return isStrengthInRange(bbCap, strength); + } + default: + return false; + } + return false; + } + + bool isStrengthInRange(const BassBoost::Capability& cap, int strength) const { + return cap.strengthSupported && strength >= BassBoost::MIN_PER_MILLE_STRENGTH && + strength <= BassBoost::MAX_PER_MILLE_STRENGTH; + } + + private: + std::vector> mTags; + void CleanUp() { mTags.clear(); } +}; + +TEST_P(BassBoostParamTest, SetAndGetStrength) { + EXPECT_NO_FATAL_FAILURE(addStrengthParam(mParamStrength)); + SetAndGetBassBoostParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + BassBoostTest, BassBoostParamTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kBassBoostTypeUUID)), + testing::ValuesIn(kStrengthValues)), + [](const testing::TestParamInfo& info) { + auto instance = std::get(info.param); + std::string strength = std::to_string(std::get(info.param)); + std::string name = instance.second.uuid.toString() + "_strength_" + strength; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BassBoostParamTest); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +}