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
This commit is contained in:
Shraddha Basantwani 2022-11-08 14:45:07 +05:30 committed by Shunkai Yao
parent f0803cd8d5
commit f627d80d37
27 changed files with 599 additions and 222 deletions

View file

@ -25,41 +25,33 @@ ndk::ScopedAStatus EffectImpl::open(const Parameter::Common& common,
const std::optional<Parameter::Specific>& 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<Parameter::common>()) != RetCode::SUCCESS,
RETURN_IF(context->setCommon(param.get<Parameter::common>()) != RetCode::SUCCESS,
EX_ILLEGAL_ARGUMENT, "setCommFailed");
break;
case Parameter::deviceDescription:
RETURN_IF(mContext->setOutputDevice(param.get<Parameter::deviceDescription>()) !=
RETURN_IF(context->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,
RETURN_IF(context->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,
RETURN_IF(context->setAudioSource(param.get<Parameter::source>()) != RetCode::SUCCESS,
EX_ILLEGAL_ARGUMENT, "setSourceFailed");
break;
case Parameter::volumeStereo:
RETURN_IF(mContext->setVolumeStereo(param.get<Parameter::volumeStereo>()) !=
RETURN_IF(context->setVolumeStereo(param.get<Parameter::volumeStereo>()) !=
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<Parameter::common>(mContext->getCommon());
param->set<Parameter::common>(context->getCommon());
break;
}
case Parameter::deviceDescription: {
param->set<Parameter::deviceDescription>(mContext->getOutputDevice());
param->set<Parameter::deviceDescription>(context->getOutputDevice());
break;
}
case Parameter::mode: {
param->set<Parameter::mode>(mContext->getAudioMode());
param->set<Parameter::mode>(context->getAudioMode());
break;
}
case Parameter::source: {
param->set<Parameter::source>(mContext->getAudioSource());
param->set<Parameter::source>(context->getAudioSource());
break;
}
case Parameter::volumeStereo: {
param->set<Parameter::volumeStereo>(mContext->getVolumeStereo());
param->set<Parameter::volumeStereo>(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

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <memory>
#define LOG_TAG "AHAL_EffectThread"
#include <android-base/logging.h>
#include <pthread.h>
@ -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<EffectContext> 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<EffectContext> context;
{
std::lock_guard lg(mThreadMutex);
context = mThreadContext;
RETURN_VALUE_IF(!context, void(), "nullContext");
}
std::shared_ptr<EffectContext::StatusMQ> statusMQ = context->getStatusFmq();
std::shared_ptr<EffectContext::DataMQ> inputMQ = context->getInputDataFmq();
std::shared_ptr<EffectContext::DataMQ> 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

View file

@ -14,10 +14,11 @@
* limitations under the License.
*/
#include <algorithm>
#include <cstddef>
#include <memory>
#define LOG_TAG "AHAL_BassBoostSw"
#include <Utils.h>
#include <algorithm>
#include <unordered_set>
#include <android-base/logging.h>
@ -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<Parameter::Specific::bassBoost>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
return ndk::ScopedAStatus::ok();
auto& bbParam = specific.get<Parameter::Specific::bassBoost>();
auto tag = bbParam.getTag();
switch (tag) {
case BassBoost::strengthPm: {
RETURN_IF(!mStrengthSupported, EX_ILLEGAL_ARGUMENT, "SettingStrengthNotSupported");
RETURN_IF(mContext->setBbStrengthPm(bbParam.get<BassBoost::strengthPm>()) !=
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<Parameter::Specific::bassBoost>(mSpecificParam);
auto bbId = id.get<Parameter::Id::bassBoostTag>();
auto bbIdTag = bbId.getTag();
switch (bbIdTag) {
case BassBoost::Id::commonTag:
return getParameterBassBoost(bbId.get<BassBoost::Id::commonTag>(), 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<BassBoost::strengthPm>(mContext->getBbStrengthPm());
break;
}
default: {
LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"BassBoostTagNotSupported");
}
}
specific->set<Parameter::Specific::bassBoost>(bbParam);
return ndk::ScopedAStatus::ok();
}
std::shared_ptr<EffectContext> BassBoostSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<BassBoostSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<BassBoostSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<BassBoostSwContext> 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<Capability::bassBoost>(kCapability)};
/* parameters */
BassBoost mSpecificParam;
ndk::ScopedAStatus getParameterBassBoost(const BassBoost::Tag& tag,
Parameter::Specific* specific);
};
} // namespace aidl::android::hardware::audio::effect

View file

@ -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<Parameter::Specific::dynamicsProcessing>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -93,9 +91,13 @@ std::shared_ptr<EffectContext> DynamicsProcessingSw::createContext(
const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<DynamicsProcessingSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<DynamicsProcessingSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<DynamicsProcessingSwContext> 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<Capability::dynamicsProcessing>(kCapability)};

View file

@ -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<Parameter::Specific::reverb>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -92,9 +90,14 @@ ndk::ScopedAStatus EnvReverbSw::getParameterSpecific(const Parameter::Id& id,
std::shared_ptr<EffectContext> EnvReverbSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<EnvReverbSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<EnvReverbSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<EnvReverbSwContext> 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<Capability::reverb>(kCapability)};

View file

@ -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<Parameter::Specific::equalizer>();
@ -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<EffectContext> EqualizerSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<EqualizerSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<EqualizerSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<EqualizerSwContext> mContext;
/* capabilities */
const std::vector<Equalizer::BandFrequency> 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<Capability::equalizer>(kEqCap)};

View file

@ -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<Parameter::Specific::hapticGenerator>();
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<EffectContext> HapticGeneratorSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<HapticGeneratorSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<HapticGeneratorSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<HapticGeneratorSwContext> 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<Capability::hapticGenerator>(kCapability)};

View file

@ -16,16 +16,13 @@
#pragma once
#include <Utils.h>
#include <android-base/logging.h>
#include <utils/Log.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <android-base/logging.h>
#include <fmq/AidlMessageQueue.h>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include "EffectTypes.h"
namespace aidl::android::hardware::audio::effect {
@ -74,13 +71,10 @@ class EffectContext {
std::shared_ptr<DataMQ> getOutputDataFmq() { return mOutputMQ; }
float* getWorkBuffer() { return static_cast<float*>(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<float*>(mWorkBuffer.data());
std::vector<IEffect::Status> 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;

View file

@ -15,45 +15,35 @@
*/
#pragma once
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <fmq/AidlMessageQueue.h>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <fmq/AidlMessageQueue.h>
#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<Parameter::Specific>& 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<EffectContext> createContext(const Parameter::Common& common) = 0;
virtual std::shared_ptr<EffectContext> 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<EffectContext> 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

View file

@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <system/thread_defs.h>
#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<EffectContext> 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<EffectContext> mThreadContext GUARDED_BY(mThreadMutex);
std::thread mThread;
int mPriority;
std::string mName;

View file

@ -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.

View file

@ -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<Parameter::Specific::loudnessEnhancer>();
@ -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<EffectContext> LoudnessEnhancerSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<LoudnessEnhancerSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<LoudnessEnhancerSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<LoudnessEnhancerSwContext> 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<Capability::loudnessEnhancer>(kCapability)};

View file

@ -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<Parameter::Specific::reverb>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -92,9 +90,14 @@ ndk::ScopedAStatus PresetReverbSw::getParameterSpecific(const Parameter::Id& id,
std::shared_ptr<EffectContext> PresetReverbSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<PresetReverbSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<PresetReverbSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<PresetReverbSwContext> 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<Capability::reverb>(kCapability)};

View file

@ -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<Parameter::Specific::virtualizer>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -92,9 +90,14 @@ ndk::ScopedAStatus VirtualizerSw::getParameterSpecific(const Parameter::Id& id,
std::shared_ptr<EffectContext> VirtualizerSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<VirtualizerSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<VirtualizerSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<VirtualizerSwContext> 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<Capability::virtualizer>(kCapability)};

View file

@ -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<Parameter::Specific::visualizer>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -92,9 +90,14 @@ ndk::ScopedAStatus VisualizerSw::getParameterSpecific(const Parameter::Id& id,
std::shared_ptr<EffectContext> VisualizerSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<VisualizerSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<VisualizerSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<VisualizerSwContext> 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<Capability::visualizer>(kCapability)};

View file

@ -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<Parameter::Specific::volume>();
LOG(DEBUG) << __func__ << " success with: " << specific.toString();
@ -92,9 +90,14 @@ ndk::ScopedAStatus VolumeSw::getParameterSpecific(const Parameter::Id& id,
std::shared_ptr<EffectContext> VolumeSw::createContext(const Parameter::Common& common) {
if (mContext) {
LOG(DEBUG) << __func__ << " context already exist";
return mContext;
} else {
mContext = std::make_shared<VolumeSwContext>(1 /* statusFmqDepth */, common);
}
mContext = std::make_shared<VolumeSwContext>(1 /* statusFmqDepth */, common);
return mContext;
}
std::shared_ptr<EffectContext> 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

View file

@ -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<EffectContext> createContext(const Parameter::Common& common) override;
std::shared_ptr<EffectContext> 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<VolumeSwContext> 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<Capability::volume>(kCapability)};

View file

@ -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 <Utils.h>
#include <aidl/Vintf.h>
#include <limits.h>
#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<std::pair<std::shared_ptr<IFactory>, 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<int> kStrengthValues = {
std::numeric_limits<int>::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<int>::max()};
class BassBoostParamTest : public ::testing::TestWithParam<BassBoostParamTestParam>,
public EffectHelper {
public:
BassBoostParamTest() : mParamStrength(std::get<PARAM_STRENGTH>(GetParam())) {
std::tie(mFactory, mIdentity) = std::get<PARAM_INSTANCE_NAME>(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::strengthPm>(BassBoost::MIN_PER_MILLE_STRENGTH);
Parameter::Specific specific =
Parameter::Specific::make<Parameter::Specific::bassBoost>(bb);
return specific;
}
static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
std::shared_ptr<IFactory> mFactory;
std::shared_ptr<IEffect> 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<Parameter::Specific::bassBoost>(bb);
expectParam.set<Parameter::specific>(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<BassBoost::Id::commonTag>(tag);
id.set<Parameter::Id::bassBoostTag>(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<BassBoost::strengthPm>(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<Capability::bassBoost>();
switch (tag) {
case BassBoost::strengthPm: {
int strength = bb.get<BassBoost::strengthPm>();
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<std::pair<BassBoost::Tag, BassBoost>> 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<BassBoostParamTest::ParamType>& info) {
auto instance = std::get<PARAM_INSTANCE_NAME>(info.param);
std::string strength = std::to_string(std::get<PARAM_STRENGTH>(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();
}