Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eb2a2c1 authored by Shunkai Yao's avatar Shunkai Yao Committed by Gerrit Code Review
Browse files

Merge changes from topic "effect_reopen" into main

* changes:
  Effect AIDL: implement IEffect.reopen
  Effect AIDL: add IEffect.reopen to update the effect instances data FMQ
parents f005be18 65c7c705
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ interface IEffect {
  android.hardware.audio.effect.State getState();
  void setParameter(in android.hardware.audio.effect.Parameter param);
  android.hardware.audio.effect.Parameter getParameter(in android.hardware.audio.effect.Parameter.Id paramId);
  android.hardware.audio.effect.IEffect.OpenEffectReturn reopen();
  @FixedSize @VintfStability
  parcelable Status {
    int status;
+33 −4
Original line number Diff line number Diff line
@@ -54,7 +54,16 @@ interface IEffect {
    }

    /**
     * Return data structure of IEffect.open() interface.
     * Return data structure of the IEffect.open() and IEffect.reopen() method.
     *
     * Contains Fast Message Queues (FMQs) for effect processing status and input/output data.
     * The status FMQ remains valid and unchanged after opening, while input/output data FMQs can be
     * modified with changes in input/output AudioConfig. If reallocation of data FMQ is necessary,
     * the effect instance must release the existing data FMQs to signal the need to the audio
     * framework.
     * When the audio framework encounters a valid status FMQ and invalid input/output data FMQs,
     * it must invoke the IEffect.reopen() method to request the effect instance to reallocate
     * the FMQ and return to the audio framework.
     */
    @VintfStability
    parcelable OpenEffectReturn {
@@ -97,7 +106,7 @@ interface IEffect {
     * client responsibility to make sure all parameter/buffer is correct if client wants to reopen
     * a closed instance.
     *
     * Effect instance close interface should always succeed unless:
     * Effect instance close method should always succeed unless:
     * 1. The effect instance is not in a proper state to be closed, for example it's still in
     * State::PROCESSING state.
     * 2. There is system/hardware related failure when close.
@@ -154,8 +163,8 @@ interface IEffect {
    /**
     * Get a parameter from the effect instance with parameter ID.
     *
     * This interface must return the current parameter of the effect instance, if no parameter
     * has been set by client yet, the default value must be returned.
     * This method must return the current parameter of the effect instance, if no parameter has
     * been set by client yet, the default value must be returned.
     *
     * Must be available for the effect instance after open().
     *
@@ -165,4 +174,24 @@ interface IEffect {
     * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported parameter tag.
     */
    Parameter getParameter(in Parameter.Id paramId);

    /**
     * Reopen the effect instance, keeping all previous parameters unchanged, and reallocate only
     * the Fast Message Queues (FMQs) allocated during the open time as needed.
     *
     * When the audio framework encounters a valid status FMQ and invalid data FMQ(s), it calls
     * this method to reopen the effect and request the latest data FMQ.
     * Upon receiving this call, if the effect instance's data FMQ(s) is invalid, it must reallocate
     * the necessary data FMQ(s) and return them to the audio framework. All previous parameters and
     * states keep unchanged.
     * Once the audio framework successfully completes the call, it must validate the returned FMQs,
     * deprecate all old FMQs, and exclusively use the FMQs returned from this method.
     *
     * @return The reallocated data FMQ and the original status FMQ.
     *
     * @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state  to reallocate FMQ.
     * This may occur if the effect instance has already been closed or if there is no need to
     * update any data FMQ.
     */
    OpenEffectReturn reopen();
}
+4 −2
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ digraph effect_state_machine {
    IDLE -> INIT[label = "IEffect.close()"];

    INIT -> INIT[label = "IEffect.getState\nIEffect.getDescriptor"];
    IDLE -> IDLE[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor\nIEffect.command(RESET)"];
    PROCESSING -> PROCESSING[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor"];
    IDLE -> IDLE[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor\nIEffect.command(RESET)\nIEffect.reopen"];
    PROCESSING
            -> PROCESSING
                    [label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor\nIEffect.reopen"];
}
+1 −0
Original line number Diff line number Diff line
@@ -226,6 +226,7 @@ cc_defaults {
filegroup {
    name: "effectCommonFile",
    srcs: [
        "EffectContext.cpp",
        "EffectThread.cpp",
        "EffectImpl.cpp",
    ],
+227 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#include <memory>
#define LOG_TAG "AHAL_EffectContext"
#include "effect-impl/EffectContext.h"
#include "include/effect-impl/EffectTypes.h"

using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::media::audio::common::PcmType;
using ::android::hardware::EventFlag;

namespace aidl::android::hardware::audio::effect {

EffectContext::EffectContext(size_t statusDepth, const Parameter::Common& common) {
    LOG_ALWAYS_FATAL_IF(RetCode::SUCCESS != setCommon(common), "illegalCommonParameter");

    // in/outBuffer size in float (FMQ data format defined for DataMQ)
    size_t inBufferSizeInFloat = common.input.frameCount * mInputFrameSize / sizeof(float);
    size_t outBufferSizeInFloat = common.output.frameCount * mOutputFrameSize / sizeof(float);

    // only status FMQ use the EventFlag
    mStatusMQ = std::make_shared<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
    mInputMQ = std::make_shared<DataMQ>(inBufferSizeInFloat);
    mOutputMQ = std::make_shared<DataMQ>(outBufferSizeInFloat);

    if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
        LOG(ERROR) << __func__ << " created invalid FMQ";
    }

    ::android::status_t status =
            EventFlag::createEventFlag(mStatusMQ->getEventFlagWord(), &mEfGroup);
    LOG_ALWAYS_FATAL_IF(status != ::android::OK || !mEfGroup, " create EventFlagGroup failed ");
    mWorkBuffer.reserve(std::max(inBufferSizeInFloat, outBufferSizeInFloat));
}

// reset buffer status by abandon input data in FMQ
void EffectContext::resetBuffer() {
    auto buffer = static_cast<float*>(mWorkBuffer.data());
    std::vector<IEffect::Status> status(mStatusMQ->availableToRead());
    if (mInputMQ) {
        mInputMQ->read(buffer, mInputMQ->availableToRead());
    }
}

void EffectContext::dupeFmqWithReopen(IEffect::OpenEffectReturn* effectRet) {
    if (!mInputMQ) {
        mInputMQ = std::make_shared<DataMQ>(mCommon.input.frameCount * mInputFrameSize /
                                            sizeof(float));
    }
    if (!mOutputMQ) {
        mOutputMQ = std::make_shared<DataMQ>(mCommon.output.frameCount * mOutputFrameSize /
                                             sizeof(float));
    }
    dupeFmq(effectRet);
}

void EffectContext::dupeFmq(IEffect::OpenEffectReturn* effectRet) {
    if (effectRet) {
        effectRet->statusMQ = mStatusMQ->dupeDesc();
        effectRet->inputDataMQ = mInputMQ->dupeDesc();
        effectRet->outputDataMQ = mOutputMQ->dupeDesc();
    }
}

float* EffectContext::getWorkBuffer() {
    return static_cast<float*>(mWorkBuffer.data());
}

std::shared_ptr<EffectContext::StatusMQ> EffectContext::getStatusFmq() const {
    return mStatusMQ;
}

std::shared_ptr<EffectContext::DataMQ> EffectContext::getInputDataFmq() const {
    return mInputMQ;
}

std::shared_ptr<EffectContext::DataMQ> EffectContext::getOutputDataFmq() const {
    return mOutputMQ;
}

size_t EffectContext::getInputFrameSize() const {
    return mInputFrameSize;
}

size_t EffectContext::getOutputFrameSize() const {
    return mOutputFrameSize;
}

int EffectContext::getSessionId() const {
    return mCommon.session;
}

int EffectContext::getIoHandle() const {
    return mCommon.ioHandle;
}

RetCode EffectContext::setOutputDevice(
        const std::vector<aidl::android::media::audio::common::AudioDeviceDescription>& device) {
    mOutputDevice = device;
    return RetCode::SUCCESS;
}

std::vector<aidl::android::media::audio::common::AudioDeviceDescription>
EffectContext::getOutputDevice() {
    return mOutputDevice;
}

RetCode EffectContext::setAudioMode(const aidl::android::media::audio::common::AudioMode& mode) {
    mMode = mode;
    return RetCode::SUCCESS;
}
aidl::android::media::audio::common::AudioMode EffectContext::getAudioMode() {
    return mMode;
}

RetCode EffectContext::setAudioSource(
        const aidl::android::media::audio::common::AudioSource& source) {
    mSource = source;
    return RetCode::SUCCESS;
}

aidl::android::media::audio::common::AudioSource EffectContext::getAudioSource() {
    return mSource;
}

RetCode EffectContext::setVolumeStereo(const Parameter::VolumeStereo& volumeStereo) {
    mVolumeStereo = volumeStereo;
    return RetCode::SUCCESS;
}

Parameter::VolumeStereo EffectContext::getVolumeStereo() {
    return mVolumeStereo;
}

RetCode EffectContext::setCommon(const Parameter::Common& common) {
    LOG(VERBOSE) << __func__ << common.toString();
    auto& input = common.input;
    auto& output = common.output;

    if (input.base.format.pcm != aidl::android::media::audio::common::PcmType::FLOAT_32_BIT ||
        output.base.format.pcm != aidl::android::media::audio::common::PcmType::FLOAT_32_BIT) {
        LOG(ERROR) << __func__ << " illegal IO, input "
                   << ::android::internal::ToString(input.base.format) << ", output "
                   << ::android::internal::ToString(output.base.format);
        return RetCode::ERROR_ILLEGAL_PARAMETER;
    }

    if (auto ret = updateIOFrameSize(common); ret != RetCode::SUCCESS) {
        return ret;
    }

    mInputChannelCount = getChannelCount(input.base.channelMask);
    mOutputChannelCount = getChannelCount(output.base.channelMask);
    if (mInputChannelCount == 0 || mOutputChannelCount == 0) {
        LOG(ERROR) << __func__ << " illegal channel count input " << mInputChannelCount
                   << ", output " << mOutputChannelCount;
        return RetCode::ERROR_ILLEGAL_PARAMETER;
    }

    mCommon = common;
    return RetCode::SUCCESS;
}

Parameter::Common EffectContext::getCommon() {
    LOG(VERBOSE) << __func__ << mCommon.toString();
    return mCommon;
}

EventFlag* EffectContext::getStatusEventFlag() {
    return mEfGroup;
}

RetCode EffectContext::updateIOFrameSize(const Parameter::Common& common) {
    const auto iFrameSize = ::aidl::android::hardware::audio::common::getFrameSizeInBytes(
            common.input.base.format, common.input.base.channelMask);
    const auto oFrameSize = ::aidl::android::hardware::audio::common::getFrameSizeInBytes(
            common.output.base.format, common.output.base.channelMask);

    bool needUpdateMq = false;
    if (mInputMQ &&
        (mInputFrameSize != iFrameSize || mCommon.input.frameCount != common.input.frameCount)) {
        mInputMQ.reset();
        needUpdateMq = true;
    }
    if (mOutputMQ &&
        (mOutputFrameSize != oFrameSize || mCommon.output.frameCount != common.output.frameCount)) {
        mOutputMQ.reset();
        needUpdateMq = true;
    }
    mInputFrameSize = iFrameSize;
    mOutputFrameSize = oFrameSize;
    if (needUpdateMq) {
        return notifyDataMqUpdate();
    }
    return RetCode::SUCCESS;
}

RetCode EffectContext::notifyDataMqUpdate() {
    if (!mEfGroup) {
        LOG(ERROR) << __func__ << ": invalid EventFlag group";
        return RetCode::ERROR_EVENT_FLAG_ERROR;
    }

    if (const auto ret = mEfGroup->wake(kEventFlagDataMqUpdate); ret != ::android::OK) {
        LOG(ERROR) << __func__ << ": wake failure with ret " << ret;
        return RetCode::ERROR_EVENT_FLAG_ERROR;
    }
    LOG(DEBUG) << __func__ << " : signal client for reopen";
    return RetCode::SUCCESS;
}
}  // namespace aidl::android::hardware::audio::effect
Loading