/*
* 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,
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;
}
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);
}