/* * 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 #include #include #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