Eric Laurent 7d850f23c8 Modifications in audio effect engine state management.
- Separate the updating of effect engine state from the process call in EffectModule so that the state
of all effects in the same effect chain is updated simultaneusly before all process functions are called.
- Added a mechanism for the effect engine to continue being called for processing after receiving the disable
commands untils it considers that the framework can stop calling the process function without causing
a glitch or loosing some effect tail.
- Updated test reverb and equalizer to support this new feature

Change-Id: Icb56ae2c84c076d4dbad6cf733b1a62f823febe7
2010-07-13 12:27:18 -07:00

402 lines
13 KiB
C++

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Visualizer"
//#define LOG_NDEBUG 0
#include <cutils/log.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <media/EffectVisualizerApi.h>
namespace android {
// effect_interface_t interface implementation for visualizer effect
extern "C" const struct effect_interface_s gVisualizerInterface;
// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b
const effect_descriptor_t gVisualizerDescriptor = {
{0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
{0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
EFFECT_API_VERSION,
(EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
0, // TODO
1,
"Visualizer",
"Google Inc.",
};
enum visualizer_state_e {
VISUALIZER_STATE_UNINITIALIZED,
VISUALIZER_STATE_INITIALIZED,
VISUALIZER_STATE_ACTIVE,
};
struct VisualizerContext {
const struct effect_interface_s *mItfe;
effect_config_t mConfig;
uint32_t mState;
uint32_t mCaptureIdx;
uint32_t mCaptureSize;
uint32_t mCurrentBuf;
uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
};
//
//--- Local functions
//
void Visualizer_reset(VisualizerContext *pContext)
{
pContext->mCaptureIdx = 0;
pContext->mCurrentBuf = 0;
memset(pContext->mCaptureBuf[0], 0, VISUALIZER_CAPTURE_SIZE_MAX);
memset(pContext->mCaptureBuf[1], 0, VISUALIZER_CAPTURE_SIZE_MAX);
}
//----------------------------------------------------------------------------
// Visualizer_configure()
//----------------------------------------------------------------------------
// Purpose: Set input and output audio configuration.
//
// Inputs:
// pContext: effect engine context
// pConfig: pointer to effect_config_t structure holding input and output
// configuration parameters
//
// Outputs:
//
//----------------------------------------------------------------------------
int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig)
{
LOGV("Visualizer_configure start");
if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
if (pConfig->inputCfg.channels != CHANNEL_STEREO) return -EINVAL;
if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
if (pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15) return -EINVAL;
memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t));
Visualizer_reset(pContext);
return 0;
}
//----------------------------------------------------------------------------
// Visualizer_init()
//----------------------------------------------------------------------------
// Purpose: Initialize engine with default configuration.
//
// Inputs:
// pContext: effect engine context
//
// Outputs:
//
//----------------------------------------------------------------------------
int Visualizer_init(VisualizerContext *pContext)
{
pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
pContext->mConfig.inputCfg.channels = CHANNEL_STEREO;
pContext->mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
pContext->mConfig.inputCfg.samplingRate = 44100;
pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
pContext->mConfig.outputCfg.channels = CHANNEL_STEREO;
pContext->mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
pContext->mConfig.outputCfg.samplingRate = 44100;
pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
Visualizer_configure(pContext, &pContext->mConfig);
return 0;
}
//
//--- Effect Library Interface Implementation
//
extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
*pNumEffects = 1;
return 0;
}
extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
if (pDescriptor == NULL) {
return -EINVAL;
}
if (index > 0) {
return -EINVAL;
}
memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
return 0;
}
extern "C" int EffectCreate(effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_interface_t *pInterface) {
int ret;
int i;
if (pInterface == NULL || uuid == NULL) {
return -EINVAL;
}
if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
return -EINVAL;
}
VisualizerContext *pContext = new VisualizerContext;
pContext->mItfe = &gVisualizerInterface;
pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
ret = Visualizer_init(pContext);
if (ret < 0) {
LOGW("EffectCreate() init failed");
delete pContext;
return ret;
}
*pInterface = (effect_interface_t)pContext;
pContext->mState = VISUALIZER_STATE_INITIALIZED;
LOGV("EffectCreate %p", pContext);
return 0;
}
extern "C" int EffectRelease(effect_interface_t interface) {
VisualizerContext * pContext = (VisualizerContext *)interface;
LOGV("EffectRelease %p", interface);
if (pContext == NULL) {
return -EINVAL;
}
pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
delete pContext;
return 0;
}
//
//--- Effect Control Interface Implementation
//
static inline int16_t clamp16(int32_t sample)
{
if ((sample>>15) ^ (sample>>31))
sample = 0x7FFF ^ (sample>>31);
return sample;
}
extern "C" int Visualizer_process(
effect_interface_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
{
android::VisualizerContext * pContext = (android::VisualizerContext *)self;
if (pContext == NULL) {
return -EINVAL;
}
if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
return -ENODATA;
}
if (inBuffer == NULL || inBuffer->raw == NULL ||
outBuffer == NULL || outBuffer->raw == NULL ||
inBuffer->frameCount != outBuffer->frameCount ||
inBuffer->frameCount == 0) {
return -EINVAL;
}
// all code below assumes stereo 16 bit PCM output and input
uint32_t captIdx;
uint32_t inIdx;
uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
for (inIdx = 0, captIdx = pContext->mCaptureIdx;
inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
inIdx++, captIdx++) {
int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
smp = (smp + (1 << 8)) >> 9;
buf[captIdx] = ((uint8_t)smp)^0x80;
}
pContext->mCaptureIdx = captIdx;
// go to next buffer when buffer full
if (pContext->mCaptureIdx == pContext->mCaptureSize) {
pContext->mCurrentBuf ^= 1;
pContext->mCaptureIdx = 0;
}
if (inBuffer->raw != outBuffer->raw) {
if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
}
} else {
memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
}
}
return 0;
} // end Visualizer_process
extern "C" int Visualizer_command(effect_interface_t self, int cmdCode, int cmdSize,
void *pCmdData, int *replySize, void *pReplyData) {
android::VisualizerContext * pContext = (android::VisualizerContext *)self;
int retsize;
if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
return -EINVAL;
}
// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
switch (cmdCode) {
case EFFECT_CMD_INIT:
if (pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
*(int *) pReplyData = Visualizer_init(pContext);
break;
case EFFECT_CMD_CONFIGURE:
if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
|| pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
*(int *) pReplyData = Visualizer_configure(pContext,
(effect_config_t *) pCmdData);
break;
case EFFECT_CMD_RESET:
Visualizer_reset(pContext);
break;
case EFFECT_CMD_ENABLE:
if (pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
return -ENOSYS;
}
pContext->mState = VISUALIZER_STATE_ACTIVE;
LOGV("EFFECT_CMD_ENABLE() OK");
*(int *)pReplyData = 0;
break;
case EFFECT_CMD_DISABLE:
if (pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
return -ENOSYS;
}
pContext->mState = VISUALIZER_STATE_INITIALIZED;
LOGV("EFFECT_CMD_DISABLE() OK");
*(int *)pReplyData = 0;
break;
case EFFECT_CMD_GET_PARAM: {
if (pCmdData == NULL ||
cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
pReplyData == NULL ||
*replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
return -EINVAL;
}
memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
effect_param_t *p = (effect_param_t *)pReplyData;
p->status = 0;
*replySize = sizeof(effect_param_t) + sizeof(uint32_t);
if (p->psize != sizeof(uint32_t) ||
*(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
p->status = -EINVAL;
break;
}
LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
*((uint32_t *)p->data + 1) = pContext->mCaptureSize;
p->vsize = sizeof(uint32_t);
*replySize += sizeof(uint32_t);
} break;
case EFFECT_CMD_SET_PARAM: {
if (pCmdData == NULL ||
cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
pReplyData == NULL || *replySize != sizeof(int32_t)) {
return -EINVAL;
}
*(int32_t *)pReplyData = 0;
effect_param_t *p = (effect_param_t *)pCmdData;
if (p->psize != sizeof(uint32_t) ||
p->vsize != sizeof(uint32_t) ||
*(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
*(int32_t *)pReplyData = -EINVAL;
break;;
}
pContext->mCaptureSize = *((uint32_t *)p->data + 1);
LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
} break;
case EFFECT_CMD_SET_DEVICE:
case EFFECT_CMD_SET_VOLUME:
case EFFECT_CMD_SET_AUDIO_MODE:
break;
case VISU_CMD_CAPTURE:
if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) {
LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
*replySize, pContext->mCaptureSize);
return -EINVAL;
}
if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
memcpy(pReplyData,
pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
pContext->mCaptureSize);
} else {
memset(pReplyData, 0x80, pContext->mCaptureSize);
}
break;
default:
LOGW("Visualizer_command invalid command %d",cmdCode);
return -EINVAL;
}
return 0;
}
// effect_interface_t interface implementation for visualizer effect
const struct effect_interface_s gVisualizerInterface = {
Visualizer_process,
Visualizer_command
};
} // namespace