Home | History | Annotate | Download | only in post_proc
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "offload_effect_bundle"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <stdlib.h>
     21 #include <cutils/list.h>
     22 #include <cutils/log.h>
     23 #include <system/thread_defs.h>
     24 #include <tinyalsa/asoundlib.h>
     25 #include <hardware/audio_effect.h>
     26 
     27 #include "bundle.h"
     28 #include "equalizer.h"
     29 #include "bass_boost.h"
     30 #include "virtualizer.h"
     31 #include "reverb.h"
     32 
     33 enum {
     34     EFFECT_STATE_UNINITIALIZED,
     35     EFFECT_STATE_INITIALIZED,
     36     EFFECT_STATE_ACTIVE,
     37 };
     38 
     39 const effect_descriptor_t *descriptors[] = {
     40         &equalizer_descriptor,
     41         &bassboost_descriptor,
     42         &virtualizer_descriptor,
     43         &aux_env_reverb_descriptor,
     44         &ins_env_reverb_descriptor,
     45         &aux_preset_reverb_descriptor,
     46         &ins_preset_reverb_descriptor,
     47         NULL,
     48 };
     49 
     50 pthread_once_t once = PTHREAD_ONCE_INIT;
     51 int init_status;
     52 /*
     53  * list of created effects.
     54  * Updated by offload_effects_bundle_hal_start_output()
     55  * and offload_effects_bundle_hal_stop_output()
     56  */
     57 struct listnode created_effects_list;
     58 /*
     59  * list of active output streams.
     60  * Updated by offload_effects_bundle_hal_start_output()
     61  * and offload_effects_bundle_hal_stop_output()
     62  */
     63 struct listnode active_outputs_list;
     64 /*
     65  * lock must be held when modifying or accessing
     66  * created_effects_list or active_outputs_list
     67  */
     68 pthread_mutex_t lock;
     69 
     70 
     71 /*
     72  *  Local functions
     73  */
     74 static void init_once() {
     75     list_init(&created_effects_list);
     76     list_init(&active_outputs_list);
     77 
     78     pthread_mutex_init(&lock, NULL);
     79 
     80     init_status = 0;
     81 }
     82 
     83 int lib_init()
     84 {
     85     pthread_once(&once, init_once);
     86     return init_status;
     87 }
     88 
     89 bool effect_exists(effect_context_t *context)
     90 {
     91     struct listnode *node;
     92 
     93     list_for_each(node, &created_effects_list) {
     94         effect_context_t *fx_ctxt = node_to_item(node,
     95                                                  effect_context_t,
     96                                                  effects_list_node);
     97         if (fx_ctxt == context) {
     98             return true;
     99         }
    100     }
    101     return false;
    102 }
    103 
    104 output_context_t *get_output(audio_io_handle_t output)
    105 {
    106     struct listnode *node;
    107 
    108     list_for_each(node, &active_outputs_list) {
    109         output_context_t *out_ctxt = node_to_item(node,
    110                                                   output_context_t,
    111                                                   outputs_list_node);
    112         if (out_ctxt->handle == output)
    113             return out_ctxt;
    114     }
    115     return NULL;
    116 }
    117 
    118 void add_effect_to_output(output_context_t * output, effect_context_t *context)
    119 {
    120     struct listnode *fx_node;
    121 
    122     list_for_each(fx_node, &output->effects_list) {
    123         effect_context_t *fx_ctxt = node_to_item(fx_node,
    124                                                  effect_context_t,
    125                                                  output_node);
    126         if (fx_ctxt == context)
    127             return;
    128     }
    129     list_add_tail(&output->effects_list, &context->output_node);
    130     if (context->ops.start)
    131         context->ops.start(context, output);
    132 
    133 }
    134 
    135 void remove_effect_from_output(output_context_t * output,
    136                                effect_context_t *context)
    137 {
    138     struct listnode *fx_node;
    139 
    140     list_for_each(fx_node, &output->effects_list) {
    141         effect_context_t *fx_ctxt = node_to_item(fx_node,
    142                                                  effect_context_t,
    143                                                  output_node);
    144         if (fx_ctxt == context) {
    145             if (context->ops.stop)
    146                 context->ops.stop(context, output);
    147             list_remove(&context->output_node);
    148             return;
    149         }
    150     }
    151 }
    152 
    153 bool effects_enabled()
    154 {
    155     struct listnode *out_node;
    156 
    157     list_for_each(out_node, &active_outputs_list) {
    158         struct listnode *fx_node;
    159         output_context_t *out_ctxt = node_to_item(out_node,
    160                                                   output_context_t,
    161                                                   outputs_list_node);
    162 
    163         list_for_each(fx_node, &out_ctxt->effects_list) {
    164             effect_context_t *fx_ctxt = node_to_item(fx_node,
    165                                                      effect_context_t,
    166                                                      output_node);
    167             if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) &&
    168                 (fx_ctxt->ops.process != NULL))
    169                 return true;
    170         }
    171     }
    172     return false;
    173 }
    174 
    175 
    176 /*
    177  * Interface from audio HAL
    178  */
    179 __attribute__ ((visibility ("default")))
    180 int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id)
    181 {
    182     int ret = 0;
    183     struct listnode *node;
    184     char mixer_string[128];
    185     output_context_t * out_ctxt = NULL;
    186 
    187     ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
    188 
    189     if (lib_init() != 0)
    190         return init_status;
    191 
    192     pthread_mutex_lock(&lock);
    193     if (get_output(output) != NULL) {
    194         ALOGW("%s output already started", __func__);
    195         ret = -ENOSYS;
    196         goto exit;
    197     }
    198 
    199     out_ctxt = (output_context_t *)
    200                                  malloc(sizeof(output_context_t));
    201     out_ctxt->handle = output;
    202     out_ctxt->pcm_device_id = pcm_id;
    203 
    204     /* populate the mixer control to send offload parameters */
    205     snprintf(mixer_string, sizeof(mixer_string),
    206              "%s %d", "Audio Effects Config", out_ctxt->pcm_device_id);
    207     out_ctxt->mixer = mixer_open(MIXER_CARD);
    208     if (!out_ctxt->mixer) {
    209         ALOGE("Failed to open mixer");
    210         out_ctxt->ctl = NULL;
    211         ret = -EINVAL;
    212         free(out_ctxt);
    213         goto exit;
    214     } else {
    215         out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string);
    216         if (!out_ctxt->ctl) {
    217             ALOGE("mixer_get_ctl_by_name failed");
    218             mixer_close(out_ctxt->mixer);
    219             out_ctxt->mixer = NULL;
    220             ret = -EINVAL;
    221             free(out_ctxt);
    222             goto exit;
    223         }
    224     }
    225 
    226     list_init(&out_ctxt->effects_list);
    227 
    228     list_for_each(node, &created_effects_list) {
    229         effect_context_t *fx_ctxt = node_to_item(node,
    230                                                  effect_context_t,
    231                                                  effects_list_node);
    232         if (fx_ctxt->out_handle == output) {
    233             if (fx_ctxt->ops.start)
    234                 fx_ctxt->ops.start(fx_ctxt, out_ctxt);
    235             list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
    236         }
    237     }
    238     list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
    239 exit:
    240     pthread_mutex_unlock(&lock);
    241     return ret;
    242 }
    243 
    244 __attribute__ ((visibility ("default")))
    245 int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id)
    246 {
    247     int ret;
    248     struct listnode *node;
    249     struct listnode *fx_node;
    250     output_context_t *out_ctxt;
    251 
    252     ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
    253 
    254     if (lib_init() != 0)
    255         return init_status;
    256 
    257     pthread_mutex_lock(&lock);
    258 
    259     out_ctxt = get_output(output);
    260     if (out_ctxt == NULL) {
    261         ALOGW("%s output not started", __func__);
    262         ret = -ENOSYS;
    263         goto exit;
    264     }
    265 
    266     if (out_ctxt->mixer)
    267         mixer_close(out_ctxt->mixer);
    268 
    269     list_for_each(fx_node, &out_ctxt->effects_list) {
    270         effect_context_t *fx_ctxt = node_to_item(fx_node,
    271                                                  effect_context_t,
    272                                                  output_node);
    273         if (fx_ctxt->ops.stop)
    274             fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
    275     }
    276 
    277     list_remove(&out_ctxt->outputs_list_node);
    278 
    279     free(out_ctxt);
    280 
    281 exit:
    282     pthread_mutex_unlock(&lock);
    283     return ret;
    284 }
    285 
    286 
    287 /*
    288  * Effect operations
    289  */
    290 int set_config(effect_context_t *context, effect_config_t *config)
    291 {
    292     context->config = *config;
    293 
    294     if (context->ops.reset)
    295         context->ops.reset(context);
    296 
    297     return 0;
    298 }
    299 
    300 void get_config(effect_context_t *context, effect_config_t *config)
    301 {
    302     *config = context->config;
    303 }
    304 
    305 
    306 /*
    307  * Effect Library Interface Implementation
    308  */
    309 int effect_lib_create(const effect_uuid_t *uuid,
    310                          int32_t sessionId,
    311                          int32_t ioId,
    312                          effect_handle_t *pHandle) {
    313     int ret;
    314     int i;
    315 
    316     ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId);
    317     if (lib_init() != 0)
    318         return init_status;
    319 
    320     if (pHandle == NULL || uuid == NULL)
    321         return -EINVAL;
    322 
    323     for (i = 0; descriptors[i] != NULL; i++) {
    324         if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
    325             break;
    326     }
    327 
    328     if (descriptors[i] == NULL)
    329         return -EINVAL;
    330 
    331     effect_context_t *context;
    332     if (memcmp(uuid, &equalizer_descriptor.uuid,
    333         sizeof(effect_uuid_t)) == 0) {
    334         equalizer_context_t *eq_ctxt = (equalizer_context_t *)
    335                                        calloc(1, sizeof(equalizer_context_t));
    336         context = (effect_context_t *)eq_ctxt;
    337         context->ops.init = equalizer_init;
    338         context->ops.reset = equalizer_reset;
    339         context->ops.set_parameter = equalizer_set_parameter;
    340         context->ops.get_parameter = equalizer_get_parameter;
    341         context->ops.set_device = equalizer_set_device;
    342         context->ops.enable = equalizer_enable;
    343         context->ops.disable = equalizer_disable;
    344         context->ops.start = equalizer_start;
    345         context->ops.stop = equalizer_stop;
    346 
    347         context->desc = &equalizer_descriptor;
    348         eq_ctxt->ctl = NULL;
    349     } else if (memcmp(uuid, &bassboost_descriptor.uuid,
    350                sizeof(effect_uuid_t)) == 0) {
    351         bassboost_context_t *bass_ctxt = (bassboost_context_t *)
    352                                          calloc(1, sizeof(bassboost_context_t));
    353         context = (effect_context_t *)bass_ctxt;
    354         context->ops.init = bassboost_init;
    355         context->ops.reset = bassboost_reset;
    356         context->ops.set_parameter = bassboost_set_parameter;
    357         context->ops.get_parameter = bassboost_get_parameter;
    358         context->ops.set_device = bassboost_set_device;
    359         context->ops.enable = bassboost_enable;
    360         context->ops.disable = bassboost_disable;
    361         context->ops.start = bassboost_start;
    362         context->ops.stop = bassboost_stop;
    363 
    364         context->desc = &bassboost_descriptor;
    365         bass_ctxt->ctl = NULL;
    366     } else if (memcmp(uuid, &virtualizer_descriptor.uuid,
    367                sizeof(effect_uuid_t)) == 0) {
    368         virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)
    369                                            calloc(1, sizeof(virtualizer_context_t));
    370         context = (effect_context_t *)virt_ctxt;
    371         context->ops.init = virtualizer_init;
    372         context->ops.reset = virtualizer_reset;
    373         context->ops.set_parameter = virtualizer_set_parameter;
    374         context->ops.get_parameter = virtualizer_get_parameter;
    375         context->ops.set_device = virtualizer_set_device;
    376         context->ops.enable = virtualizer_enable;
    377         context->ops.disable = virtualizer_disable;
    378         context->ops.start = virtualizer_start;
    379         context->ops.stop = virtualizer_stop;
    380 
    381         context->desc = &virtualizer_descriptor;
    382         virt_ctxt->ctl = NULL;
    383     } else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid,
    384                 sizeof(effect_uuid_t)) == 0) ||
    385                (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
    386                 sizeof(effect_uuid_t)) == 0) ||
    387                (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
    388                 sizeof(effect_uuid_t)) == 0) ||
    389                (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
    390                 sizeof(effect_uuid_t)) == 0)) {
    391         reverb_context_t *reverb_ctxt = (reverb_context_t *)
    392                                         calloc(1, sizeof(reverb_context_t));
    393         context = (effect_context_t *)reverb_ctxt;
    394         context->ops.init = reverb_init;
    395         context->ops.reset = reverb_reset;
    396         context->ops.set_parameter = reverb_set_parameter;
    397         context->ops.get_parameter = reverb_get_parameter;
    398         context->ops.set_device = reverb_set_device;
    399         context->ops.enable = reverb_enable;
    400         context->ops.disable = reverb_disable;
    401         context->ops.start = reverb_start;
    402         context->ops.stop = reverb_stop;
    403 
    404         if (memcmp(uuid, &aux_env_reverb_descriptor.uuid,
    405                    sizeof(effect_uuid_t)) == 0) {
    406             context->desc = &aux_env_reverb_descriptor;
    407             reverb_auxiliary_init(reverb_ctxt);
    408         } else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
    409                    sizeof(effect_uuid_t)) == 0) {
    410             context->desc = &ins_env_reverb_descriptor;
    411             reverb_insert_init(reverb_ctxt);
    412         } else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
    413                    sizeof(effect_uuid_t)) == 0) {
    414             context->desc = &aux_preset_reverb_descriptor;
    415             reverb_auxiliary_init(reverb_ctxt);
    416         } else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
    417                    sizeof(effect_uuid_t)) == 0) {
    418             context->desc = &ins_preset_reverb_descriptor;
    419             reverb_preset_init(reverb_ctxt);
    420         }
    421         reverb_ctxt->ctl = NULL;
    422     } else {
    423         return -EINVAL;
    424     }
    425 
    426     context->itfe = &effect_interface;
    427     context->state = EFFECT_STATE_UNINITIALIZED;
    428     context->out_handle = (audio_io_handle_t)ioId;
    429 
    430     ret = context->ops.init(context);
    431     if (ret < 0) {
    432         ALOGW("%s init failed", __func__);
    433         free(context);
    434         return ret;
    435     }
    436 
    437     context->state = EFFECT_STATE_INITIALIZED;
    438 
    439     pthread_mutex_lock(&lock);
    440     list_add_tail(&created_effects_list, &context->effects_list_node);
    441     output_context_t *out_ctxt = get_output(ioId);
    442     if (out_ctxt != NULL)
    443         add_effect_to_output(out_ctxt, context);
    444     pthread_mutex_unlock(&lock);
    445 
    446     *pHandle = (effect_handle_t)context;
    447 
    448     ALOGV("%s created context %p", __func__, context);
    449 
    450     return 0;
    451 
    452 }
    453 
    454 int effect_lib_release(effect_handle_t handle)
    455 {
    456     effect_context_t *context = (effect_context_t *)handle;
    457     int status;
    458 
    459     if (lib_init() != 0)
    460         return init_status;
    461 
    462     ALOGV("%s context %p", __func__, handle);
    463     pthread_mutex_lock(&lock);
    464     status = -EINVAL;
    465     if (effect_exists(context)) {
    466         output_context_t *out_ctxt = get_output(context->out_handle);
    467         if (out_ctxt != NULL)
    468             remove_effect_from_output(out_ctxt, context);
    469         list_remove(&context->effects_list_node);
    470         if (context->ops.release)
    471             context->ops.release(context);
    472         free(context);
    473         status = 0;
    474     }
    475     pthread_mutex_unlock(&lock);
    476 
    477     return status;
    478 }
    479 
    480 int effect_lib_get_descriptor(const effect_uuid_t *uuid,
    481                               effect_descriptor_t *descriptor)
    482 {
    483     int i;
    484 
    485     if (lib_init() != 0)
    486         return init_status;
    487 
    488     if (descriptor == NULL || uuid == NULL) {
    489         ALOGV("%s called with NULL pointer", __func__);
    490         return -EINVAL;
    491     }
    492 
    493     for (i = 0; descriptors[i] != NULL; i++) {
    494         if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
    495             *descriptor = *descriptors[i];
    496             return 0;
    497         }
    498     }
    499 
    500     return  -EINVAL;
    501 }
    502 
    503 
    504 /*
    505  * Effect Control Interface Implementation
    506  */
    507 
    508 /* Stub function for effect interface: never called for offloaded effects */
    509 int effect_process(effect_handle_t self,
    510                        audio_buffer_t *inBuffer __unused,
    511                        audio_buffer_t *outBuffer __unused)
    512 {
    513     effect_context_t * context = (effect_context_t *)self;
    514     int status = 0;
    515 
    516     ALOGW("%s Called ?????", __func__);
    517 
    518     pthread_mutex_lock(&lock);
    519     if (!effect_exists(context)) {
    520         status = -ENOSYS;
    521         goto exit;
    522     }
    523 
    524     if (context->state != EFFECT_STATE_ACTIVE) {
    525         status = -ENODATA;
    526         goto exit;
    527     }
    528 
    529 exit:
    530     pthread_mutex_unlock(&lock);
    531     return status;
    532 }
    533 
    534 int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
    535                    void *pCmdData, uint32_t *replySize, void *pReplyData)
    536 {
    537 
    538     effect_context_t * context = (effect_context_t *)self;
    539     int retsize;
    540     int status = 0;
    541 
    542     pthread_mutex_lock(&lock);
    543 
    544     if (!effect_exists(context)) {
    545         status = -ENOSYS;
    546         goto exit;
    547     }
    548 
    549     if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
    550         status = -ENOSYS;
    551         goto exit;
    552     }
    553 
    554     switch (cmdCode) {
    555     case EFFECT_CMD_INIT:
    556         if (pReplyData == NULL || *replySize != sizeof(int)) {
    557             status = -EINVAL;
    558             goto exit;
    559         }
    560         if (context->ops.init)
    561             *(int *) pReplyData = context->ops.init(context);
    562         else
    563             *(int *) pReplyData = 0;
    564         break;
    565     case EFFECT_CMD_SET_CONFIG:
    566         if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
    567                 || pReplyData == NULL || *replySize != sizeof(int)) {
    568             status = -EINVAL;
    569             goto exit;
    570         }
    571         *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
    572         break;
    573     case EFFECT_CMD_GET_CONFIG:
    574         if (pReplyData == NULL ||
    575             *replySize != sizeof(effect_config_t)) {
    576             status = -EINVAL;
    577             goto exit;
    578         }
    579         if (!context->offload_enabled) {
    580             status = -EINVAL;
    581             goto exit;
    582         }
    583 
    584         get_config(context, (effect_config_t *)pReplyData);
    585         break;
    586     case EFFECT_CMD_RESET:
    587         if (context->ops.reset)
    588             context->ops.reset(context);
    589         break;
    590     case EFFECT_CMD_ENABLE:
    591         if (pReplyData == NULL || *replySize != sizeof(int)) {
    592             status = -EINVAL;
    593             goto exit;
    594         }
    595         if (context->state != EFFECT_STATE_INITIALIZED) {
    596             status = -ENOSYS;
    597             goto exit;
    598         }
    599         context->state = EFFECT_STATE_ACTIVE;
    600         if (context->ops.enable)
    601             context->ops.enable(context);
    602         ALOGV("%s EFFECT_CMD_ENABLE", __func__);
    603         *(int *)pReplyData = 0;
    604         break;
    605     case EFFECT_CMD_DISABLE:
    606         if (pReplyData == NULL || *replySize != sizeof(int)) {
    607             status = -EINVAL;
    608             goto exit;
    609         }
    610         if (context->state != EFFECT_STATE_ACTIVE) {
    611             status = -ENOSYS;
    612             goto exit;
    613         }
    614         context->state = EFFECT_STATE_INITIALIZED;
    615         if (context->ops.disable)
    616             context->ops.disable(context);
    617         ALOGV("%s EFFECT_CMD_DISABLE", __func__);
    618         *(int *)pReplyData = 0;
    619         break;
    620     case EFFECT_CMD_GET_PARAM: {
    621         if (pCmdData == NULL ||
    622             cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
    623             pReplyData == NULL ||
    624             *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
    625                                sizeof(uint16_t))) {
    626             status = -EINVAL;
    627             ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
    628                   cmdSize, *replySize);
    629             goto exit;
    630         }
    631         if (!context->offload_enabled) {
    632             status = -EINVAL;
    633             goto exit;
    634         }
    635         effect_param_t *q = (effect_param_t *)pCmdData;
    636         memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize);
    637         effect_param_t *p = (effect_param_t *)pReplyData;
    638         if (context->ops.get_parameter)
    639             context->ops.get_parameter(context, p, replySize);
    640         } break;
    641     case EFFECT_CMD_SET_PARAM: {
    642         if (pCmdData == NULL ||
    643             cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
    644                             sizeof(uint16_t)) ||
    645             pReplyData == NULL || *replySize != sizeof(int32_t)) {
    646             status = -EINVAL;
    647             ALOGV("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d",
    648                   cmdSize, *replySize);
    649             goto exit;
    650         }
    651         *(int32_t *)pReplyData = 0;
    652         effect_param_t *p = (effect_param_t *)pCmdData;
    653         if (context->ops.set_parameter)
    654             *(int32_t *)pReplyData = context->ops.set_parameter(context, p,
    655                                                                 *replySize);
    656 
    657         } break;
    658     case EFFECT_CMD_SET_DEVICE: {
    659         uint32_t device;
    660         ALOGV("\t EFFECT_CMD_SET_DEVICE start");
    661         if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) {
    662             status = -EINVAL;
    663             ALOGV("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize);
    664             goto exit;
    665         }
    666         device = *(uint32_t *)pCmdData;
    667         if (context->ops.set_device)
    668             context->ops.set_device(context, device);
    669         } break;
    670     case EFFECT_CMD_SET_VOLUME:
    671     case EFFECT_CMD_SET_AUDIO_MODE:
    672         break;
    673 
    674     case EFFECT_CMD_OFFLOAD: {
    675         output_context_t *out_ctxt;
    676 
    677         if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
    678                 || pReplyData == NULL || *replySize != sizeof(int)) {
    679             ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
    680             status = -EINVAL;
    681             break;
    682         }
    683 
    684         effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
    685 
    686         ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__,
    687               offload_param->isOffload, offload_param->ioHandle);
    688 
    689         *(int *)pReplyData = 0;
    690 
    691         context->offload_enabled = offload_param->isOffload;
    692         if (context->out_handle == offload_param->ioHandle)
    693             break;
    694 
    695         out_ctxt = get_output(context->out_handle);
    696         if (out_ctxt != NULL)
    697             remove_effect_from_output(out_ctxt, context);
    698 
    699         context->out_handle = offload_param->ioHandle;
    700         out_ctxt = get_output(context->out_handle);
    701         if (out_ctxt != NULL)
    702             add_effect_to_output(out_ctxt, context);
    703 
    704         } break;
    705 
    706 
    707     default:
    708         if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
    709             status = context->ops.command(context, cmdCode, cmdSize,
    710                                           pCmdData, replySize, pReplyData);
    711         else {
    712             ALOGW("%s invalid command %d", __func__, cmdCode);
    713             status = -EINVAL;
    714         }
    715         break;
    716     }
    717 
    718 exit:
    719     pthread_mutex_unlock(&lock);
    720 
    721     return status;
    722 }
    723 
    724 /* Effect Control Interface Implementation: get_descriptor */
    725 int effect_get_descriptor(effect_handle_t   self,
    726                           effect_descriptor_t *descriptor)
    727 {
    728     effect_context_t *context = (effect_context_t *)self;
    729 
    730     if (!effect_exists(context) || (descriptor == NULL))
    731         return -EINVAL;
    732 
    733     *descriptor = *context->desc;
    734 
    735     return 0;
    736 }
    737 
    738 bool effect_is_active(effect_context_t * ctxt) {
    739     return ctxt->state == EFFECT_STATE_ACTIVE;
    740 }
    741 
    742 /* effect_handle_t interface implementation for offload effects */
    743 const struct effect_interface_s effect_interface = {
    744     effect_process,
    745     effect_command,
    746     effect_get_descriptor,
    747     NULL,
    748 };
    749 
    750 __attribute__ ((visibility ("default")))
    751 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
    752     tag : AUDIO_EFFECT_LIBRARY_TAG,
    753     version : EFFECT_LIBRARY_API_VERSION,
    754     name : "Offload Effects Bundle Library",
    755     implementor : "The Android Open Source Project",
    756     create_effect : effect_lib_create,
    757     release_effect : effect_lib_release,
    758     get_descriptor : effect_lib_get_descriptor,
    759 };
    760