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