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.
769 lines
24 KiB
769 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, |
|
};
|
|
|