post_proc: Add support for non-tunnel DSP audio effects
Add hw accelerator module to send PCM data to DSP and get
back the effects processed data.
Expose a wrapper library for AudioFlinger to be able use the new
module to apply the DSP audio effects.
Change-Id: I6ee30c11f04a97b35f12201fb61b8cd901921e68
Signed-off-by: Alexy Joseph <alexyj@codeaurora.org>
diff --git a/post_proc/EffectsHwAcc.cpp b/post_proc/EffectsHwAcc.cpp
new file mode 100644
index 0000000..d88a199
--- /dev/null
+++ b/post_proc/EffectsHwAcc.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "EffectsHwAcc"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <media/EffectsFactoryApi.h>
+#include <audio_effects/effect_hwaccelerator.h>
+#include "EffectsHwAcc.h"
+
+namespace android {
+
+#define FRAME_SIZE(format) ((format == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? \
+ 3 /* bytes for 24 bit */ : \
+ (format == AUDIO_FORMAT_PCM_16_BIT) ? \
+ sizeof(uint16_t) : sizeof(uint8_t))
+// ----------------------------------------------------------------------------
+EffectsHwAcc::EffectsBufferProvider::EffectsBufferProvider()
+ : AudioBufferProvider(), mEffectsHandle(NULL),
+ mInputBuffer(NULL), mOutputBuffer(NULL),
+ mInputBufferFrameCountOffset(0)
+{
+}
+
+EffectsHwAcc::EffectsBufferProvider::~EffectsBufferProvider()
+{
+ ALOGV(" deleting HwAccEffBufferProvider");
+
+ if (mEffectsHandle)
+ EffectRelease(mEffectsHandle);
+ if (mInputBuffer)
+ free(mInputBuffer);
+ if (mOutputBuffer)
+ free(mOutputBuffer);
+}
+
+status_t EffectsHwAcc::EffectsBufferProvider::getNextBuffer(
+ AudioBufferProvider::Buffer *pBuffer,
+ int64_t pts)
+{
+ ALOGV("EffectsBufferProvider::getNextBuffer");
+
+ size_t reqInputFrameCount, frameCount, offset;
+ size_t reqOutputFrameCount = pBuffer->frameCount;
+ int ret = 0;
+
+ if (mTrackBufferProvider != NULL) {
+ while (1) {
+ reqInputFrameCount = ((reqOutputFrameCount *
+ mEffectsConfig.inputCfg.samplingRate)/
+ mEffectsConfig.outputCfg.samplingRate) +
+ (((reqOutputFrameCount *
+ mEffectsConfig.inputCfg.samplingRate)%
+ mEffectsConfig.outputCfg.samplingRate) ? 1 : 0);
+ ALOGV("InputFrameCount: %d, OutputFrameCount: %d, InputBufferFrameCountOffset: %d",
+ reqInputFrameCount, reqOutputFrameCount,
+ mInputBufferFrameCountOffset);
+ frameCount = reqInputFrameCount - mInputBufferFrameCountOffset;
+ offset = mInputBufferFrameCountOffset *
+ FRAME_SIZE(mEffectsConfig.inputCfg.format) *
+ popcount(mEffectsConfig.inputCfg.channels);
+ while (frameCount) {
+ pBuffer->frameCount = frameCount;
+ ret = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+ if (ret == OK) {
+ int bytesInBuffer = pBuffer->frameCount *
+ FRAME_SIZE(mEffectsConfig.inputCfg.format) *
+ popcount(mEffectsConfig.inputCfg.channels);
+ memcpy((char *)mInputBuffer+offset, pBuffer->i8, bytesInBuffer);
+ frameCount -= pBuffer->frameCount;
+ mInputBufferFrameCountOffset += pBuffer->frameCount;
+ offset += bytesInBuffer;
+ mTrackBufferProvider->releaseBuffer(pBuffer);
+ } else
+ break;
+ }
+ if (ret == OK) {
+ mEffectsConfig.inputCfg.buffer.frameCount = reqInputFrameCount;
+ mEffectsConfig.inputCfg.buffer.raw = (void *)mInputBuffer;
+ mEffectsConfig.outputCfg.buffer.frameCount = reqOutputFrameCount;
+ mEffectsConfig.outputCfg.buffer.raw = (void *)mOutputBuffer;
+
+ ret = (*mEffectsHandle)->process(mEffectsHandle,
+ &mEffectsConfig.inputCfg.buffer,
+ &mEffectsConfig.outputCfg.buffer);
+ if (ret == -ENODATA) {
+ ALOGV("Continue to provide more data for initial buffering");
+ mInputBufferFrameCountOffset -= reqInputFrameCount;
+ continue;
+ }
+ if (ret > 0)
+ mInputBufferFrameCountOffset -= reqInputFrameCount;
+ pBuffer->raw = (void *)mOutputBuffer;
+ pBuffer->frameCount = reqOutputFrameCount;
+ }
+ return ret;
+ }
+ } else {
+ ALOGE("EffBufferProvider::getNextBuffer() error: NULL track buffer provider");
+ return NO_INIT;
+ }
+}
+
+void EffectsHwAcc::EffectsBufferProvider::releaseBuffer(
+ AudioBufferProvider::Buffer *pBuffer)
+{
+ ALOGV("EffBufferProvider::releaseBuffer()");
+ if (this->mTrackBufferProvider != NULL) {
+ pBuffer->frameCount = 0;
+ pBuffer->raw = NULL;
+ } else {
+ ALOGE("HwAccEffectsBufferProvider::releaseBuffer() error: NULL track buffer provider");
+ }
+}
+
+EffectsHwAcc::EffectsHwAcc(uint32_t sampleRate)
+ : mEnabled(false), mFd(-1), mBufferProvider(NULL),
+ mInputSampleRate(sampleRate), mOutputSampleRate(sampleRate)
+{
+}
+
+EffectsHwAcc::~EffectsHwAcc()
+{
+ ALOGV("deleting EffectsHwAcc");
+
+ if (mBufferProvider)
+ delete mBufferProvider;
+}
+
+void EffectsHwAcc::setSampleRate(uint32_t inpSR, uint32_t outSR)
+{
+ mInputSampleRate = inpSR;
+ mOutputSampleRate = outSR;
+}
+
+void EffectsHwAcc::unprepareEffects(AudioBufferProvider **bufferProvider)
+{
+ ALOGV("EffectsHwAcc::unprepareEffects");
+
+ EffectsBufferProvider *pHwAccbp = mBufferProvider;
+ if (mBufferProvider != NULL) {
+ ALOGV(" deleting h/w accelerator EffectsBufferProvider");
+ int cmdStatus, status;
+ uint32_t replySize = sizeof(int);
+
+ replySize = sizeof(int);
+ status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
+ EFFECT_CMD_DISABLE,
+ 0 /*cmdSize*/, NULL /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if ((status != 0) || (cmdStatus != 0))
+ ALOGE("error %d while enabling hw acc effects", status);
+
+ *bufferProvider = pHwAccbp->mTrackBufferProvider;
+ delete mBufferProvider;
+
+ mBufferProvider = NULL;
+ } else {
+ ALOGV(" nothing to do, no h/w accelerator effects to delete");
+ }
+ mEnabled = false;
+}
+
+status_t EffectsHwAcc::prepareEffects(AudioBufferProvider **bufferProvider,
+ int sessionId,
+ audio_channel_mask_t channelMask,
+ int frameCount)
+{
+ ALOGV("EffectsHwAcc::prepareAccEffects");
+
+ // discard the previous hw acc effects if there was one
+ unprepareEffects(bufferProvider);
+
+ EffectsBufferProvider* pHwAccbp = new EffectsBufferProvider();
+ int32_t status;
+ int cmdStatus;
+ uint32_t replySize;
+ uint32_t size = (sizeof(effect_param_t) + 2 * sizeof(int32_t) - 1) /
+ (sizeof(uint32_t) + 1);
+ uint32_t buf32[size];
+ effect_param_t *param = (effect_param_t *)buf32;
+
+ uint32_t i, numEffects = 0;
+ effect_descriptor_t hwAccFxDesc;
+ int ret = EffectQueryNumberEffects(&numEffects);
+ if (ret != 0) {
+ ALOGE("AudioMixer() error %d querying number of effects", ret);
+ goto noEffectsForActiveTrack;
+ }
+ ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+ for (i = 0 ; i < numEffects ; i++) {
+ if (EffectQueryEffect(i, &hwAccFxDesc) == 0) {
+ if (memcmp(&hwAccFxDesc.type, EFFECT_UIID_HWACCELERATOR,
+ sizeof(effect_uuid_t)) == 0) {
+ ALOGI("found effect \"%s\" from %s",
+ hwAccFxDesc.name, hwAccFxDesc.implementor);
+ break;
+ }
+ }
+ }
+ if (i == numEffects) {
+ ALOGW("H/W accelerated effects library not found");
+ goto noEffectsForActiveTrack;
+ }
+ if (EffectCreate(&hwAccFxDesc.uuid, sessionId, -1 /*ioId not relevant here*/,
+ &pHwAccbp->mEffectsHandle) != 0) {
+ ALOGE("prepareEffects fails: error creating effect");
+ goto noEffectsForActiveTrack;
+ }
+
+ // channel input configuration will be overridden per-track
+ pHwAccbp->mEffectsConfig.inputCfg.channels = channelMask;
+ pHwAccbp->mEffectsConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ pHwAccbp->mEffectsConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pHwAccbp->mEffectsConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pHwAccbp->mEffectsConfig.inputCfg.samplingRate = mInputSampleRate;
+ pHwAccbp->mEffectsConfig.outputCfg.samplingRate = mOutputSampleRate;
+ pHwAccbp->mEffectsConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ pHwAccbp->mEffectsConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+ pHwAccbp->mEffectsConfig.outputCfg.buffer.frameCount = frameCount;
+ pHwAccbp->mEffectsConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
+ EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
+ pHwAccbp->mEffectsConfig.outputCfg.mask = pHwAccbp->mEffectsConfig.inputCfg.mask;
+
+ // Configure hw acc effects
+ replySize = sizeof(int);
+ status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
+ EFFECT_CMD_SET_CONFIG,
+ sizeof(effect_config_t) /*cmdSize*/,
+ &pHwAccbp->mEffectsConfig /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if ((status != 0) || (cmdStatus != 0)) {
+ ALOGE("error %d while configuring h/w acc effects", status);
+ goto noEffectsForActiveTrack;
+ }
+ replySize = sizeof(int);
+ status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
+ EFFECT_CMD_HW_ACC,
+ sizeof(frameCount) /*cmdSize*/,
+ &frameCount /*pCmdData*/,
+ &replySize,
+ &cmdStatus /*pReplyData*/);
+ if ((status != 0) || (cmdStatus != 0)) {
+ ALOGE("error %d while enabling h/w acc effects", status);
+ goto noEffectsForActiveTrack;
+ }
+ replySize = sizeof(int);
+ status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
+ EFFECT_CMD_ENABLE,
+ 0 /*cmdSize*/, NULL /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if ((status != 0) || (cmdStatus != 0)) {
+ ALOGE("error %d while enabling h/w acc effects", status);
+ goto noEffectsForActiveTrack;
+ }
+
+ param->psize = sizeof(int32_t);
+ *(int32_t *)param->data = HW_ACCELERATOR_FD;
+ param->vsize = sizeof(int32_t);
+ replySize = sizeof(effect_param_t) +
+ ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
+ param->vsize;
+ status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
+ EFFECT_CMD_GET_PARAM,
+ sizeof(effect_param_t) + param->psize,
+ param, &replySize, param);
+ if ((param->status != 0) || (*(int32_t *)(param->data + sizeof(int32_t)) <= 0)) {
+ ALOGE("error %d while enabling h/w acc effects", status);
+ goto noEffectsForActiveTrack;
+ }
+ mFd = *(int32_t *)(param->data + sizeof(int32_t));
+
+ pHwAccbp->mInputBuffer = calloc(6*frameCount,
+ /* 6 times buffering to account for an input of
+ 192kHz to an output of 32kHz - may be a least
+ sampling rate of rendering device */
+ FRAME_SIZE(pHwAccbp->mEffectsConfig.inputCfg.format) *
+ popcount(channelMask));
+ if (!pHwAccbp->mInputBuffer)
+ goto noEffectsForActiveTrack;
+
+ pHwAccbp->mOutputBuffer = calloc(frameCount,
+ FRAME_SIZE(pHwAccbp->mEffectsConfig.outputCfg.format) *
+ popcount(AUDIO_CHANNEL_OUT_STEREO));
+ if (!pHwAccbp->mOutputBuffer) {
+ free(pHwAccbp->mInputBuffer);
+ goto noEffectsForActiveTrack;
+ }
+ // initialization successful:
+ // - keep track of the real buffer provider in case it was set before
+ pHwAccbp->mTrackBufferProvider = *bufferProvider;
+ // - we'll use the hw acc effect integrated inside this
+ // track's buffer provider, and we'll use it as the track's buffer provider
+ mBufferProvider = pHwAccbp;
+ *bufferProvider = pHwAccbp;
+
+ mEnabled = true;
+ return NO_ERROR;
+
+noEffectsForActiveTrack:
+ delete pHwAccbp;
+ mBufferProvider = NULL;
+ return NO_INIT;
+}
+
+void EffectsHwAcc::setBufferProvider(AudioBufferProvider **bufferProvider,
+ AudioBufferProvider **trackBufferProvider)
+{
+ ALOGV("setBufferProvider");
+ if (mBufferProvider &&
+ (mBufferProvider->mTrackBufferProvider != *bufferProvider)) {
+ *trackBufferProvider = mBufferProvider;
+ mBufferProvider->mTrackBufferProvider = *bufferProvider;
+ }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android