Added methods for audio effects management by audio policy manager. - control of total CPU load and memory used by effect engines - selection of output stream for global effects - added audio session id in parameter list for startOutput() and stopOutput(). this is not used in default audio policy manager implementation. Modifications of audio effect framework in AudioFlinger to allow moving and reconfiguring effect engines from one output mixer thread to another when audio tracks in the same session are moved or when requested by audio policy manager. Also fixed mutex deadlock problem with effect chains locks. Change-Id: Ida43484b06e9b890d6b9e53c13958d042720ebdb
643 lines
17 KiB
C
643 lines
17 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>
|
|
|
|
|
|
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
|
|
|
|
const char * const gEffectLibPath = "/system/lib/soundfx"; // path to built-in effect libraries
|
|
static int gInitDone; // true is global initialization has been preformed
|
|
static int gNextLibId; // used by loadLibrary() to allocate unique library handles
|
|
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 loadLibrary(const char *libPath, int *handle);
|
|
static int unloadLibrary(int handle);
|
|
static void resetEffectEnumeration();
|
|
static uint32_t updateNumEffects();
|
|
static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
|
|
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
|
|
|
|
/////////////////////////////////////////////////
|
|
// Effect Control Interface functions
|
|
/////////////////////////////////////////////////
|
|
|
|
int Effect_Process(effect_interface_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_interface_t self, int cmdCode, int cmdSize, void *pCmdData, int *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;
|
|
}
|
|
|
|
const struct effect_interface_s gInterface = {
|
|
Effect_Process,
|
|
Effect_Command
|
|
};
|
|
|
|
/////////////////////////////////////////////////
|
|
// 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(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_interface_t *pInterface)
|
|
{
|
|
list_elem_t *e = gLibraryList;
|
|
lib_entry_t *l = NULL;
|
|
effect_descriptor_t *d = NULL;
|
|
effect_interface_t itfe;
|
|
effect_entry_t *fx;
|
|
int found = 0;
|
|
int ret;
|
|
|
|
if (uuid == NULL || pInterface == 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(uuid, &l, &d);
|
|
if (ret < 0){
|
|
goto exit;
|
|
}
|
|
|
|
// create effect in library
|
|
ret = l->createFx(uuid, sessionId, ioId, &itfe);
|
|
if (ret != 0) {
|
|
LOGW("EffectCreate() library %s: could not create fx %s, error %d", l->path, d->name, ret);
|
|
goto exit;
|
|
}
|
|
|
|
// add entry to effect list
|
|
fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
|
|
fx->subItfe = itfe;
|
|
fx->itfe = (struct effect_interface_s *)&gInterface;
|
|
fx->lib = l;
|
|
|
|
e = (list_elem_t *)malloc(sizeof(list_elem_t));
|
|
e->object = fx;
|
|
e->next = gEffectList;
|
|
gEffectList = e;
|
|
|
|
*pInterface = (effect_interface_t)fx;
|
|
|
|
LOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pInterface, itfe, l->path);
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int EffectRelease(effect_interface_t interface)
|
|
{
|
|
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 == interface) {
|
|
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", interface);
|
|
} else {
|
|
pthread_mutex_lock(&fx->lib->lock);
|
|
fx->lib->releaseFx(fx->subItfe);
|
|
pthread_mutex_unlock(&fx->lib->lock);
|
|
}
|
|
free(fx);
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int EffectLoadLibrary(const char *libPath, int *handle)
|
|
{
|
|
int ret = init();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if (libPath == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = loadLibrary(libPath, handle);
|
|
updateNumEffects();
|
|
return ret;
|
|
}
|
|
|
|
int EffectUnloadLibrary(int handle)
|
|
{
|
|
int ret = init();
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = unloadLibrary(handle);
|
|
updateNumEffects();
|
|
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() {
|
|
struct dirent *ent;
|
|
DIR *dir = NULL;
|
|
char libpath[PATH_MAX];
|
|
int hdl;
|
|
|
|
if (gInitDone) {
|
|
return 0;
|
|
}
|
|
|
|
pthread_mutex_init(&gLibLock, NULL);
|
|
|
|
// load built-in libraries
|
|
dir = opendir(gEffectLibPath);
|
|
if (dir == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
LOGV("init() reading file %s", ent->d_name);
|
|
if ((strlen(ent->d_name) < 3) ||
|
|
strncmp(ent->d_name, "lib", 3) != 0 ||
|
|
strncmp(ent->d_name + strlen(ent->d_name) - 3, ".so", 3) != 0) {
|
|
continue;
|
|
}
|
|
strcpy(libpath, gEffectLibPath);
|
|
strcat(libpath, "/");
|
|
strcat(libpath, ent->d_name);
|
|
if (loadLibrary(libpath, &hdl) < 0) {
|
|
LOGW("init() failed to load library %s",libpath);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
updateNumEffects();
|
|
gInitDone = 1;
|
|
LOGV("init() done");
|
|
return 0;
|
|
}
|
|
|
|
|
|
int loadLibrary(const char *libPath, int *handle)
|
|
{
|
|
void *hdl;
|
|
effect_QueryNumberEffects_t queryNumFx;
|
|
effect_QueryEffect_t queryFx;
|
|
effect_CreateEffect_t createFx;
|
|
effect_ReleaseEffect_t releaseFx;
|
|
uint32_t numFx;
|
|
uint32_t fx;
|
|
int ret;
|
|
list_elem_t *e, *descHead = NULL;
|
|
lib_entry_t *l;
|
|
|
|
if (handle == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*handle = 0;
|
|
|
|
hdl = dlopen(libPath, RTLD_NOW);
|
|
if (hdl == 0) {
|
|
LOGW("could open lib %s", libPath);
|
|
return -ENODEV;
|
|
}
|
|
|
|
// Check functions availability
|
|
queryNumFx = (effect_QueryNumberEffects_t)dlsym(hdl, "EffectQueryNumberEffects");
|
|
if (queryNumFx == NULL) {
|
|
LOGW("could not get EffectQueryNumberEffects from lib %s", libPath);
|
|
ret = -ENODEV;
|
|
goto error;
|
|
}
|
|
queryFx = (effect_QueryEffect_t)dlsym(hdl, "EffectQueryEffect");
|
|
if (queryFx == NULL) {
|
|
LOGW("could not get EffectQueryEffect from lib %s", libPath);
|
|
ret = -ENODEV;
|
|
goto error;
|
|
}
|
|
createFx = (effect_CreateEffect_t)dlsym(hdl, "EffectCreate");
|
|
if (createFx == NULL) {
|
|
LOGW("could not get EffectCreate from lib %s", libPath);
|
|
ret = -ENODEV;
|
|
goto error;
|
|
}
|
|
releaseFx = (effect_ReleaseEffect_t)dlsym(hdl, "EffectRelease");
|
|
if (releaseFx == NULL) {
|
|
LOGW("could not get EffectRelease from lib %s", libPath);
|
|
ret = -ENODEV;
|
|
goto error;
|
|
}
|
|
|
|
// load effect descriptors
|
|
ret = queryNumFx(&numFx);
|
|
if (ret) {
|
|
goto error;
|
|
}
|
|
|
|
for (fx = 0; fx < numFx; fx++) {
|
|
effect_descriptor_t *d = malloc(sizeof(effect_descriptor_t));
|
|
if (d == NULL) {
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
ret = queryFx(fx, d);
|
|
if (ret == 0) {
|
|
#if (LOG_NDEBUG==0)
|
|
char s[256];
|
|
dumpEffectDescriptor(d, s, 256);
|
|
LOGV("loadLibrary() read descriptor %p:%s",d, s);
|
|
#endif
|
|
if (d->apiVersion != EFFECT_API_VERSION) {
|
|
LOGW("Bad API version %04x on lib %s", d->apiVersion, libPath);
|
|
free(d);
|
|
continue;
|
|
}
|
|
e = malloc(sizeof(list_elem_t));
|
|
if (e == NULL) {
|
|
free(d);
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
e->object = d;
|
|
e->next = descHead;
|
|
descHead = e;
|
|
} else {
|
|
LOGW("Error querying effect # %d on lib %s", fx, libPath);
|
|
}
|
|
}
|
|
|
|
pthread_mutex_lock(&gLibLock);
|
|
|
|
// add entry for library in gLibraryList
|
|
l = malloc(sizeof(lib_entry_t));
|
|
l->id = ++gNextLibId;
|
|
l->handle = hdl;
|
|
strncpy(l->path, libPath, PATH_MAX);
|
|
l->createFx = createFx;
|
|
l->releaseFx = releaseFx;
|
|
l->effects = descHead;
|
|
pthread_mutex_init(&l->lock, NULL);
|
|
|
|
e = malloc(sizeof(list_elem_t));
|
|
e->next = gLibraryList;
|
|
e->object = l;
|
|
gLibraryList = e;
|
|
pthread_mutex_unlock(&gLibLock);
|
|
LOGV("loadLibrary() linked library %p", l);
|
|
|
|
*handle = l->id;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
LOGW("loadLibrary() error: %d on lib: %s", ret, libPath);
|
|
while (descHead) {
|
|
free(descHead->object);
|
|
e = descHead->next;
|
|
free(descHead);
|
|
descHead = e;;
|
|
}
|
|
dlclose(hdl);
|
|
return ret;
|
|
}
|
|
|
|
int unloadLibrary(int handle)
|
|
{
|
|
void *hdl;
|
|
int ret;
|
|
list_elem_t *el1, *el2;
|
|
lib_entry_t *l;
|
|
effect_entry_t *fx;
|
|
|
|
pthread_mutex_lock(&gLibLock);
|
|
el1 = gLibraryList;
|
|
el2 = NULL;
|
|
while (el1) {
|
|
l = (lib_entry_t *)el1->object;
|
|
if (handle == l->id) {
|
|
if (el2) {
|
|
el2->next = el1->next;
|
|
} else {
|
|
gLibraryList = el1->next;
|
|
}
|
|
free(el1);
|
|
break;
|
|
}
|
|
el2 = el1;
|
|
el1 = el1->next;
|
|
}
|
|
pthread_mutex_unlock(&gLibLock);
|
|
if (el1 == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
// clear effect descriptor list
|
|
el1 = l->effects;
|
|
while (el1) {
|
|
free(el1->object);
|
|
el2 = el1->next;
|
|
free(el1);
|
|
el1 = el2;
|
|
}
|
|
|
|
// disable all effects from this library
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
el1 = gEffectList;
|
|
while (el1) {
|
|
fx = (effect_entry_t *)el1->object;
|
|
if (fx->lib == l) {
|
|
fx->lib = NULL;
|
|
}
|
|
el1 = el1->next;
|
|
}
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
dlclose(l->handle);
|
|
free(l);
|
|
return 0;
|
|
}
|
|
|
|
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 *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 (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->path);
|
|
*lib = l;
|
|
*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);
|
|
sprintf(s, "- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
|
|
desc->uuid.timeLow, desc->uuid.timeMid, desc->uuid.timeHiAndVersion,
|
|
desc->uuid.clockSeq, desc->uuid.node[0], desc->uuid.node[1],desc->uuid.node[2],
|
|
desc->uuid.node[3],desc->uuid.node[4],desc->uuid.node[5]);
|
|
strncat(str, s, len);
|
|
sprintf(s, "- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
|
|
desc->type.timeLow, desc->type.timeMid, desc->type.timeHiAndVersion,
|
|
desc->type.clockSeq, desc->type.node[0], desc->type.node[1],desc->type.node[2],
|
|
desc->type.node[3],desc->type.node[4],desc->type.node[5]);
|
|
strncat(str, s, len);
|
|
sprintf(s, "- apiVersion: %04X\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);
|
|
}
|
|
|