AIDL effect: add draining state support
- effects can transit to DRAINING state with STOP command and continue
the fade out processing
- add draining in the audio eraser effect as an example
- update VTS to support draining
Flag: EXEMPT bugfix
Bug: 379776482
Test: --test-mapping hardware/interfaces/audio/aidl/vts:presubmit
Change-Id: I2ca25cd085d1b6ae6cf8b0d1b58cd713aef0f7e5
Merged-In: I2ca25cd085d1b6ae6cf8b0d1b58cd713aef0f7e5
diff --git a/audio/aidl/default/eraser/Eraser.cpp b/audio/aidl/default/eraser/Eraser.cpp
index 157ec79..59cc9a2 100644
--- a/audio/aidl/default/eraser/Eraser.cpp
+++ b/audio/aidl/default/eraser/Eraser.cpp
@@ -133,10 +133,70 @@
LOG(DEBUG) << __func__;
}
+ndk::ScopedAStatus EraserSw::command(CommandId command) {
+ std::lock_guard lg(mImplMutex);
+ RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "instanceNotOpen");
+
+ switch (command) {
+ case CommandId::START:
+ RETURN_OK_IF(mState == State::PROCESSING);
+ mState = State::PROCESSING;
+ mContext->enable();
+ startThread();
+ RETURN_IF(notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS, EX_ILLEGAL_STATE,
+ "notifyEventFlagNotEmptyFailed");
+ break;
+ case CommandId::STOP:
+ RETURN_OK_IF(mState == State::IDLE || mState == State::DRAINING);
+ if (mVersion < kDrainSupportedVersion) {
+ mState = State::IDLE;
+ stopThread();
+ mContext->disable();
+ } else {
+ mState = State::DRAINING;
+ startDraining();
+ mContext->startDraining();
+ }
+ RETURN_IF(notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS, EX_ILLEGAL_STATE,
+ "notifyEventFlagNotEmptyFailed");
+ break;
+ case CommandId::RESET:
+ mState = State::IDLE;
+ RETURN_IF(notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS, EX_ILLEGAL_STATE,
+ "notifyEventFlagNotEmptyFailed");
+ stopThread();
+ mImplContext->disable();
+ mImplContext->reset();
+ mImplContext->resetBuffer();
+ break;
+ default:
+ LOG(ERROR) << getEffectNameWithVersion() << __func__ << " instance still processing";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "CommandIdNotSupported");
+ }
+ LOG(VERBOSE) << getEffectNameWithVersion() << __func__
+ << " transfer to state: " << toString(mState);
+ return ndk::ScopedAStatus::ok();
+}
+
// Processing method running in EffectWorker thread.
IEffect::Status EraserSw::effectProcessImpl(float* in, float* out, int samples) {
RETURN_VALUE_IF(!mContext, (IEffect::Status{EX_NULL_POINTER, 0, 0}), "nullContext");
- return mContext->process(in, out, samples);
+ IEffect::Status procStatus{STATUS_NOT_ENOUGH_DATA, 0, 0};
+ procStatus = mContext->process(in, out, samples);
+ if (mState == State::DRAINING && procStatus.status == STATUS_NOT_ENOUGH_DATA) {
+ drainingComplete_l();
+ }
+
+ return procStatus;
+}
+
+void EraserSw::drainingComplete_l() {
+ if (mState != State::DRAINING) return;
+
+ LOG(DEBUG) << getEffectNameWithVersion() << __func__;
+ finishDraining();
+ mState = State::IDLE;
}
EraserSwContext::EraserSwContext(int statusDepth, const Parameter::Common& common)
@@ -164,24 +224,47 @@
IEffect::Status EraserSwContext::process(float* in, float* out, int samples) {
LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << samples;
- IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0};
-
+ IEffect::Status procStatus = {EX_ILLEGAL_ARGUMENT, 0, 0};
const auto inputChannelCount = getChannelCount(mCommon.input.base.channelMask);
const auto outputChannelCount = getChannelCount(mCommon.output.base.channelMask);
if (inputChannelCount < outputChannelCount) {
LOG(ERROR) << __func__ << " invalid channel count, in: " << inputChannelCount
<< " out: " << outputChannelCount;
- return status;
+ return procStatus;
}
- int iFrames = samples / inputChannelCount;
+ if (samples <= 0 || 0 != samples % inputChannelCount) {
+ LOG(ERROR) << __func__ << " invalid samples: " << samples;
+ return procStatus;
+ }
+
+ const int iFrames = samples / inputChannelCount;
+ const float gainPerSample = 1.f / iFrames;
for (int i = 0; i < iFrames; i++) {
- std::memcpy(out, in, outputChannelCount);
+ if (isDraining()) {
+ const float gain = (iFrames - i - 1) * gainPerSample;
+ for (size_t c = 0; c < outputChannelCount; c++) {
+ out[c] = in[c] * gain;
+ }
+ } else {
+ std::memcpy(out, in, outputChannelCount * sizeof(float));
+ }
+
in += inputChannelCount;
out += outputChannelCount;
}
- return {STATUS_OK, static_cast<int32_t>(iFrames * inputChannelCount),
- static_cast<int32_t>(iFrames * outputChannelCount)};
+
+ // drain for one cycle
+ if (isDraining()) {
+ procStatus.status = STATUS_NOT_ENOUGH_DATA;
+ finishDraining();
+ } else {
+ procStatus.status = STATUS_OK;
+ }
+ procStatus.fmqConsumed = static_cast<int32_t>(iFrames * inputChannelCount);
+ procStatus.fmqProduced = static_cast<int32_t>(iFrames * outputChannelCount);
+
+ return procStatus;
}
} // namespace aidl::android::hardware::audio::effect