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