Eric Laurent 464d5b3da2 Audio framework: support for audio pre processing
Audio effect framework is extended to suport effects on
output and input audio path.

AudioFlinger: Support for audio effects and effect chains is
moved from PlaybackThread class to ThreadBase class so that
RecordThread can manage effects.
Effects of type pre processing are allowed on record thread
only. When a pre processing is enabled, the effect interface handle is
passed down to the input stream so that the audio HAL can call the
process function. The record thread loop calls the effect chain process
function that will only manage the effect state and commands and skip the
process function.

AudioRecord: The audio session is allocated before calling getInput() into
audio policy serice so that the session is known before the input theead is
created and pre processings can be created on the correct session.

AudioPolicyService: default pre processing for a given input source are
loaded from audio_effects.conf file.
When an input is created, corresponding effects are created and enabled.

Change-Id: Id17119e0979b4dcf189b5c7957fec30dc3478790
2011-07-18 09:42:57 -07:00

731 lines
19 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 "EffectsFactory"
//#define LOG_NDEBUG 0
#include "EffectsFactory.h"
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <cutils/misc.h>
#include <cutils/config_utils.h>
#include <audio_effects/audio_effects_conf.h>
static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
static uint32_t gNumEffects; // total number number of effects
static list_elem_t *gCurLib; // current library in enumeration process
static list_elem_t *gCurEffect; // current effect in enumeration process
static uint32_t gCurEffectIdx; // current effect index in enumeration process
static lib_entry_t *gCachedLibrary; // last library accessed by getLibrary()
static int gInitDone; // true is global initialization has been preformed
static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
// was not modified since last call to EffectQueryNumberEffects()
/////////////////////////////////////////////////
// Local functions prototypes
/////////////////////////////////////////////////
static int init();
static int loadEffectConfigFile(const char *path);
static int loadLibraries(cnode *root);
static int loadLibrary(cnode *root, const char *name);
static int loadEffects(cnode *root);
static int loadEffect(cnode *node);
static lib_entry_t *getLibrary(const char *path);
static void resetEffectEnumeration();
static uint32_t updateNumEffects();
static int findEffect(effect_uuid_t *type,
effect_uuid_t *uuid,
lib_entry_t **lib,
effect_descriptor_t **desc);
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
static int stringToUuid(const char *str, effect_uuid_t *uuid);
static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
/////////////////////////////////////////////////
// Effect Control Interface functions
/////////////////////////////////////////////////
int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
{
int ret = init();
if (ret < 0) {
return ret;
}
effect_entry_t *fx = (effect_entry_t *)self;
pthread_mutex_lock(&gLibLock);
if (fx->lib == NULL) {
pthread_mutex_unlock(&gLibLock);
return -EPIPE;
}
pthread_mutex_lock(&fx->lib->lock);
pthread_mutex_unlock(&gLibLock);
ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
pthread_mutex_unlock(&fx->lib->lock);
return ret;
}
int Effect_Command(effect_handle_t self,
uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t *replySize,
void *pReplyData)
{
int ret = init();
if (ret < 0) {
return ret;
}
effect_entry_t *fx = (effect_entry_t *)self;
pthread_mutex_lock(&gLibLock);
if (fx->lib == NULL) {
pthread_mutex_unlock(&gLibLock);
return -EPIPE;
}
pthread_mutex_lock(&fx->lib->lock);
pthread_mutex_unlock(&gLibLock);
ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
pthread_mutex_unlock(&fx->lib->lock);
return ret;
}
int Effect_GetDescriptor(effect_handle_t self,
effect_descriptor_t *desc)
{
int ret = init();
if (ret < 0) {
return ret;
}
effect_entry_t *fx = (effect_entry_t *)self;
pthread_mutex_lock(&gLibLock);
if (fx->lib == NULL) {
pthread_mutex_unlock(&gLibLock);
return -EPIPE;
}
pthread_mutex_lock(&fx->lib->lock);
pthread_mutex_unlock(&gLibLock);
ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
pthread_mutex_unlock(&fx->lib->lock);
return ret;
}
int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
{
int ret = init();
if (ret < 0) {
return ret;
}
effect_entry_t *fx = (effect_entry_t *)self;
pthread_mutex_lock(&gLibLock);
if (fx->lib == NULL) {
pthread_mutex_unlock(&gLibLock);
return -EPIPE;
}
pthread_mutex_lock(&fx->lib->lock);
pthread_mutex_unlock(&gLibLock);
if ((*fx->subItfe)->process_reverse != NULL) {
ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
} else {
ret = -ENOSYS;
}
pthread_mutex_unlock(&fx->lib->lock);
return ret;
}
const struct effect_interface_s gInterface = {
Effect_Process,
Effect_Command,
Effect_GetDescriptor,
NULL
};
const struct effect_interface_s gInterfaceWithReverse = {
Effect_Process,
Effect_Command,
Effect_GetDescriptor,
Effect_ProcessReverse
};
/////////////////////////////////////////////////
// Effect Factory Interface functions
/////////////////////////////////////////////////
int EffectQueryNumberEffects(uint32_t *pNumEffects)
{
int ret = init();
if (ret < 0) {
return ret;
}
if (pNumEffects == NULL) {
return -EINVAL;
}
pthread_mutex_lock(&gLibLock);
*pNumEffects = gNumEffects;
gCanQueryEffect = 1;
pthread_mutex_unlock(&gLibLock);
LOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
return ret;
}
int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
{
int ret = init();
if (ret < 0) {
return ret;
}
if (pDescriptor == NULL ||
index >= gNumEffects) {
return -EINVAL;
}
if (gCanQueryEffect == 0) {
return -ENOSYS;
}
pthread_mutex_lock(&gLibLock);
ret = -ENOENT;
if (index < gCurEffectIdx) {
resetEffectEnumeration();
}
while (gCurLib) {
if (gCurEffect) {
if (index == gCurEffectIdx) {
memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
ret = 0;
break;
} else {
gCurEffect = gCurEffect->next;
gCurEffectIdx++;
}
} else {
gCurLib = gCurLib->next;
gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
}
}
#if (LOG_NDEBUG == 0)
char str[256];
dumpEffectDescriptor(pDescriptor, str, 256);
LOGV("EffectQueryEffect() desc:%s", str);
#endif
pthread_mutex_unlock(&gLibLock);
return ret;
}
int EffectGetDescriptor(effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
{
lib_entry_t *l = NULL;
effect_descriptor_t *d = NULL;
int ret = init();
if (ret < 0) {
return ret;
}
if (pDescriptor == NULL || uuid == NULL) {
return -EINVAL;
}
pthread_mutex_lock(&gLibLock);
ret = findEffect(NULL, uuid, &l, &d);
if (ret == 0) {
memcpy(pDescriptor, d, sizeof(effect_descriptor_t));
}
pthread_mutex_unlock(&gLibLock);
return ret;
}
int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
{
list_elem_t *e = gLibraryList;
lib_entry_t *l = NULL;
effect_descriptor_t *d = NULL;
effect_handle_t itfe;
effect_entry_t *fx;
int found = 0;
int ret;
if (uuid == NULL || pHandle == NULL) {
return -EINVAL;
}
LOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
uuid->node[3],uuid->node[4],uuid->node[5]);
ret = init();
if (ret < 0) {
LOGW("EffectCreate() init error: %d", ret);
return ret;
}
pthread_mutex_lock(&gLibLock);
ret = findEffect(NULL, uuid, &l, &d);
if (ret < 0){
goto exit;
}
// create effect in library
ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
if (ret != 0) {
LOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
goto exit;
}
// add entry to effect list
fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
fx->subItfe = itfe;
if ((*itfe)->process_reverse != NULL) {
fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
LOGV("EffectCreate() gInterfaceWithReverse");
} else {
fx->itfe = (struct effect_interface_s *)&gInterface;
LOGV("EffectCreate() gInterface");
}
fx->lib = l;
e = (list_elem_t *)malloc(sizeof(list_elem_t));
e->object = fx;
e->next = gEffectList;
gEffectList = e;
*pHandle = (effect_handle_t)fx;
LOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
exit:
pthread_mutex_unlock(&gLibLock);
return ret;
}
int EffectRelease(effect_handle_t handle)
{
effect_entry_t *fx;
list_elem_t *e1;
list_elem_t *e2;
int ret = init();
if (ret < 0) {
return ret;
}
// remove effect from effect list
pthread_mutex_lock(&gLibLock);
e1 = gEffectList;
e2 = NULL;
while (e1) {
if (e1->object == handle) {
if (e2) {
e2->next = e1->next;
} else {
gEffectList = e1->next;
}
fx = (effect_entry_t *)e1->object;
free(e1);
break;
}
e2 = e1;
e1 = e1->next;
}
if (e1 == NULL) {
ret = -ENOENT;
goto exit;
}
// release effect in library
if (fx->lib == NULL) {
LOGW("EffectRelease() fx %p library already unloaded", handle);
} else {
pthread_mutex_lock(&fx->lib->lock);
fx->lib->desc->release_effect(fx->subItfe);
pthread_mutex_unlock(&fx->lib->lock);
}
free(fx);
exit:
pthread_mutex_unlock(&gLibLock);
return ret;
}
int EffectIsNullUuid(effect_uuid_t *uuid)
{
if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
return 0;
}
return 1;
}
/////////////////////////////////////////////////
// Local functions
/////////////////////////////////////////////////
int init() {
int hdl;
if (gInitDone) {
return 0;
}
pthread_mutex_init(&gLibLock, NULL);
if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
} else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
}
updateNumEffects();
gInitDone = 1;
LOGV("init() done");
return 0;
}
int loadEffectConfigFile(const char *path)
{
cnode *root;
char *data;
data = load_file(path, NULL);
if (data == NULL) {
return -ENODEV;
}
root = config_node("", "");
config_load(root, data);
loadLibraries(root);
loadEffects(root);
config_free(root);
free(root);
free(data);
return 0;
}
int loadLibraries(cnode *root)
{
cnode *node;
node = config_find(root, LIBRARIES_TAG);
if (node == NULL) {
return -ENOENT;
}
node = node->first_child;
while (node) {
loadLibrary(node, node->name);
node = node->next;
}
return 0;
}
int loadLibrary(cnode *root, const char *name)
{
cnode *node;
void *hdl;
audio_effect_library_t *desc;
list_elem_t *e;
lib_entry_t *l;
node = config_find(root, PATH_TAG);
if (node == NULL) {
return -EINVAL;
}
hdl = dlopen(node->value, RTLD_NOW);
if (hdl == NULL) {
LOGW("loadLibrary() failed to open %s", node->value);
goto error;
}
desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
if (desc == NULL) {
LOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
goto error;
}
if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
LOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
goto error;
}
if (EFFECT_API_VERSION_MAJOR(desc->version) !=
EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
LOGW("loadLibrary() bad lib version %08x", desc->version);
goto error;
}
// add entry for library in gLibraryList
l = malloc(sizeof(lib_entry_t));
l->name = strndup(name, PATH_MAX);
l->path = strndup(node->value, PATH_MAX);
l->handle = hdl;
l->desc = desc;
l->effects = NULL;
pthread_mutex_init(&l->lock, NULL);
e = malloc(sizeof(list_elem_t));
e->object = l;
pthread_mutex_lock(&gLibLock);
e->next = gLibraryList;
gLibraryList = e;
pthread_mutex_unlock(&gLibLock);
LOGV("getLibrary() linked library %p for path %s", l, node->value);
return 0;
error:
if (hdl != NULL) {
dlclose(hdl);
}
return -EINVAL;
}
int loadEffects(cnode *root)
{
cnode *node;
node = config_find(root, EFFECTS_TAG);
if (node == NULL) {
return -ENOENT;
}
node = node->first_child;
while (node) {
loadEffect(node);
node = node->next;
}
return 0;
}
int loadEffect(cnode *root)
{
cnode *node;
effect_uuid_t uuid;
lib_entry_t *l;
effect_descriptor_t *d;
list_elem_t *e;
node = config_find(root, LIBRARY_TAG);
if (node == NULL) {
return -EINVAL;
}
l = getLibrary(node->value);
if (l == NULL) {
LOGW("loadEffect() could not get library %s", node->value);
return -EINVAL;
}
node = config_find(root, UUID_TAG);
if (node == NULL) {
return -EINVAL;
}
if (stringToUuid(node->value, &uuid) != 0) {
LOGW("loadEffect() invalid uuid %s", node->value);
return -EINVAL;
}
d = malloc(sizeof(effect_descriptor_t));
if (l->desc->get_descriptor(&uuid, d) != 0) {
char s[40];
uuidToString(&uuid, s, 40);
LOGW("Error querying effect %s on lib %s", s, l->name);
free(d);
return -EINVAL;
}
#if (LOG_NDEBUG==0)
char s[256];
dumpEffectDescriptor(d, s, 256);
LOGV("loadEffect() read descriptor %p:%s",d, s);
#endif
if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
LOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
free(d);
return -EINVAL;
}
e = malloc(sizeof(list_elem_t));
e->object = d;
e->next = l->effects;
l->effects = e;
return 0;
}
lib_entry_t *getLibrary(const char *name)
{
list_elem_t *e;
if (gCachedLibrary &&
!strncmp(gCachedLibrary->name, name, PATH_MAX)) {
return gCachedLibrary;
}
e = gLibraryList;
while (e) {
lib_entry_t *l = (lib_entry_t *)e->object;
if (!strcmp(l->name, name)) {
gCachedLibrary = l;
return l;
}
e = e->next;
}
return NULL;
}
void resetEffectEnumeration()
{
gCurLib = gLibraryList;
gCurEffect = NULL;
if (gCurLib) {
gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
}
gCurEffectIdx = 0;
}
uint32_t updateNumEffects() {
list_elem_t *e;
uint32_t cnt = 0;
resetEffectEnumeration();
e = gLibraryList;
while (e) {
lib_entry_t *l = (lib_entry_t *)e->object;
list_elem_t *efx = l->effects;
while (efx) {
cnt++;
efx = efx->next;
}
e = e->next;
}
gNumEffects = cnt;
gCanQueryEffect = 0;
return cnt;
}
int findEffect(effect_uuid_t *type,
effect_uuid_t *uuid,
lib_entry_t **lib,
effect_descriptor_t **desc)
{
list_elem_t *e = gLibraryList;
lib_entry_t *l = NULL;
effect_descriptor_t *d = NULL;
int found = 0;
int ret = 0;
while (e && !found) {
l = (lib_entry_t *)e->object;
list_elem_t *efx = l->effects;
while (efx) {
d = (effect_descriptor_t *)efx->object;
if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
found = 1;
break;
}
if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
found = 1;
break;
}
efx = efx->next;
}
e = e->next;
}
if (!found) {
LOGV("findEffect() effect not found");
ret = -ENOENT;
} else {
LOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
*lib = l;
if (desc) {
*desc = d;
}
}
return ret;
}
void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len) {
char s[256];
snprintf(str, len, "\nEffect Descriptor %p:\n", desc);
strncat(str, "- TYPE: ", len);
uuidToString(&desc->uuid, s, 256);
snprintf(str, len, "- UUID: %s\n", s);
uuidToString(&desc->type, s, 256);
snprintf(str, len, "- TYPE: %s\n", s);
sprintf(s, "- apiVersion: %08X\n- flags: %08X\n",
desc->apiVersion, desc->flags);
strncat(str, s, len);
sprintf(s, "- name: %s\n", desc->name);
strncat(str, s, len);
sprintf(s, "- implementor: %s\n", desc->implementor);
strncat(str, s, len);
}
int stringToUuid(const char *str, effect_uuid_t *uuid)
{
int tmp[10];
if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
return -EINVAL;
}
uuid->timeLow = (uint32_t)tmp[0];
uuid->timeMid = (uint16_t)tmp[1];
uuid->timeHiAndVersion = (uint16_t)tmp[2];
uuid->clockSeq = (uint16_t)tmp[3];
uuid->node[0] = (uint8_t)tmp[4];
uuid->node[1] = (uint8_t)tmp[5];
uuid->node[2] = (uint8_t)tmp[6];
uuid->node[3] = (uint8_t)tmp[7];
uuid->node[4] = (uint8_t)tmp[8];
uuid->node[5] = (uint8_t)tmp[9];
return 0;
}
int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
{
snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
uuid->timeLow,
uuid->timeMid,
uuid->timeHiAndVersion,
uuid->clockSeq,
uuid->node[0],
uuid->node[1],
uuid->node[2],
uuid->node[3],
uuid->node[4],
uuid->node[5]);
return 0;
}