| /* |
| * 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 list_elem_t *gCurLib; // current library in enumeration process |
| static list_elem_t *gCurEffect; // current effect in enumeration process |
| |
| static const char * const gEffectLibPath = "/system/lib/soundfx"; // path to built-in effect libraries |
| static int gInitDone; // true is global initialization has been preformed |
| |
| ///////////////////////////////////////////////// |
| // Local functions prototypes |
| ///////////////////////////////////////////////// |
| |
| static int init(); |
| static int loadLibrary(const char *libPath, int *handle); |
| static int unloadLibrary(int handle); |
| static uint32_t numEffectModules(); |
| 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 = numEffectModules(); |
| pthread_mutex_unlock(&gLibLock); |
| LOGV("EffectQueryNumberEffects(): %d", *pNumEffects); |
| return ret; |
| } |
| |
| int EffectQueryNext(effect_descriptor_t *pDescriptor) |
| { |
| int ret = init(); |
| if (ret < 0) { |
| return ret; |
| } |
| if (pDescriptor == NULL) { |
| return -EINVAL; |
| } |
| |
| pthread_mutex_lock(&gLibLock); |
| ret = -ENOENT; |
| while (gCurLib) { |
| if (gCurEffect) { |
| memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t)); |
| gCurEffect = gCurEffect->next; |
| ret = 0; |
| break; |
| } else { |
| gCurLib = gCurLib->next; |
| gCurEffect = ((lib_entry_t *)gCurLib->object)->effects; |
| } |
| } |
| char str[256]; |
| dumpEffectDescriptor(pDescriptor, str, 256); |
| LOGV("EffectQueryNext() desc:%s", str); |
| 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, 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, &itfe); |
| if (ret < 0) { |
| LOGW("EffectCreate() library %s: could not create fx %s", l->path, d->name); |
| 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; |
| } |
| return loadLibrary(libPath, handle); |
| } |
| |
| int EffectUnloadLibrary(int handle) |
| { |
| int ret = init(); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return unloadLibrary(handle); |
| } |
| |
| 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); |
| |
| gInitDone = 1; |
| LOGV("init() done"); |
| return 0; |
| } |
| |
| |
| int loadLibrary(const char *libPath, int *handle) |
| { |
| void *hdl; |
| effect_QueryNumberEffects_t queryNumFx; |
| effect_QueryNextEffect_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_QueryNextEffect_t)dlsym(hdl, "EffectQueryNext"); |
| if (queryFx == NULL) { |
| LOGW("could not get EffectQueryNext 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(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); |
| } |
| } |
| // add entry for library in gLibraryList |
| l = malloc(sizeof(lib_entry_t)); |
| 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)); |
| pthread_mutex_lock(&gLibLock); |
| e->next = gLibraryList; |
| e->object = l; |
| gLibraryList = e; |
| pthread_mutex_unlock(&gLibLock); |
| LOGV("loadLibrary() linked library %p", l); |
| |
| *handle = (int)hdl; |
| |
| 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 == (int)l->handle) { |
| 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; |
| } |
| |
| |
| |
| uint32_t numEffectModules() { |
| list_elem_t *e = gLibraryList; |
| uint32_t cnt = 0; |
| |
| // Reset pointers for EffectQueryNext() |
| gCurLib = e; |
| if (e) { |
| gCurEffect = ((lib_entry_t *)e->object)->effects; |
| } |
| 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; |
| } |
| 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); |
| } |
| |