blob: 0e4c55a762ed9e14cdda4c76eb0f78fabb6789af [file] [log] [blame]
/*
* 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;
}
}
#ifdef HW_ACC_HPX
void EffectsHwAcc::updateHPXState(uint32_t state)
{
EffectsBufferProvider *pHwAccbp = mBufferProvider;
if (pHwAccbp) {
ALOGV("updateHPXState: %d", state);
int cmdStatus, status;
uint32_t replySize = sizeof(int);
uint32_t data = state;
uint32_t size = (sizeof(effect_param_t) + 2 * sizeof(int32_t));
uint32_t buf32[size];
effect_param_t *param = (effect_param_t *)buf32;
param->psize = sizeof(int32_t);
*(int32_t *)param->data = HW_ACCELERATOR_HPX_STATE;
param->vsize = sizeof(int32_t);
memcpy((param->data + param->psize), &data, param->vsize);
status = (*pHwAccbp->mEffectsHandle)->command(pHwAccbp->mEffectsHandle,
EFFECT_CMD_SET_PARAM,
sizeof(effect_param_t) + param->psize +
param->vsize,
param, &replySize, &cmdStatus);
if ((status != 0) || (cmdStatus != 0))
ALOGE("error %d while updating HW ACC HPX BYPASS state", status);
}
}
#endif
// ----------------------------------------------------------------------------
}; // namespace android