You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
622 lines
17 KiB
622 lines
17 KiB
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cutils/properties.h>
|
|
#include <log/log.h>
|
|
|
|
#include <media/EffectsFactoryApi.h>
|
|
|
|
#include "EffectsConfigLoader.h"
|
|
#include "EffectsFactoryState.h"
|
|
#include "EffectsXmlConfigLoader.h"
|
|
|
|
#include "EffectsFactory.h"
|
|
|
|
static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
|
|
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
|
|
/** Number of elements skipped during the effects configuration loading.
|
|
* -1 if the config loader failed
|
|
* -2 if config load was skipped
|
|
*/
|
|
static ssize_t gConfigNbElemSkipped = -2;
|
|
|
|
static int gInitDone; // true is global initialization has been preformed
|
|
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 void resetEffectEnumeration();
|
|
static uint32_t updateNumEffects();
|
|
// To search a subeffect in the gSubEffectList
|
|
static int findSubEffect(const effect_uuid_t *uuid,
|
|
lib_entry_t **lib,
|
|
effect_descriptor_t **desc);
|
|
|
|
/////////////////////////////////////////////////
|
|
// Effect Control Interface functions
|
|
/////////////////////////////////////////////////
|
|
|
|
int Effect_Process(effect_handle_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_handle_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;
|
|
}
|
|
|
|
int Effect_GetDescriptor(effect_handle_t self,
|
|
effect_descriptor_t *desc)
|
|
{
|
|
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)->get_descriptor(fx->subItfe, desc);
|
|
pthread_mutex_unlock(&fx->lib->lock);
|
|
return ret;
|
|
}
|
|
|
|
int Effect_ProcessReverse(effect_handle_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);
|
|
|
|
if ((*fx->subItfe)->process_reverse != NULL) {
|
|
ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
|
|
} else {
|
|
ret = -ENOSYS;
|
|
}
|
|
pthread_mutex_unlock(&fx->lib->lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
const struct effect_interface_s gInterface = {
|
|
Effect_Process,
|
|
Effect_Command,
|
|
Effect_GetDescriptor,
|
|
NULL
|
|
};
|
|
|
|
const struct effect_interface_s gInterfaceWithReverse = {
|
|
Effect_Process,
|
|
Effect_Command,
|
|
Effect_GetDescriptor,
|
|
Effect_ProcessReverse
|
|
};
|
|
|
|
/////////////////////////////////////////////////
|
|
// 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);
|
|
ALOGV("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) {
|
|
*pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
|
|
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[512];
|
|
dumpEffectDescriptor(pDescriptor, str, sizeof(str), 0 /* indent */);
|
|
ALOGV("EffectQueryEffect() desc:%s", str);
|
|
#endif
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int EffectGetDescriptor(const 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(NULL, uuid, &l, &d);
|
|
if (ret == 0) {
|
|
*pDescriptor = *d;
|
|
}
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int doEffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, int32_t deviceId,
|
|
effect_handle_t *pHandle)
|
|
{
|
|
list_elem_t *e = gLibraryList;
|
|
lib_entry_t *l = NULL;
|
|
effect_descriptor_t *d = NULL;
|
|
effect_handle_t itfe;
|
|
effect_entry_t *fx;
|
|
int ret;
|
|
|
|
if (uuid == NULL || pHandle == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ALOGV("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) {
|
|
ALOGW("EffectCreate() init error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
pthread_mutex_lock(&gLibLock);
|
|
|
|
ret = findEffect(NULL, uuid, &l, &d);
|
|
if (ret < 0) {
|
|
// Sub effects are not associated with the library->effects,
|
|
// so, findEffect will fail. Search for the effect in gSubEffectList.
|
|
ret = findSubEffect(uuid, &l, &d);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// create effect in library
|
|
if (sessionId == AUDIO_SESSION_DEVICE) {
|
|
if (l->desc->version >= EFFECT_LIBRARY_API_VERSION_3_1) {
|
|
ALOGI("EffectCreate() create_effect_3_1");
|
|
ret = l->desc->create_effect_3_1(uuid, sessionId, ioId, deviceId, &itfe);
|
|
} else {
|
|
ALOGE("EffectCreate() cannot create device effect on library with API version < 3.1");
|
|
ret = -ENOSYS;
|
|
}
|
|
} else {
|
|
ALOGI("EffectCreate() create_effect");
|
|
ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
|
|
}
|
|
|
|
if (ret != 0) {
|
|
ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
|
|
goto exit;
|
|
}
|
|
|
|
// add entry to effect list
|
|
fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
|
|
fx->subItfe = itfe;
|
|
if ((*itfe)->process_reverse != NULL) {
|
|
fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
|
|
ALOGV("EffectCreate() gInterfaceWithReverse");
|
|
} else {
|
|
fx->itfe = (struct effect_interface_s *)&gInterface;
|
|
ALOGV("EffectCreate() gInterface");
|
|
}
|
|
fx->lib = l;
|
|
|
|
e = (list_elem_t *)malloc(sizeof(list_elem_t));
|
|
e->object = fx;
|
|
e->next = gEffectList;
|
|
gEffectList = e;
|
|
|
|
*pHandle = (effect_handle_t)fx;
|
|
|
|
ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId,
|
|
effect_handle_t *pHandle) {
|
|
return doEffectCreate(uuid, sessionId, ioId, AUDIO_PORT_HANDLE_NONE, pHandle);
|
|
}
|
|
|
|
int EffectCreateOnDevice(const effect_uuid_t *uuid, int32_t deviceId, int32_t ioId,
|
|
effect_handle_t *pHandle) {
|
|
return doEffectCreate(uuid, AUDIO_SESSION_DEVICE, ioId, deviceId, pHandle);
|
|
}
|
|
|
|
int EffectRelease(effect_handle_t handle)
|
|
{
|
|
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 == handle) {
|
|
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) {
|
|
ALOGW("EffectRelease() fx %p library already unloaded", handle);
|
|
} else {
|
|
pthread_mutex_lock(&fx->lib->lock);
|
|
fx->lib->desc->release_effect(fx->subItfe);
|
|
pthread_mutex_unlock(&fx->lib->lock);
|
|
}
|
|
free(fx);
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&gLibLock);
|
|
return ret;
|
|
}
|
|
|
|
int EffectIsNullUuid(const effect_uuid_t *uuid)
|
|
{
|
|
if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Function to get the sub effect descriptors of the effect whose uuid
|
|
// is pointed by the first argument. It searches the gSubEffectList for the
|
|
// matching uuid and then copies the corresponding sub effect descriptors
|
|
// to the inout param
|
|
int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube,
|
|
size_t size)
|
|
{
|
|
ALOGV("EffectGetSubEffects() 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]);
|
|
|
|
// Check if the size of the desc buffer is large enough for 2 subeffects
|
|
if ((uuid == NULL) || (pSube == NULL) || (size < 2)) {
|
|
ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
|
|
return -EINVAL;
|
|
}
|
|
int ret = init();
|
|
if (ret < 0)
|
|
return ret;
|
|
list_sub_elem_t *e = gSubEffectList;
|
|
sub_effect_entry_t *subeffect;
|
|
effect_descriptor_t *d;
|
|
int count = 0;
|
|
while (e != NULL) {
|
|
d = (effect_descriptor_t*)e->object;
|
|
if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
|
|
ALOGV("EffectGetSubEffects: effect found in the list");
|
|
list_elem_t *subefx = e->sub_elem;
|
|
while (subefx != NULL) {
|
|
subeffect = (sub_effect_entry_t*)subefx->object;
|
|
pSube[count++] = subeffect;
|
|
subefx = subefx->next;
|
|
}
|
|
ALOGV("EffectGetSubEffects end - copied the sub effect structures");
|
|
return count;
|
|
}
|
|
e = e->next;
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
/////////////////////////////////////////////////
|
|
// Local functions
|
|
/////////////////////////////////////////////////
|
|
|
|
int init() {
|
|
if (gInitDone) {
|
|
return 0;
|
|
}
|
|
|
|
// ignore effects or not?
|
|
const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false);
|
|
|
|
pthread_mutex_init(&gLibLock, NULL);
|
|
|
|
if (ignoreFxConfFiles) {
|
|
ALOGI("Audio effects in configuration files will be ignored");
|
|
} else {
|
|
gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL);
|
|
if (gConfigNbElemSkipped < 0) {
|
|
ALOGW("Failed to load XML effect configuration, fallback to .conf");
|
|
EffectLoadEffectConfig();
|
|
} else if (gConfigNbElemSkipped > 0) {
|
|
ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped);
|
|
}
|
|
}
|
|
|
|
updateNumEffects();
|
|
gInitDone = 1;
|
|
ALOGV("init() done");
|
|
return 0;
|
|
}
|
|
|
|
// Searches the sub effect matching to the specified uuid
|
|
// in the gSubEffectList. It gets the lib_entry_t for
|
|
// the matched sub_effect . Used in EffectCreate of sub effects
|
|
int findSubEffect(const effect_uuid_t *uuid,
|
|
lib_entry_t **lib,
|
|
effect_descriptor_t **desc)
|
|
{
|
|
list_sub_elem_t *e = gSubEffectList;
|
|
list_elem_t *subefx;
|
|
sub_effect_entry_t *effect;
|
|
lib_entry_t *l = NULL;
|
|
effect_descriptor_t *d = NULL;
|
|
int found = 0;
|
|
int ret = 0;
|
|
|
|
if (uuid == NULL)
|
|
return -EINVAL;
|
|
|
|
while (e != NULL && !found) {
|
|
subefx = (list_elem_t*)(e->sub_elem);
|
|
while (subefx != NULL) {
|
|
effect = (sub_effect_entry_t*)subefx->object;
|
|
l = (lib_entry_t *)effect->lib;
|
|
d = (effect_descriptor_t *)effect->object;
|
|
if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
|
|
ALOGV("uuid matched");
|
|
found = 1;
|
|
break;
|
|
}
|
|
subefx = subefx->next;
|
|
}
|
|
e = e->next;
|
|
}
|
|
if (!found) {
|
|
ALOGV("findSubEffect() effect not found");
|
|
ret = -ENOENT;
|
|
} else {
|
|
ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
|
|
*lib = l;
|
|
if (desc != NULL) {
|
|
*desc = d;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
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 EffectDumpEffects(int fd) {
|
|
char s[512];
|
|
|
|
list_elem_t *fe = gLibraryFailedList;
|
|
lib_failed_entry_t *fl = NULL;
|
|
|
|
dprintf(fd, "Libraries NOT loaded:\n");
|
|
|
|
while (fe) {
|
|
fl = (lib_failed_entry_t *)fe->object;
|
|
dprintf(fd, " Library %s\n", fl->name);
|
|
dprintf(fd, " path: %s\n", fl->path);
|
|
fe = fe->next;
|
|
}
|
|
|
|
list_elem_t *e = gLibraryList;
|
|
lib_entry_t *l = NULL;
|
|
effect_descriptor_t *d = NULL;
|
|
int ret = 0;
|
|
|
|
dprintf(fd, "Libraries loaded:\n");
|
|
while (e) {
|
|
l = (lib_entry_t *)e->object;
|
|
list_elem_t *efx = l->effects;
|
|
dprintf(fd, " Library %s\n", l->name);
|
|
dprintf(fd, " path: %s\n", l->path);
|
|
if (!efx) {
|
|
dprintf(fd, " (no effects)\n");
|
|
}
|
|
while (efx) {
|
|
d = (effect_descriptor_t *)efx->object;
|
|
dumpEffectDescriptor(d, s, sizeof(s), 2);
|
|
dprintf(fd, "%s", s);
|
|
efx = efx->next;
|
|
}
|
|
e = e->next;
|
|
}
|
|
|
|
e = gSkippedEffects;
|
|
if (e) {
|
|
dprintf(fd, "Skipped effects\n");
|
|
while(e) {
|
|
d = (effect_descriptor_t *)e->object;
|
|
dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */);
|
|
dprintf(fd, "%s", s);
|
|
e = e->next;
|
|
}
|
|
}
|
|
switch (gConfigNbElemSkipped) {
|
|
case -2:
|
|
dprintf(fd, "Effect configuration loading skipped.\n");
|
|
break;
|
|
case -1:
|
|
dprintf(fd, "XML effect configuration failed to load.\n");
|
|
break;
|
|
case 0:
|
|
dprintf(fd, "XML effect configuration loaded successfully.\n");
|
|
break;
|
|
default:
|
|
dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n",
|
|
gConfigNbElemSkipped);
|
|
}
|
|
return ret;
|
|
}
|
|
|