378 lines
16 KiB
C++
378 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2014-15, 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 (mTrackInputBufferProvider != 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 = mTrackInputBufferProvider->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;
|
|
mTrackInputBufferProvider->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->mTrackInputBufferProvider != 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 **inputBufferProvider,
|
|
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 backup of track's buffer provider
|
|
pHwAccbp->mTrackBufferProvider = *bufferProvider;
|
|
pHwAccbp->mTrackInputBufferProvider = *inputBufferProvider;
|
|
// - 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 **trackInputBufferProvider,
|
|
AudioBufferProvider **trackBufferProvider)
|
|
{
|
|
ALOGV("setBufferProvider");
|
|
if (mBufferProvider &&
|
|
(mBufferProvider->mTrackInputBufferProvider != *trackInputBufferProvider)) {
|
|
*trackBufferProvider = mBufferProvider;
|
|
mBufferProvider->mTrackInputBufferProvider = *trackInputBufferProvider;
|
|
}
|
|
}
|
|
|
|
#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
|