From 2c8e5cab3faa6d360e222b7a6c40a80083d021ac Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 9 Jul 2010 12:28:50 -0700 Subject: First submission of audio effect library from NXP software. This CL contains the first open sourceable version of the audio effect library from NXP software. The effects implemented are: - Bass boost - Virtualizer (stereo widening) - Equalizer - Spectrum analyzer Source file for the effect engines are located under libeffects/lvm/lib The wrapper implementing the interface with the audio effect framework in under libeffects/lvm/wrapper The code of other effect libraries has also been reorganized fo clarity: - the effect factory is now under libeffects/factory - the test equalizer and reverb effects are under libeffect/testlibs - the visualizer is under libeffects/virtualizer Change-Id: I8d91e2181f81b89f8fc0c1e1e6bf552c5809b2eb --- media/libeffects/factory/Android.mk | 25 ++ media/libeffects/factory/EffectsFactory.c | 642 ++++++++++++++++++++++++++++++ media/libeffects/factory/EffectsFactory.h | 56 +++ 3 files changed, 723 insertions(+) create mode 100644 media/libeffects/factory/Android.mk create mode 100644 media/libeffects/factory/EffectsFactory.c create mode 100644 media/libeffects/factory/EffectsFactory.h (limited to 'media/libeffects/factory') diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk new file mode 100644 index 0000000..20f58e5 --- /dev/null +++ b/media/libeffects/factory/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH:= $(call my-dir) + +# Effect factory library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectsFactory.c + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) +LOCAL_MODULE:= libeffects + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c new file mode 100644 index 0000000..edd6184 --- /dev/null +++ b/media/libeffects/factory/EffectsFactory.c @@ -0,0 +1,642 @@ +/* + * 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 +#include +#include + + +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 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); +} + diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h new file mode 100644 index 0000000..8f543ca --- /dev/null +++ b/media/libeffects/factory/EffectsFactory.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef ANDROID_EFFECTSFACTORY_H_ +#define ANDROID_EFFECTSFACTORY_H_ + +#include +#include +#include +#include + + +#if __cplusplus +extern "C" { +#endif + +typedef struct list_elem_s { + void *object; + struct list_elem_s *next; +} list_elem_t; + +typedef struct lib_entry_s { + char path[PATH_MAX]; + void *handle; + int id; + effect_CreateEffect_t createFx; + effect_ReleaseEffect_t releaseFx; + list_elem_t *effects; //list of effect_descriptor_t + pthread_mutex_t lock; +} lib_entry_t; + +typedef struct effect_entry_s { + struct effect_interface_s *itfe; + effect_interface_t subItfe; + lib_entry_t *lib; +} effect_entry_t; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTSFACTORY_H_*/ -- cgit v1.1