diff --git a/src/AidlUtils.h b/src/AidlUtils.h new file mode 100644 index 0000000..e1beec4 --- /dev/null +++ b/src/AidlUtils.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +using aidl::android::media::audio::common::AudioUuid; +using aidl::android::media::audio::common::PcmType; + +inline AudioUuid stringToUuid(const char* str) { + AudioUuid uuid{}; + uint32_t tmp[10]; + if (!str || sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", tmp, + tmp + 1, tmp + 2, tmp + 3, tmp + 4, tmp + 5, tmp + 6, + tmp + 7, tmp + 8, tmp + 9) < 10) { + return uuid; + } + + uuid.timeLow = (uint32_t)tmp[0]; + uuid.timeMid = (uint16_t)tmp[1]; + uuid.timeHiAndVersion = (uint16_t)tmp[2]; + uuid.clockSeq = (uint16_t)tmp[3]; + uuid.node.insert(uuid.node.end(), {(uint8_t)tmp[4], (uint8_t)tmp[5], (uint8_t)tmp[6], + (uint8_t)tmp[7], (uint8_t)tmp[8], (uint8_t)tmp[9]}); + return uuid; +} + +constexpr size_t getPcmSampleSizeInBytes(::aidl::android::media::audio::common::PcmType pcm) { + using ::aidl::android::media::audio::common::PcmType; + switch (pcm) { + case PcmType::UINT_8_BIT: + return 1; + case PcmType::INT_16_BIT: + return 2; + case PcmType::INT_32_BIT: + return 4; + case PcmType::FIXED_Q_8_24: + return 4; + case PcmType::FLOAT_32_BIT: + return 4; + case PcmType::INT_24_BIT: + return 3; + } + return 0; +} + +constexpr size_t getChannelCount( + const ::aidl::android::media::audio::common::AudioChannelLayout& layout, + int32_t mask = std::numeric_limits::max()) { + using Tag = ::aidl::android::media::audio::common::AudioChannelLayout::Tag; + switch (layout.getTag()) { + case Tag::none: + return 0; + case Tag::invalid: + return 0; + case Tag::indexMask: + return __builtin_popcount(layout.get() & mask); + case Tag::layoutMask: + return __builtin_popcount(layout.get() & mask); + case Tag::voiceMask: + return __builtin_popcount(layout.get() & mask); + } + return 0; +} + +constexpr size_t getFrameSizeInBytes( + const ::aidl::android::media::audio::common::AudioFormatDescription& format, + const ::aidl::android::media::audio::common::AudioChannelLayout& layout) { + if (format == ::aidl::android::media::audio::common::AudioFormatDescription{}) { + // Unspecified format. + return 0; + } + using ::aidl::android::media::audio::common::AudioFormatType; + if (format.type == AudioFormatType::PCM) { + return getPcmSampleSizeInBytes(format.pcm) * getChannelCount(layout); + } else if (format.type == AudioFormatType::NON_PCM) { + // For non-PCM formats always use the underlying PCM size. The default value for + // PCM is "UINT_8_BIT", thus non-encapsulated streams have the frame size of 1. + return getPcmSampleSizeInBytes(format.pcm); + } + // Something unexpected. + return 0; +} diff --git a/src/ViPER4Aidl.cpp b/src/ViPER4Aidl.cpp index d345b58..a571d3f 100644 --- a/src/ViPER4Aidl.cpp +++ b/src/ViPER4Aidl.cpp @@ -1,45 +1,28 @@ -#include "ViPER4Aidl.h" -#include -#include +#define LOG_TAG "ViPER4AIDL" +#include "ViPER4Aidl.h" +#include "AidlUtils.h" +#include +#include +#include + +using aidl::android::hardware::audio::effect::kEventFlagDataMqUpdate; using aidl::android::hardware::audio::effect::CommandId; +using aidl::android::hardware::audio::effect::DefaultExtension; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::Flags; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::RetCode; using aidl::android::hardware::audio::effect::State; +using aidl::android::hardware::audio::effect::VendorExtension; using aidl::android::media::audio::common::AudioUuid; using aidl::android::media::audio::common::PcmType; using android::hardware::EventFlag; -/** - * EventFlag to indicate that the data FMQ is not Empty after a write. - * TODO: b/277900230, Define in future AIDL version. - */ -static constexpr uint32_t kEventFlagDataMqNotEmpty = 0x1 << 11; - -inline AudioUuid stringToUuid(const char* str) { - AudioUuid uuid{}; - uint32_t tmp[10]; - if (!str || sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", tmp, - tmp + 1, tmp + 2, tmp + 3, tmp + 4, tmp + 5, tmp + 6, - tmp + 7, tmp + 8, tmp + 9) < 10) { - return uuid; - } - - uuid.timeLow = (uint32_t)tmp[0]; - uuid.timeMid = (uint16_t)tmp[1]; - uuid.timeHiAndVersion = (uint16_t)tmp[2]; - uuid.clockSeq = (uint16_t)tmp[3]; - uuid.node.insert(uuid.node.end(), {(uint8_t)tmp[4], (uint8_t)tmp[5], (uint8_t)tmp[6], - (uint8_t)tmp[7], (uint8_t)tmp[8], (uint8_t)tmp[9]}); - return uuid; -} - -const AudioUuid kType = stringToUuid("b9bc100c-26cd-42e6-acb6-cad8c3f778de"); -const AudioUuid kUuid = stringToUuid("90380da3-8536-4744-a6a3-5731970e640f"); -const Descriptor kDescriptor = { +static const AudioUuid kType = stringToUuid("b9bc100c-26cd-42e6-acb6-cad8c3f778de"); +static const AudioUuid kUuid = stringToUuid("90380da3-8536-4744-a6a3-5731970e640f"); +static const Descriptor kDescriptor = { .common = { .id = { .type = kType, @@ -49,128 +32,51 @@ const Descriptor kDescriptor = { .flags = { .type = Flags::Type::INSERT, .insert = Flags::Insert::LAST, - .volume = Flags::Volume::NONE }, .name = "ViPER4Android", .implementor = "Iscle", }, }; -constexpr size_t getPcmSampleSizeInBytes(::aidl::android::media::audio::common::PcmType pcm) { - using ::aidl::android::media::audio::common::PcmType; - switch (pcm) { - case PcmType::UINT_8_BIT: - return 1; - case PcmType::INT_16_BIT: - return 2; - case PcmType::INT_32_BIT: - return 4; - case PcmType::FIXED_Q_8_24: - return 4; - case PcmType::FLOAT_32_BIT: - return 4; - case PcmType::INT_24_BIT: - return 3; - } - return 0; -} - -constexpr size_t getChannelCount( - const ::aidl::android::media::audio::common::AudioChannelLayout& layout, - int32_t mask = std::numeric_limits::max()) { - using Tag = ::aidl::android::media::audio::common::AudioChannelLayout::Tag; - switch (layout.getTag()) { - case Tag::none: - return 0; - case Tag::invalid: - return 0; - case Tag::indexMask: - return __builtin_popcount(layout.get() & mask); - case Tag::layoutMask: - return __builtin_popcount(layout.get() & mask); - case Tag::voiceMask: - return __builtin_popcount(layout.get() & mask); - } - return 0; -} - -constexpr size_t getFrameSizeInBytes( - const ::aidl::android::media::audio::common::AudioFormatDescription& format, - const ::aidl::android::media::audio::common::AudioChannelLayout& layout) { - if (format == ::aidl::android::media::audio::common::AudioFormatDescription{}) { - // Unspecified format. - return 0; - } - using ::aidl::android::media::audio::common::AudioFormatType; - if (format.type == AudioFormatType::PCM) { - return getPcmSampleSizeInBytes(format.pcm) * getChannelCount(layout); - } else if (format.type == AudioFormatType::NON_PCM) { - // For non-PCM formats always use the underlying PCM size. The default value for - // PCM is "UINT_8_BIT", thus non-encapsulated streams have the frame size of 1. - return getPcmSampleSizeInBytes(format.pcm); - } - // Something unexpected. - return 0; -} - -//int ViPER4AndroidAIDL::notifyEventFlag(uint32_t flag) { -// if (!mEventFlag) { -// ALOGE("notifyEventFlag: StatusEventFlag invalid"); -// return -1; -// } -// if (const auto ret = mEventFlag->wake(flag); ret != ::android::OK) { -// ALOGE("notifyEventFlag: wake failure with ret %d", ret); -// return -1; -// } -// return 0; -//} - -ndk::ScopedAStatus ViPER4AndroidAIDL::open(const Parameter::Common &common, - const std::optional &specific, - IEffect::OpenEffectReturn *ret) { - ALOGD("open called"); +ndk::ScopedAStatus ViPER4AIDL::open(const Parameter::Common &common, + const std::optional &specific, + IEffect::OpenEffectReturn *ret) { if (common.input.base.format.pcm != PcmType::FLOAT_32_BIT || common.output.base.format.pcm != PcmType::FLOAT_32_BIT) { - ALOGE("open called with invalid PCM type"); + ALOGE("open: unsupported PCM type (input: %d, output: %d)", + static_cast(common.input.base.format.pcm), + static_cast(common.output.base.format.pcm)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } std::lock_guard lg(mImplMutex); - ALOGD("mImplMutex locked"); - if (mState != State::INIT) { ALOGD("open: already opened"); return ndk::ScopedAStatus::ok(); } - size_t mInputFrameSize = getFrameSizeInBytes( + size_t inputFrameSize = getFrameSizeInBytes( common.input.base.format, common.input.base.channelMask); - size_t mOutputFrameSize = getFrameSizeInBytes( + size_t outputFrameSize = getFrameSizeInBytes( common.output.base.format, common.output.base.channelMask); - size_t inBufferSizeInFloat = common.input.frameCount * mInputFrameSize / sizeof(float); - size_t outBufferSizeInFloat = common.output.frameCount * mOutputFrameSize / sizeof(float); + /* EffectContext constructor start */ + mCommon = common; + size_t inBufferSizeInFloat = common.input.frameCount * inputFrameSize / sizeof(float); + size_t outBufferSizeInFloat = common.output.frameCount * outputFrameSize / sizeof(float); ALOGD("open: inBufferSizeInFloat %zu, outBufferSizeInFloat %zu", inBufferSizeInFloat, outBufferSizeInFloat); // only status FMQ use the EventFlag - mStatusMQ = std::make_shared(1, true /*configureEventFlagWord*/); + mStatusMQ = std::make_shared(1, true /* configureEventFlagWord */); mInputMQ = std::make_shared(inBufferSizeInFloat); mOutputMQ = std::make_shared(outBufferSizeInFloat); - if (!mStatusMQ || !mInputMQ || !mOutputMQ) { - ALOGE("open: failed to create message queues"); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - - ALOGD("message queues created"); - ALOGD("open: mStatusMQ %p, mInputMQ %p, mOutputMQ %p", mStatusMQ.get(), mInputMQ.get(), - mOutputMQ.get()); - if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) { - ALOGE("open: failed to create message queues"); + ALOGE("open: invalid FMQ (status: %d, input: %d, output: %d)", + mStatusMQ->isValid(), mInputMQ->isValid(), mOutputMQ->isValid()); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } @@ -181,11 +87,8 @@ ndk::ScopedAStatus ViPER4AndroidAIDL::open(const Parameter::Common &common, return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - ALOGD("open: event flag created"); - mWorkBuffer.resize(std::max(inBufferSizeInFloat, outBufferSizeInFloat)); - - ALOGD("open: work buffer size %zu", mWorkBuffer.size()); + /* EffectContext constructor end */ if (specific.has_value()) { ALOGD("open: specific parameters provided, ignoring for now..."); @@ -193,143 +96,475 @@ ndk::ScopedAStatus ViPER4AndroidAIDL::open(const Parameter::Common &common, mState = State::IDLE; - ALOGD("open: state set to IDLE"); - - ret->statusMQ = mStatusMQ->dupeDesc(); - ret->inputDataMQ = mInputMQ->dupeDesc(); - ret->outputDataMQ = mOutputMQ->dupeDesc(); - - ALOGD("open: mq descriptors set"); + dupeFmq(ret); if (createThread("ViPER4Android") != RetCode::SUCCESS) { ALOGE("open: failed to create thread"); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } - ALOGD("open: thread created"); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ViPER4AIDL::close() { + { + std::lock_guard lg(mImplMutex); + + if (mState == State::INIT) { + ALOGD("close: already closed"); + return ndk::ScopedAStatus::ok(); + } + + if (mState == State::PROCESSING) { + ALOGE("close: cannot close while processing"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + + mState = State::INIT; + } + + if (notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS) { + ALOGE("close: failed to notify not empty event flag"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + + if (destroyThread() != RetCode::SUCCESS) { + ALOGE("close: failed to destroy thread"); + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + + { + std::lock_guard lg(mImplMutex); + EventFlag::deleteEventFlag(&mEventFlag); + } return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus ViPER4AndroidAIDL::close() { - ALOGD("close called"); - return ndk::ScopedAStatus::ok(); -} - -ndk::ScopedAStatus -ViPER4AndroidAIDL::getDescriptor(Descriptor *descriptor) { - ALOGD("getDescriptor: returning descriptor"); +ndk::ScopedAStatus ViPER4AIDL::getDescriptor(Descriptor *descriptor) { + if (descriptor == nullptr) { + ALOGE("getDescriptor: descriptor is null"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } *descriptor = kDescriptor; return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus ViPER4AndroidAIDL::command(CommandId id) { +ndk::ScopedAStatus ViPER4AIDL::command(CommandId command) { std::lock_guard lg(mImplMutex); + if (mState == State::INIT) { ALOGE("command: instance not open"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - switch (id) { + switch (command) { case CommandId::START: { - ALOGD("command: START"); - if (mState == State::PROCESSING) { + ALOGD("command: already started"); return ndk::ScopedAStatus::ok(); } + + //RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed"); + mState = State::PROCESSING; -// if (notifyEventFlag() != 0) { -// ALOGE("id: failed to notify event flag"); -// return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); -// } + if (notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS) { + ALOGE("command: failed to notify not empty event flag"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + startThread(); + break; } case CommandId::STOP: { - ALOGD("command: STOP"); - if (mState == State::IDLE) { + ALOGD("command: already stopped"); return ndk::ScopedAStatus::ok(); } + mState = State::IDLE; + if (notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS) { + ALOGE("command: failed to notify not empty event flag"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + stopThread(); + + //RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed"); + break; } case CommandId::RESET: { - ALOGD("command: RESET"); mState = State::IDLE; + + if (notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS) { + ALOGE("command: failed to notify not empty event flag"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + stopThread(); + + resetBuffer(); + //RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed"); + break; } default: - ALOGE("command: unknown command %d", static_cast(id)); + ALOGE("command: unknown command %d", static_cast(command)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus ViPER4AndroidAIDL::getState(State *state) { - ALOGD("getState: returning state"); +ndk::ScopedAStatus ViPER4AIDL::getState(State *state) { + if (state == nullptr) { + ALOGE("getState: state is null"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } *state = mState; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus -ViPER4AndroidAIDL::setParameter(const Parameter ¶meter) { - ALOGD("setParameter called"); - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +ViPER4AIDL::setParameter(const Parameter ¶meter) { + std::lock_guard lg(mImplMutex); + + const auto &tag = parameter.getTag(); + switch (tag) { + case Parameter::Tag::common: { + auto common = parameter.get(); + if (common.input.base.format.pcm != PcmType::FLOAT_32_BIT || + common.output.base.format.pcm != PcmType::FLOAT_32_BIT) { + ALOGE("setParameter: common: unsupported PCM type (input: %d, output: %d)", + static_cast(common.input.base.format.pcm), + static_cast(common.output.base.format.pcm)); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + +#if VIPER_AIDL_VERSION >= 2 + if (mWorkBuffer.size() != 0 && mInputMQ != nullptr && mOutputMQ != nullptr) { + size_t prevInputFrameSize = getFrameSizeInBytes( + mCommon.input.base.format, mCommon.input.base.channelMask); + size_t prevOutputFrameSize = getFrameSizeInBytes( + mCommon.output.base.format, mCommon.output.base.channelMask); + + size_t inputFrameSize = getFrameSizeInBytes( + common.input.base.format, common.input.base.channelMask); + size_t outputFrameSize = getFrameSizeInBytes( + common.output.base.format, common.output.base.channelMask); + + bool needUpdateMq = false; + if (inputFrameSize != prevInputFrameSize || + mCommon.input.frameCount != common.input.frameCount) { + ALOGD("setParameter: common: reset input MQ"); + mInputMQ.reset(); + needUpdateMq = true; + } + if (outputFrameSize != prevOutputFrameSize || + mCommon.output.frameCount != common.output.frameCount) { + ALOGD("setParameter: common: reset output MQ"); + mOutputMQ.reset(); + needUpdateMq = true; + } + + ALOGD("setParameter: common: inBufferSizeInFloat %zu, outBufferSizeInFloat %zu, needUpdateMq %d", + (size_t) (common.input.frameCount * inputFrameSize / sizeof(float)), + (size_t) (common.output.frameCount * outputFrameSize / sizeof(float)), + needUpdateMq); + + if (needUpdateMq && mEventFlag->wake(kEventFlagDataMqUpdate) != ::android::OK) { + ALOGE("setParameter: common: failed to wake event flag"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + } +#endif + mCommon = common; + return ndk::ScopedAStatus::ok(); + } + case Parameter::Tag::specific: { + auto specific = parameter.get(); + if (specific.getTag() != Parameter::Specific::Tag::vendorEffect) { + ALOGE("setParameter: specific: unsupported tag %d", + static_cast(specific.getTag())); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + auto vendorEffect = specific.get(); + std::optional defaultExtension; + if (vendorEffect.extension.getParcelable(&defaultExtension) != STATUS_OK || !defaultExtension.has_value()) { + ALOGE("setParameter: specific: failed to get default extension"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + auto data = defaultExtension->bytes; + + ALOGD("setParameter: specific: vendorEffect data size %zu", data.size()); + + int32_t ret = 0; + uint32_t retSize = sizeof(ret); + if (viperContext.handleCommand( + EFFECT_CMD_SET_PARAM, + data.size(), + data.data(), + &retSize, + &ret + ) != 0) { + ALOGE("setParameter: specific: failed to handle command"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (ret != 0) { + ALOGE("setParameter: specific: command failed with ret %d", ret); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + return ndk::ScopedAStatus::ok(); + } + default: + ALOGE("setParameter: unsupported parameter tag %d", static_cast(tag)); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } } ndk::ScopedAStatus -ViPER4AndroidAIDL::getParameter(const Parameter::Id &id, Parameter *param) { - ALOGD("getParameter called"); - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +ViPER4AIDL::getParameter(const Parameter::Id &id, Parameter *param) { + if (param == nullptr) { + ALOGE("getParameter: param is null"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + std::lock_guard lg(mImplMutex); + + const auto &tag = id.getTag(); + switch (tag) { + case Parameter::Id::commonTag: { + auto commonTag = id.get(); + if (commonTag != Parameter::Tag::common) { + ALOGE("getParameter: commonTag: unsupported tag %d", + static_cast(commonTag)); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + param->set(mCommon); + return ndk::ScopedAStatus::ok(); + } + case Parameter::Id::vendorEffectTag: { + auto vendorEffectTag = id.get(); + std::optional cmdDefaultExtension; + if (vendorEffectTag.extension.getParcelable(&cmdDefaultExtension) != STATUS_OK || !cmdDefaultExtension.has_value()) { + ALOGE("getParameter: vendorEffectTag: failed to get default extension"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + VendorExtension replyVendorExtension; + DefaultExtension replyDefaultExtension; + replyDefaultExtension.bytes.resize(sizeof(effect_param_t) + 2 * sizeof(int32_t)); + + auto cmdData = cmdDefaultExtension->bytes; + auto replyData = replyDefaultExtension.bytes; + uint32_t replySize = replyData.size(); + + if (viperContext.handleCommand( + EFFECT_CMD_GET_PARAM, + cmdData.size(), + cmdData.data(), + &replySize, + replyData.data() + ) != 0) { + ALOGE("getParameter: vendorEffectTag: failed to handle command"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + replyData.resize(replySize); + + if (replyVendorExtension.extension.setParcelable(replyDefaultExtension) != STATUS_OK) { + ALOGE("getParameter: vendorEffectTag: failed to set default extension"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + Parameter::Specific specific; + specific.set(replyVendorExtension); + param->set(specific); + + return ndk::ScopedAStatus::ok(); + } + default: + ALOGD("getParameter: unsupported parameter tag %d", static_cast(tag)); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } } #if VIPER_AIDL_VERSION >= 2 -ndk::ScopedAStatus -ViPER4AndroidAIDL::reopen(IEffect::OpenEffectReturn *open_effect_return) { +ndk::ScopedAStatus ViPER4AIDL::reopen(IEffect::OpenEffectReturn *ret) { std::lock_guard lg(mImplMutex); + if (mState == State::INIT) { ALOGE("reopen: already closed"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - // TODO + + ALOGD("reopen: reopening effect"); + + dupeFmqWithReopen(ret); + return ndk::ScopedAStatus::ok(); } #endif -void ViPER4AndroidAIDL::process() { - ALOGD("process called"); -} - -extern "C" binder_exception_t createEffect(const AudioUuid *audio_uuid, std::shared_ptr *instance) { - if (audio_uuid == nullptr || instance == nullptr) { - ALOGE("createEffect called with null arguments"); - return EX_ILLEGAL_ARGUMENT; +void ViPER4AIDL::process() { + /** + * wait for the EventFlag without lock, it's ok because the mEventFlag pointer will not change + * in the life cycle of workerThread (threadLoop). + */ + uint32_t efState = 0; + if (!mEventFlag || mEventFlag->wait(mDataMqNotEmptyEf, &efState, 0 /* no timeout */, true /* retry */) != ::android::OK || !(efState & mDataMqNotEmptyEf)) { + ALOGE("process: mEventFlag - %p, efState - %x", mEventFlag, efState); + return; + } + + { + std::lock_guard lg(mImplMutex); +#if VIPER_AIDL_VERSION >= 3 + if (mState != State::PROCESSING && mState != State::DRAINING) { +#else + if (mState != State::PROCESSING) { +#endif + ALOGD("process: skip process in state: %s", toString(mState).c_str()); + return; + } + + ALOGD("process: processing in state: %s, mInputMQ - %p, mOutputMQ - %p", + toString(mState).c_str(), mInputMQ.get(), mOutputMQ.get()); + + if (!mInputMQ || !mOutputMQ) { + ALOGE("process: mInputMQ or mOutputMQ is null"); + return; + } + + assert(mWorkBuffer.size() >= std::max(mInputMQ->availableToRead(), mOutputMQ->availableToWrite())); + auto processSamples = std::min(mInputMQ->availableToRead(), mOutputMQ->availableToWrite()); + if (processSamples) { + auto buffer = static_cast(mWorkBuffer.data()); + mInputMQ->read(buffer, processSamples); + IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples); + mOutputMQ->write(buffer, status.fmqProduced); + mStatusMQ->writeBlocking(&status, 1); + } } - ALOGD("createEffect: creating effect instance"); - *instance = ndk::SharedRefBase::make(); - return EX_NONE; } -extern "C" binder_exception_t destroyEffect(const std::shared_ptr &instance) { - ALOGD("destroyEffect called"); - return EX_ILLEGAL_STATE; +void ViPER4AIDL::dupeFmq(IEffect::OpenEffectReturn* ret) { + ALOGD("dupeFmq: mStatusMQ - %p, mInputMQ - %p, mOutputMQ - %p", + mStatusMQ.get(), mInputMQ.get(), mOutputMQ.get()); + if (ret && mStatusMQ && mInputMQ && mOutputMQ) { + ret->statusMQ = mStatusMQ->dupeDesc(); + ret->inputDataMQ = mInputMQ->dupeDesc(); + ret->outputDataMQ = mOutputMQ->dupeDesc(); + } +} + +void ViPER4AIDL::dupeFmqWithReopen(IEffect::OpenEffectReturn* ret) { + size_t inputFrameSize = getFrameSizeInBytes( + mCommon.input.base.format, mCommon.input.base.channelMask); + size_t outputFrameSize = getFrameSizeInBytes( + mCommon.output.base.format, mCommon.output.base.channelMask); + const size_t inBufferSizeInFloat = mCommon.input.frameCount * inputFrameSize / sizeof(float); + const size_t outBufferSizeInFloat = mCommon.output.frameCount * outputFrameSize / sizeof(float); + const size_t bufferSize = std::max(inBufferSizeInFloat, outBufferSizeInFloat); + if (!mInputMQ) { + mInputMQ = std::make_shared(inBufferSizeInFloat); + } + if (!mOutputMQ) { + mOutputMQ = std::make_shared(outBufferSizeInFloat); + } + if (mWorkBuffer.size() != bufferSize) { + mWorkBuffer.resize(bufferSize); + } + dupeFmq(ret); +} + +RetCode ViPER4AIDL::notifyEventFlag(uint32_t flag) { + if (!mEventFlag) { + ALOGE("notifyEventFlag: StatusEventFlag invalid"); + return RetCode::ERROR_EVENT_FLAG_ERROR; + } + if (const auto ret = mEventFlag->wake(flag); ret != ::android::OK) { + ALOGE("notifyEventFlag: wake failure with ret %d", ret); + return RetCode::ERROR_EVENT_FLAG_ERROR; + } + return RetCode::SUCCESS; +} + +// reset buffer status by abandon input data in FMQ +void ViPER4AIDL::resetBuffer() { + if (mStatusMQ) { + std::vector status(mStatusMQ->availableToRead()); + } + if (mInputMQ) { + auto buffer = static_cast(mWorkBuffer.data()); + mInputMQ->read(buffer, mInputMQ->availableToRead()); + } +} + +// A placeholder processing implementation to copy samples from input to output +IEffect::Status ViPER4AIDL::effectProcessImpl(float* in, float* out, int samples) { + viperContext.process(in, out, samples); + return {STATUS_OK, samples, samples}; } extern "C" binder_exception_t queryEffect(const AudioUuid *audio_uuid, Descriptor *descriptor) { if (audio_uuid == nullptr || descriptor == nullptr) { - ALOGE("queryEffect called with null arguments"); + ALOGE("queryEffect: audio_uuid or descriptor is null"); return EX_ILLEGAL_ARGUMENT; } if (*audio_uuid != kUuid) { - ALOGE("queryEffect called with invalid uuid"); + ALOGE("queryEffect: invalid uuid"); return EX_ILLEGAL_ARGUMENT; } - ALOGD("queryEffect: returning descriptor"); *descriptor = kDescriptor; return EX_NONE; } + +extern "C" binder_exception_t createEffect(const AudioUuid *audio_uuid, std::shared_ptr *instanceSp) { + if (audio_uuid == nullptr || instanceSp == nullptr) { + ALOGE("createEffect: audio_uuid or instanceSp is null"); + return EX_ILLEGAL_ARGUMENT; + } + *instanceSp = ndk::SharedRefBase::make(); + return EX_NONE; +} + +extern "C" binder_exception_t destroyEffect(const std::shared_ptr &instanceSp) { + if (!instanceSp) { + ALOGE("destroyEffect: instanceSp is null"); + return EX_ILLEGAL_ARGUMENT; + } + + Descriptor desc; + ndk::ScopedAStatus status = instanceSp->getDescriptor(&desc); + if (!status.isOk()) { + ALOGE("destroyEffect: failed to get descriptor, status: %s", status.getDescription().c_str()); + return EX_ILLEGAL_STATE; + } + + State state; + status = instanceSp->getState(&state); + if (!status.isOk()) { + ALOGE("destroyEffect: failed to get state, status: %s", status.getDescription().c_str()); + return EX_ILLEGAL_STATE; + } + +#if VIPER_AIDL_VERSION >= 3 + instanceSp->command(CommandId::RESET); + instanceSp->close(); +#else + if (state != State::INIT) { + ALOGE("destroyEffect: can not destroy instance in state: %s", toString(state).c_str()); + return EX_ILLEGAL_STATE; + } +#endif + + return EX_NONE; +} diff --git a/src/ViPER4Aidl.h b/src/ViPER4Aidl.h index 5c5934d..79ce668 100644 --- a/src/ViPER4Aidl.h +++ b/src/ViPER4Aidl.h @@ -5,27 +5,36 @@ #include #include #include +#include "ViperContext.h" using aidl::android::hardware::common::fmq::SynchronizedReadWrite; +#if VIPER_AIDL_VERSION >= 2 +using aidl::android::hardware::audio::effect::kEventFlagDataMqNotEmpty; +#else +using aidl::android::hardware::audio::effect::kEventFlagNotEmpty; +#endif + using aidl::android::hardware::audio::effect::BnEffect; using aidl::android::hardware::audio::effect::CommandId; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::EffectThread; using aidl::android::hardware::audio::effect::Parameter; +using aidl::android::hardware::audio::effect::RetCode; using aidl::android::hardware::audio::effect::State; +using android::hardware::EventFlag; -class ViPER4AndroidAIDL : public BnEffect, public EffectThread { +class ViPER4AIDL : public BnEffect, public EffectThread { public: // BnEffect ndk::ScopedAStatus open(const Parameter::Common &common, const std::optional &specific, IEffect::OpenEffectReturn *ret) override; ndk::ScopedAStatus close() override; ndk::ScopedAStatus getDescriptor(Descriptor *_aidl_return) override; ndk::ScopedAStatus command(CommandId id) override; - ndk::ScopedAStatus getState(State *_aidl_return) override; + ndk::ScopedAStatus getState(State *state) override; ndk::ScopedAStatus setParameter(const Parameter &in_param) override; - ndk::ScopedAStatus getParameter(const Parameter::Id &in_paramId, Parameter *_aidl_return) override; + ndk::ScopedAStatus getParameter(const Parameter::Id &in_paramId, Parameter *param) override; #if VIPER_AIDL_VERSION >= 2 - ndk::ScopedAStatus reopen(IEffect::OpenEffectReturn *_aidl_return) override; + ndk::ScopedAStatus reopen(IEffect::OpenEffectReturn *ret) override; #endif // EffectThread @@ -34,12 +43,28 @@ private: typedef android::AidlMessageQueue StatusMQ; typedef android::AidlMessageQueue DataMQ; + void dupeFmq(IEffect::OpenEffectReturn* ret); + void dupeFmqWithReopen(IEffect::OpenEffectReturn* ret); + RetCode notifyEventFlag(uint32_t flag); + void resetBuffer(); + IEffect::Status effectProcessImpl(float* in, float* out, int samples); + std::mutex mImplMutex; State mState = State::INIT; +#if VIPER_AIDL_VERSION >= 2 + static const int mDataMqNotEmptyEf = kEventFlagDataMqNotEmpty; +#else + static const int mDataMqNotEmptyEf = kEventFlagNotEmpty; +#endif + + Parameter::Common mCommon; + std::shared_ptr mStatusMQ; std::shared_ptr mInputMQ; std::shared_ptr mOutputMQ; - android::hardware::EventFlag *mEventFlag; + EventFlag *mEventFlag; std::vector mWorkBuffer; + + ViperContext viperContext; }; diff --git a/src/ViperContext.cpp b/src/ViperContext.cpp index bbd265c..9a1da81 100644 --- a/src/ViperContext.cpp +++ b/src/ViperContext.cpp @@ -2,7 +2,7 @@ #include #include #include "ViperContext.h" -#include +#include #include #define SET(type, ptr, value) (*(type *) (ptr) = (value)) @@ -840,7 +840,7 @@ int32_t ViperContext::process(audio_buffer_t *inBuffer, audio_buffer_t *outBuffe return -EINVAL; } - viper.process(buffer, frameCount); + viper.process(buffer.data(), frameCount); const bool accumulate = config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE; switch (config.outputCfg.format) { @@ -860,6 +860,14 @@ int32_t ViperContext::process(audio_buffer_t *inBuffer, audio_buffer_t *outBuffe return 0; } +int32_t ViperContext::process(float *inBuffer, float *outBuffer, size_t count) { + memcpy(outBuffer, inBuffer, count * sizeof(float)); + // The viper process function expects the count to be the number of + // stereo frames, not the number of samples. Thus, we divide by 2. + viper.process(outBuffer, count / 2); + return 0; +} + void ViperContext::setDisableReason(DisableReason reason) { this->disableReason = reason; } diff --git a/src/ViperContext.h b/src/ViperContext.h index 00da196..1a68062 100644 --- a/src/ViperContext.h +++ b/src/ViperContext.h @@ -21,6 +21,7 @@ public: int32_t handleCommand(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData); int32_t process(audio_buffer_t *inBuffer, audio_buffer_t *outBuffer); + int32_t process(float *inBuffer, float *outBuffer, size_t count); private: effect_config_t config; diff --git a/src/include/constants.h b/src/include/constants.h index 91bdd8e..5508daa 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -6,7 +6,7 @@ #include #endif -#include // TODO: Remove this dependency +#include // TODO: Remove this dependency enum class Architecture : uint8_t { UNKNOWN = 0, diff --git a/src/include/log.h b/src/include/log.h deleted file mode 100644 index 2f88b74..0000000 --- a/src/include/log.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#ifndef LOG_TAG -#define LOG_TAG "ViPER4Android" -#endif - -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - - -/* - * Basic log message macros intended to emulate the behavior of log/log.h - * in system core. This should be dependent only on ndk exposed logging - * functionality. - */ - -#ifndef ALOG -#define ALOG(priority, tag, ...) \ - __android_log_print(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -#ifndef ALOGV -#if LOG_NDEBUG -#define ALOGV(...) ((void)0) -#else -#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#ifndef ALOGD -#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGI -#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGW -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGE -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGF -#define ALOGF(...) ((void)ALOG(LOG_FATAL, LOG_TAG, __VA_ARGS__)) -#endif - -/* - * Log a fatal error if cond is true. The condition test is inverted from - * assert(3) semantics. The test and message are not stripped from release - * builds - */ -#ifndef ALOG_ALWAYS_FATAL_IF -#define ALOG_ALWAYS_FATAL_IF(cond, ...) \ - if (cond) __android_log_assert(#cond, LOG_TAG, __VA_ARGS__) -#endif diff --git a/src/viper/ViPER.cpp b/src/viper/ViPER.cpp index 39e302a..0c0fc1a 100644 --- a/src/viper/ViPER.cpp +++ b/src/viper/ViPER.cpp @@ -1,7 +1,7 @@ #include "ViPER.h" #include #include -#include +#include ViPER::ViPER() : adaptiveBuffer(2, 4096), @@ -86,7 +86,7 @@ ViPER::ViPER() : } } -void ViPER::process(std::vector& buffer, uint32_t size) { +void ViPER::process(float *buffer, uint32_t size) { this->frameCount += size; uint32_t ret; @@ -96,7 +96,7 @@ void ViPER::process(std::vector& buffer, uint32_t size) { if (this->convolver.GetEnabled() || this->vhe.GetEnabled()) { // ALOGD("Convolver or VHE is enable, use wave buffer"); - if (!this->waveBuffer.PushSamples(buffer.data(), size)) { + if (!this->waveBuffer.PushSamples(buffer, size)) { this->waveBuffer.Reset(); return; } @@ -121,7 +121,7 @@ void ViPER::process(std::vector& buffer, uint32_t size) { } else { // ALOGD("Convolver and VHE are disabled, use adaptive buffer"); - if (this->adaptiveBuffer.PushFrames(buffer.data(), size)) { + if (this->adaptiveBuffer.PushFrames(buffer, size)) { this->adaptiveBuffer.SetBufferOffset(size); tmpBuf = this->adaptiveBuffer.GetBuffer(); @@ -159,7 +159,7 @@ void ViPER::process(std::vector& buffer, uint32_t size) { tmpBuf[i + 1] = this->softwareLimiters[1].Process(tmpBuf[i + 1]); } - if (!this->adaptiveBuffer.PopFrames(buffer.data(), tmpBufSize)) { + if (!this->adaptiveBuffer.PopFrames(buffer, tmpBufSize)) { this->adaptiveBuffer.FlushBuffer(); return; } @@ -169,8 +169,8 @@ void ViPER::process(std::vector& buffer, uint32_t size) { } } - memmove(buffer.data() + (size - tmpBufSize) * 2, buffer.data(), tmpBufSize * sizeof(float)); - memset(buffer.data(), 0, (size - tmpBufSize) * sizeof(float)); + memmove(buffer + (size - tmpBufSize) * 2, buffer, tmpBufSize * sizeof(float)); + memset(buffer, 0, (size - tmpBufSize) * sizeof(float)); } //void ViPER::DispatchCommand(int param, int val1, int val2, int val3, int val4, uint32_t arrSize, diff --git a/src/viper/ViPER.h b/src/viper/ViPER.h index 72b6336..e147a6e 100644 --- a/src/viper/ViPER.h +++ b/src/viper/ViPER.h @@ -27,7 +27,7 @@ class ViPER { public: ViPER(); - void process(std::vector& buffer, uint32_t size); + void process(float *buffer, uint32_t size); void reset(); uint64_t getFrameCount(); void setSamplingRate(uint32_t samplingRate); diff --git a/src/viper/effects/ViPERDDC.cpp b/src/viper/effects/ViPERDDC.cpp index 090e6d9..e332beb 100644 --- a/src/viper/effects/ViPERDDC.cpp +++ b/src/viper/effects/ViPERDDC.cpp @@ -1,5 +1,5 @@ #include "ViPERDDC.h" -#include +#include #include #include