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.
770 lines
24 KiB
770 lines
24 KiB
/*
|
|
* Copyright (C) 2014 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 "offload_effect_bundle"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <cutils/list.h>
|
|
#include <cutils/log.h>
|
|
#include <system/thread_defs.h>
|
|
#include <tinyalsa/asoundlib.h>
|
|
#include <hardware/audio_effect.h>
|
|
|
|
#include "bundle.h"
|
|
#include "equalizer.h"
|
|
#include "bass_boost.h"
|
|
#include "virtualizer.h"
|
|
#include "reverb.h"
|
|
|
|
enum {
|
|
EFFECT_STATE_UNINITIALIZED,
|
|
EFFECT_STATE_INITIALIZED,
|
|
EFFECT_STATE_ACTIVE,
|
|
};
|
|
|
|
const effect_descriptor_t *descriptors[] = {
|
|
&equalizer_descriptor,
|
|
&bassboost_descriptor,
|
|
&virtualizer_descriptor,
|
|
&aux_env_reverb_descriptor,
|
|
&ins_env_reverb_descriptor,
|
|
&aux_preset_reverb_descriptor,
|
|
&ins_preset_reverb_descriptor,
|
|
NULL,
|
|
};
|
|
|
|
pthread_once_t once = PTHREAD_ONCE_INIT;
|
|
int init_status;
|
|
/*
|
|
* list of created effects.
|
|
* Updated by offload_effects_bundle_hal_start_output()
|
|
* and offload_effects_bundle_hal_stop_output()
|
|
*/
|
|
struct listnode created_effects_list;
|
|
/*
|
|
* list of active output streams.
|
|
* Updated by offload_effects_bundle_hal_start_output()
|
|
* and offload_effects_bundle_hal_stop_output()
|
|
*/
|
|
struct listnode active_outputs_list;
|
|
/*
|
|
* lock must be held when modifying or accessing
|
|
* created_effects_list or active_outputs_list
|
|
*/
|
|
pthread_mutex_t lock;
|
|
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
static void init_once() {
|
|
list_init(&created_effects_list);
|
|
list_init(&active_outputs_list);
|
|
|
|
pthread_mutex_init(&lock, NULL);
|
|
|
|
init_status = 0;
|
|
}
|
|
|
|
int lib_init()
|
|
{
|
|
pthread_once(&once, init_once);
|
|
return init_status;
|
|
}
|
|
|
|
bool effect_exists(effect_context_t *context)
|
|
{
|
|
struct listnode *node;
|
|
|
|
list_for_each(node, &created_effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(node,
|
|
effect_context_t,
|
|
effects_list_node);
|
|
if (fx_ctxt == context) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
output_context_t *get_output(audio_io_handle_t output)
|
|
{
|
|
struct listnode *node;
|
|
|
|
list_for_each(node, &active_outputs_list) {
|
|
output_context_t *out_ctxt = node_to_item(node,
|
|
output_context_t,
|
|
outputs_list_node);
|
|
if (out_ctxt->handle == output)
|
|
return out_ctxt;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void add_effect_to_output(output_context_t * output, effect_context_t *context)
|
|
{
|
|
struct listnode *fx_node;
|
|
|
|
list_for_each(fx_node, &output->effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(fx_node,
|
|
effect_context_t,
|
|
output_node);
|
|
if (fx_ctxt == context)
|
|
return;
|
|
}
|
|
list_add_tail(&output->effects_list, &context->output_node);
|
|
if (context->ops.start)
|
|
context->ops.start(context, output);
|
|
|
|
}
|
|
|
|
void remove_effect_from_output(output_context_t * output,
|
|
effect_context_t *context)
|
|
{
|
|
struct listnode *fx_node;
|
|
|
|
list_for_each(fx_node, &output->effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(fx_node,
|
|
effect_context_t,
|
|
output_node);
|
|
if (fx_ctxt == context) {
|
|
if (context->ops.stop)
|
|
context->ops.stop(context, output);
|
|
list_remove(&context->output_node);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool effects_enabled()
|
|
{
|
|
struct listnode *out_node;
|
|
|
|
list_for_each(out_node, &active_outputs_list) {
|
|
struct listnode *fx_node;
|
|
output_context_t *out_ctxt = node_to_item(out_node,
|
|
output_context_t,
|
|
outputs_list_node);
|
|
|
|
list_for_each(fx_node, &out_ctxt->effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(fx_node,
|
|
effect_context_t,
|
|
output_node);
|
|
if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) &&
|
|
(fx_ctxt->ops.process != NULL))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Interface from audio HAL
|
|
*/
|
|
__attribute__ ((visibility ("default")))
|
|
int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id)
|
|
{
|
|
int ret = 0;
|
|
struct listnode *node;
|
|
char mixer_string[128];
|
|
output_context_t * out_ctxt = NULL;
|
|
|
|
ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
|
|
|
|
if (lib_init() != 0)
|
|
return init_status;
|
|
|
|
pthread_mutex_lock(&lock);
|
|
if (get_output(output) != NULL) {
|
|
ALOGW("%s output already started", __func__);
|
|
ret = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
|
|
out_ctxt = (output_context_t *)
|
|
malloc(sizeof(output_context_t));
|
|
out_ctxt->handle = output;
|
|
out_ctxt->pcm_device_id = pcm_id;
|
|
|
|
/* populate the mixer control to send offload parameters */
|
|
snprintf(mixer_string, sizeof(mixer_string),
|
|
"%s %d", "Audio Effects Config", out_ctxt->pcm_device_id);
|
|
out_ctxt->mixer = mixer_open(MIXER_CARD);
|
|
if (!out_ctxt->mixer) {
|
|
ALOGE("Failed to open mixer");
|
|
out_ctxt->ctl = NULL;
|
|
ret = -EINVAL;
|
|
free(out_ctxt);
|
|
goto exit;
|
|
} else {
|
|
out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string);
|
|
if (!out_ctxt->ctl) {
|
|
ALOGE("mixer_get_ctl_by_name failed");
|
|
mixer_close(out_ctxt->mixer);
|
|
out_ctxt->mixer = NULL;
|
|
ret = -EINVAL;
|
|
free(out_ctxt);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
list_init(&out_ctxt->effects_list);
|
|
|
|
list_for_each(node, &created_effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(node,
|
|
effect_context_t,
|
|
effects_list_node);
|
|
if (fx_ctxt->out_handle == output) {
|
|
if (fx_ctxt->ops.start)
|
|
fx_ctxt->ops.start(fx_ctxt, out_ctxt);
|
|
list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
|
|
}
|
|
}
|
|
list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
|
|
exit:
|
|
pthread_mutex_unlock(&lock);
|
|
return ret;
|
|
}
|
|
|
|
__attribute__ ((visibility ("default")))
|
|
int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id)
|
|
{
|
|
int ret = 0;
|
|
struct listnode *node;
|
|
struct listnode *fx_node;
|
|
output_context_t *out_ctxt;
|
|
|
|
ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
|
|
|
|
if (lib_init() != 0)
|
|
return init_status;
|
|
|
|
pthread_mutex_lock(&lock);
|
|
|
|
out_ctxt = get_output(output);
|
|
if (out_ctxt == NULL) {
|
|
ALOGW("%s output not started", __func__);
|
|
ret = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
|
|
if (out_ctxt->mixer)
|
|
mixer_close(out_ctxt->mixer);
|
|
|
|
list_for_each(fx_node, &out_ctxt->effects_list) {
|
|
effect_context_t *fx_ctxt = node_to_item(fx_node,
|
|
effect_context_t,
|
|
output_node);
|
|
if (fx_ctxt->ops.stop)
|
|
fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
|
|
}
|
|
|
|
list_remove(&out_ctxt->outputs_list_node);
|
|
|
|
free(out_ctxt);
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Effect operations
|
|
*/
|
|
int set_config(effect_context_t *context, effect_config_t *config)
|
|
{
|
|
context->config = *config;
|
|
|
|
if (context->ops.reset)
|
|
context->ops.reset(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void get_config(effect_context_t *context, effect_config_t *config)
|
|
{
|
|
*config = context->config;
|
|
}
|
|
|
|
|
|
/*
|
|
* Effect Library Interface Implementation
|
|
*/
|
|
int effect_lib_create(const effect_uuid_t *uuid,
|
|
int32_t sessionId,
|
|
int32_t ioId,
|
|
effect_handle_t *pHandle) {
|
|
int ret;
|
|
int i;
|
|
|
|
ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId);
|
|
if (lib_init() != 0)
|
|
return init_status;
|
|
|
|
if (pHandle == NULL || uuid == NULL)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; descriptors[i] != NULL; i++) {
|
|
if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
|
|
break;
|
|
}
|
|
|
|
if (descriptors[i] == NULL)
|
|
return -EINVAL;
|
|
|
|
effect_context_t *context;
|
|
if (memcmp(uuid, &equalizer_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
equalizer_context_t *eq_ctxt = (equalizer_context_t *)
|
|
calloc(1, sizeof(equalizer_context_t));
|
|
context = (effect_context_t *)eq_ctxt;
|
|
context->ops.init = equalizer_init;
|
|
context->ops.reset = equalizer_reset;
|
|
context->ops.set_parameter = equalizer_set_parameter;
|
|
context->ops.get_parameter = equalizer_get_parameter;
|
|
context->ops.set_device = equalizer_set_device;
|
|
context->ops.enable = equalizer_enable;
|
|
context->ops.disable = equalizer_disable;
|
|
context->ops.start = equalizer_start;
|
|
context->ops.stop = equalizer_stop;
|
|
|
|
context->desc = &equalizer_descriptor;
|
|
eq_ctxt->ctl = NULL;
|
|
} else if (memcmp(uuid, &bassboost_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
bassboost_context_t *bass_ctxt = (bassboost_context_t *)
|
|
calloc(1, sizeof(bassboost_context_t));
|
|
context = (effect_context_t *)bass_ctxt;
|
|
context->ops.init = bassboost_init;
|
|
context->ops.reset = bassboost_reset;
|
|
context->ops.set_parameter = bassboost_set_parameter;
|
|
context->ops.get_parameter = bassboost_get_parameter;
|
|
context->ops.set_device = bassboost_set_device;
|
|
context->ops.enable = bassboost_enable;
|
|
context->ops.disable = bassboost_disable;
|
|
context->ops.start = bassboost_start;
|
|
context->ops.stop = bassboost_stop;
|
|
|
|
context->desc = &bassboost_descriptor;
|
|
bass_ctxt->ctl = NULL;
|
|
} else if (memcmp(uuid, &virtualizer_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)
|
|
calloc(1, sizeof(virtualizer_context_t));
|
|
context = (effect_context_t *)virt_ctxt;
|
|
context->ops.init = virtualizer_init;
|
|
context->ops.reset = virtualizer_reset;
|
|
context->ops.set_parameter = virtualizer_set_parameter;
|
|
context->ops.get_parameter = virtualizer_get_parameter;
|
|
context->ops.set_device = virtualizer_set_device;
|
|
context->ops.enable = virtualizer_enable;
|
|
context->ops.disable = virtualizer_disable;
|
|
context->ops.start = virtualizer_start;
|
|
context->ops.stop = virtualizer_stop;
|
|
|
|
context->desc = &virtualizer_descriptor;
|
|
virt_ctxt->ctl = NULL;
|
|
} else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) ||
|
|
(memcmp(uuid, &ins_env_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) ||
|
|
(memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) ||
|
|
(memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0)) {
|
|
reverb_context_t *reverb_ctxt = (reverb_context_t *)
|
|
calloc(1, sizeof(reverb_context_t));
|
|
context = (effect_context_t *)reverb_ctxt;
|
|
context->ops.init = reverb_init;
|
|
context->ops.reset = reverb_reset;
|
|
context->ops.set_parameter = reverb_set_parameter;
|
|
context->ops.get_parameter = reverb_get_parameter;
|
|
context->ops.set_device = reverb_set_device;
|
|
context->ops.enable = reverb_enable;
|
|
context->ops.disable = reverb_disable;
|
|
context->ops.start = reverb_start;
|
|
context->ops.stop = reverb_stop;
|
|
|
|
if (memcmp(uuid, &aux_env_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
context->desc = &aux_env_reverb_descriptor;
|
|
reverb_auxiliary_init(reverb_ctxt);
|
|
} else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
context->desc = &ins_env_reverb_descriptor;
|
|
reverb_insert_init(reverb_ctxt);
|
|
} else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
context->desc = &aux_preset_reverb_descriptor;
|
|
reverb_auxiliary_init(reverb_ctxt);
|
|
} else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
|
|
sizeof(effect_uuid_t)) == 0) {
|
|
context->desc = &ins_preset_reverb_descriptor;
|
|
reverb_preset_init(reverb_ctxt);
|
|
}
|
|
reverb_ctxt->ctl = NULL;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
context->itfe = &effect_interface;
|
|
context->state = EFFECT_STATE_UNINITIALIZED;
|
|
context->out_handle = (audio_io_handle_t)ioId;
|
|
|
|
ret = context->ops.init(context);
|
|
if (ret < 0) {
|
|
ALOGW("%s init failed", __func__);
|
|
free(context);
|
|
return ret;
|
|
}
|
|
|
|
context->state = EFFECT_STATE_INITIALIZED;
|
|
|
|
pthread_mutex_lock(&lock);
|
|
list_add_tail(&created_effects_list, &context->effects_list_node);
|
|
output_context_t *out_ctxt = get_output(ioId);
|
|
if (out_ctxt != NULL)
|
|
add_effect_to_output(out_ctxt, context);
|
|
pthread_mutex_unlock(&lock);
|
|
|
|
*pHandle = (effect_handle_t)context;
|
|
|
|
ALOGV("%s created context %p", __func__, context);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int effect_lib_release(effect_handle_t handle)
|
|
{
|
|
effect_context_t *context = (effect_context_t *)handle;
|
|
int status;
|
|
|
|
if (lib_init() != 0)
|
|
return init_status;
|
|
|
|
ALOGV("%s context %p", __func__, handle);
|
|
pthread_mutex_lock(&lock);
|
|
status = -EINVAL;
|
|
if (effect_exists(context)) {
|
|
output_context_t *out_ctxt = get_output(context->out_handle);
|
|
if (out_ctxt != NULL)
|
|
remove_effect_from_output(out_ctxt, context);
|
|
list_remove(&context->effects_list_node);
|
|
if (context->ops.release)
|
|
context->ops.release(context);
|
|
free(context);
|
|
status = 0;
|
|
}
|
|
pthread_mutex_unlock(&lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
int effect_lib_get_descriptor(const effect_uuid_t *uuid,
|
|
effect_descriptor_t *descriptor)
|
|
{
|
|
int i;
|
|
|
|
if (lib_init() != 0)
|
|
return init_status;
|
|
|
|
if (descriptor == NULL || uuid == NULL) {
|
|
ALOGV("%s called with NULL pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; descriptors[i] != NULL; i++) {
|
|
if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
|
|
*descriptor = *descriptors[i];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Effect Control Interface Implementation
|
|
*/
|
|
|
|
/* Stub function for effect interface: never called for offloaded effects */
|
|
int effect_process(effect_handle_t self,
|
|
audio_buffer_t *inBuffer __unused,
|
|
audio_buffer_t *outBuffer __unused)
|
|
{
|
|
effect_context_t * context = (effect_context_t *)self;
|
|
int status = 0;
|
|
|
|
ALOGW("%s Called ?????", __func__);
|
|
|
|
pthread_mutex_lock(&lock);
|
|
if (!effect_exists(context)) {
|
|
status = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
|
|
if (context->state != EFFECT_STATE_ACTIVE) {
|
|
status = -ENODATA;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&lock);
|
|
return status;
|
|
}
|
|
|
|
int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
|
|
void *pCmdData, uint32_t *replySize, void *pReplyData)
|
|
{
|
|
|
|
effect_context_t * context = (effect_context_t *)self;
|
|
int retsize;
|
|
int status = 0;
|
|
|
|
pthread_mutex_lock(&lock);
|
|
|
|
if (!effect_exists(context)) {
|
|
status = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
|
|
if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
|
|
status = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
|
|
switch (cmdCode) {
|
|
case EFFECT_CMD_INIT:
|
|
if (pReplyData == NULL || *replySize != sizeof(int)) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
if (context->ops.init)
|
|
*(int *) pReplyData = context->ops.init(context);
|
|
else
|
|
*(int *) pReplyData = 0;
|
|
break;
|
|
case EFFECT_CMD_SET_CONFIG:
|
|
if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
|
|
|| pReplyData == NULL || *replySize != sizeof(int)) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
*(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
|
|
break;
|
|
case EFFECT_CMD_GET_CONFIG:
|
|
if (pReplyData == NULL ||
|
|
*replySize != sizeof(effect_config_t)) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
if (!context->offload_enabled) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
get_config(context, (effect_config_t *)pReplyData);
|
|
break;
|
|
case EFFECT_CMD_RESET:
|
|
if (context->ops.reset)
|
|
context->ops.reset(context);
|
|
break;
|
|
case EFFECT_CMD_ENABLE:
|
|
if (pReplyData == NULL || *replySize != sizeof(int)) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
if (context->state != EFFECT_STATE_INITIALIZED) {
|
|
status = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
context->state = EFFECT_STATE_ACTIVE;
|
|
if (context->ops.enable)
|
|
context->ops.enable(context);
|
|
ALOGV("%s EFFECT_CMD_ENABLE", __func__);
|
|
*(int *)pReplyData = 0;
|
|
break;
|
|
case EFFECT_CMD_DISABLE:
|
|
if (pReplyData == NULL || *replySize != sizeof(int)) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
if (context->state != EFFECT_STATE_ACTIVE) {
|
|
status = -ENOSYS;
|
|
goto exit;
|
|
}
|
|
context->state = EFFECT_STATE_INITIALIZED;
|
|
if (context->ops.disable)
|
|
context->ops.disable(context);
|
|
ALOGV("%s EFFECT_CMD_DISABLE", __func__);
|
|
*(int *)pReplyData = 0;
|
|
break;
|
|
case EFFECT_CMD_GET_PARAM: {
|
|
if (pCmdData == NULL ||
|
|
cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
|
|
pReplyData == NULL ||
|
|
*replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint16_t)) ||
|
|
// constrain memcpy below
|
|
((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
|
|
status = -EINVAL;
|
|
ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
|
|
cmdSize, *replySize);
|
|
goto exit;
|
|
}
|
|
if (!context->offload_enabled) {
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
effect_param_t *q = (effect_param_t *)pCmdData;
|
|
memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize);
|
|
effect_param_t *p = (effect_param_t *)pReplyData;
|
|
if (context->ops.get_parameter)
|
|
context->ops.get_parameter(context, p, replySize);
|
|
} break;
|
|
case EFFECT_CMD_SET_PARAM: {
|
|
if (pCmdData == NULL ||
|
|
cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
|
|
sizeof(uint16_t)) ||
|
|
pReplyData == NULL || *replySize != sizeof(int32_t)) {
|
|
status = -EINVAL;
|
|
ALOGV("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d",
|
|
cmdSize, *replySize);
|
|
goto exit;
|
|
}
|
|
*(int32_t *)pReplyData = 0;
|
|
effect_param_t *p = (effect_param_t *)pCmdData;
|
|
if (context->ops.set_parameter)
|
|
*(int32_t *)pReplyData = context->ops.set_parameter(context, p,
|
|
*replySize);
|
|
|
|
} break;
|
|
case EFFECT_CMD_SET_DEVICE: {
|
|
uint32_t device;
|
|
ALOGV("\t EFFECT_CMD_SET_DEVICE start");
|
|
if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) {
|
|
status = -EINVAL;
|
|
ALOGV("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize);
|
|
goto exit;
|
|
}
|
|
device = *(uint32_t *)pCmdData;
|
|
if (context->ops.set_device)
|
|
context->ops.set_device(context, device);
|
|
} break;
|
|
case EFFECT_CMD_SET_VOLUME: {
|
|
// if pReplyData is NULL, VOL_CTRL is delegated to another effect
|
|
if (pReplyData == NULL) {
|
|
break;
|
|
}
|
|
if (pCmdData == NULL || cmdSize != 2 * sizeof(uint32_t) ||
|
|
replySize == NULL || *replySize < 2*sizeof(int32_t)) {
|
|
return -EINVAL;
|
|
}
|
|
memcpy(pReplyData, pCmdData, sizeof(int32_t)*2);
|
|
} break;
|
|
case EFFECT_CMD_SET_AUDIO_MODE:
|
|
break;
|
|
case EFFECT_CMD_OFFLOAD: {
|
|
output_context_t *out_ctxt;
|
|
|
|
if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
|
|
|| pReplyData == NULL || *replySize != sizeof(int)) {
|
|
ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
|
|
status = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
|
|
|
|
ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__,
|
|
offload_param->isOffload, offload_param->ioHandle);
|
|
|
|
*(int *)pReplyData = 0;
|
|
|
|
context->offload_enabled = offload_param->isOffload;
|
|
if (context->out_handle == offload_param->ioHandle)
|
|
break;
|
|
|
|
out_ctxt = get_output(context->out_handle);
|
|
if (out_ctxt != NULL)
|
|
remove_effect_from_output(out_ctxt, context);
|
|
|
|
context->out_handle = offload_param->ioHandle;
|
|
out_ctxt = get_output(context->out_handle);
|
|
if (out_ctxt != NULL)
|
|
add_effect_to_output(out_ctxt, context);
|
|
|
|
} break;
|
|
default:
|
|
if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
|
|
status = context->ops.command(context, cmdCode, cmdSize,
|
|
pCmdData, replySize, pReplyData);
|
|
else {
|
|
ALOGW("%s invalid command %d", __func__, cmdCode);
|
|
status = -EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
pthread_mutex_unlock(&lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Effect Control Interface Implementation: get_descriptor */
|
|
int effect_get_descriptor(effect_handle_t self,
|
|
effect_descriptor_t *descriptor)
|
|
{
|
|
effect_context_t *context = (effect_context_t *)self;
|
|
|
|
if (!effect_exists(context) || (descriptor == NULL))
|
|
return -EINVAL;
|
|
|
|
*descriptor = *context->desc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool effect_is_active(effect_context_t * ctxt) {
|
|
return ctxt->state == EFFECT_STATE_ACTIVE;
|
|
}
|
|
|
|
/* effect_handle_t interface implementation for offload effects */
|
|
const struct effect_interface_s effect_interface = {
|
|
effect_process,
|
|
effect_command,
|
|
effect_get_descriptor,
|
|
NULL,
|
|
};
|
|
|
|
__attribute__ ((visibility ("default")))
|
|
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
|
|
.tag = AUDIO_EFFECT_LIBRARY_TAG,
|
|
.version = EFFECT_LIBRARY_API_VERSION,
|
|
.name = "Offload Effects Bundle Library",
|
|
.implementor = "The Android Open Source Project",
|
|
.create_effect = effect_lib_create,
|
|
.release_effect = effect_lib_release,
|
|
.get_descriptor = effect_lib_get_descriptor,
|
|
};
|