Home | History | Annotate | Download | only in visualizer
      1 /*
      2  * Copyright (C) 2013 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_visualizer"
     18 /*#define LOG_NDEBUG 0*/
     19 #include <assert.h>
     20 #include <math.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <time.h>
     24 #include <sys/prctl.h>
     25 #include <dlfcn.h>
     26 
     27 #include <cutils/list.h>
     28 #include <cutils/log.h>
     29 #include <system/thread_defs.h>
     30 #include <tinyalsa/asoundlib.h>
     31 #include <audio_effects/effect_visualizer.h>
     32 
     33 #define LIB_ACDB_LOADER "libacdbloader.so"
     34 #define ACDB_DEV_TYPE_OUT 1
     35 #define AFE_PROXY_ACDB_ID 45
     36 
     37 static void* acdb_handle;
     38 
     39 typedef void (*acdb_send_audio_cal_t)(int, int);
     40 
     41 acdb_send_audio_cal_t acdb_send_audio_cal;
     42 
     43 enum {
     44     EFFECT_STATE_UNINITIALIZED,
     45     EFFECT_STATE_INITIALIZED,
     46     EFFECT_STATE_ACTIVE,
     47 };
     48 
     49 typedef struct effect_context_s effect_context_t;
     50 typedef struct output_context_s output_context_t;
     51 
     52 /* effect specific operations. Only the init() and process() operations must be defined.
     53  * Others are optional.
     54  */
     55 typedef struct effect_ops_s {
     56     int (*init)(effect_context_t *context);
     57     int (*release)(effect_context_t *context);
     58     int (*reset)(effect_context_t *context);
     59     int (*enable)(effect_context_t *context);
     60     int (*disable)(effect_context_t *context);
     61     int (*start)(effect_context_t *context, output_context_t *output);
     62     int (*stop)(effect_context_t *context, output_context_t *output);
     63     int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
     64     int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
     65     int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
     66     int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
     67             void *pCmdData, uint32_t *replySize, void *pReplyData);
     68 } effect_ops_t;
     69 
     70 struct effect_context_s {
     71     const struct effect_interface_s *itfe;
     72     struct listnode effects_list_node;  /* node in created_effects_list */
     73     struct listnode output_node;  /* node in output_context_t.effects_list */
     74     effect_config_t config;
     75     const effect_descriptor_t *desc;
     76     audio_io_handle_t out_handle;  /* io handle of the output the effect is attached to */
     77     uint32_t state;
     78     bool offload_enabled;  /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command.
     79                               Otherwise non offloaded visualizer has already processed the command
     80                               and we must not overwrite the reply. */
     81     effect_ops_t ops;
     82 };
     83 
     84 typedef struct output_context_s {
     85     struct listnode outputs_list_node;  /* node in active_outputs_list */
     86     audio_io_handle_t handle; /* io handle */
     87     struct listnode effects_list; /* list of effects attached to this output */
     88 } output_context_t;
     89 
     90 
     91 /* maximum time since last capture buffer update before resetting capture buffer. This means
     92   that the framework has stopped playing audio and we must start returning silence */
     93 #define MAX_STALL_TIME_MS 1000
     94 
     95 #define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
     96 
     97 #define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
     98 
     99 /* maximum number of buffers for which we keep track of the measurements */
    100 #define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
    101 
    102 typedef struct buffer_stats_s {
    103     bool is_valid;
    104     uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
    105     float rms_squared; /* the average square of the samples in a buffer */
    106 } buffer_stats_t;
    107 
    108 typedef struct visualizer_context_s {
    109     effect_context_t common;
    110 
    111     uint32_t capture_idx;
    112     uint32_t capture_size;
    113     uint32_t scaling_mode;
    114     uint32_t last_capture_idx;
    115     uint32_t latency;
    116     struct timespec buffer_update_time;
    117     uint8_t capture_buf[CAPTURE_BUF_SIZE];
    118     /* for measurements */
    119     uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
    120     uint32_t meas_mode;
    121     uint8_t meas_wndw_size_in_buffers;
    122     uint8_t meas_buffer_idx;
    123     buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
    124 } visualizer_context_t;
    125 
    126 
    127 extern const struct effect_interface_s effect_interface;
    128 
    129 /* Offload visualizer UUID: 7a8044a0-1a71-11e3-a184-0002a5d5c51b */
    130 const effect_descriptor_t visualizer_descriptor = {
    131         {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
    132         {0x7a8044a0, 0x1a71, 0x11e3, 0xa184, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
    133         EFFECT_CONTROL_API_VERSION,
    134         (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL ),
    135         0, /* TODO */
    136         1,
    137         "QCOM MSM offload visualizer",
    138         "The Android Open Source Project",
    139 };
    140 
    141 const effect_descriptor_t *descriptors[] = {
    142         &visualizer_descriptor,
    143         NULL,
    144 };
    145 
    146 
    147 pthread_once_t once = PTHREAD_ONCE_INIT;
    148 int init_status;
    149 
    150 /* list of created effects. Updated by visualizer_hal_start_output()
    151  * and visualizer_hal_stop_output() */
    152 struct listnode created_effects_list;
    153 /* list of active output streams. Updated by visualizer_hal_start_output()
    154  * and visualizer_hal_stop_output() */
    155 struct listnode active_outputs_list;
    156 
    157 /* thread capturing PCM from Proxy port and calling the process function on each enabled effect
    158  * attached to an active output stream */
    159 pthread_t capture_thread;
    160 /* lock must be held when modifying or accessing created_effects_list or active_outputs_list */
    161 pthread_mutex_t lock;
    162 /* thread_lock must be held when starting or stopping the capture thread.
    163  * Locking order: thread_lock -> lock */
    164 pthread_mutex_t thread_lock;
    165 /* cond is signaled when an output is started or stopped or an effect is enabled or disable: the
    166  * capture thread will reevaluate the capture and effect rocess conditions. */
    167 pthread_cond_t cond;
    168 /* true when requesting the capture thread to exit */
    169 bool exit_thread;
    170 /* 0 if the capture thread was created successfully */
    171 int thread_status;
    172 
    173 
    174 #define DSP_OUTPUT_LATENCY_MS 0 /* Fudge factor for latency after capture point in audio DSP */
    175 
    176 /* Retry for delay for mixer open */
    177 #define RETRY_NUMBER 10
    178 #define RETRY_US 500000
    179 
    180 #define MIXER_CARD 0
    181 #define SOUND_CARD 0
    182 #define CAPTURE_DEVICE 8
    183 
    184 /* Proxy port supports only MMAP read and those fixed parameters*/
    185 #define AUDIO_CAPTURE_CHANNEL_COUNT 2
    186 #define AUDIO_CAPTURE_SMP_RATE 48000
    187 #define AUDIO_CAPTURE_PERIOD_SIZE (768)
    188 #define AUDIO_CAPTURE_PERIOD_COUNT 32
    189 
    190 struct pcm_config pcm_config_capture = {
    191     .channels = AUDIO_CAPTURE_CHANNEL_COUNT,
    192     .rate = AUDIO_CAPTURE_SMP_RATE,
    193     .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
    194     .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
    195     .format = PCM_FORMAT_S16_LE,
    196     .start_threshold = AUDIO_CAPTURE_PERIOD_SIZE / 4,
    197     .stop_threshold = INT_MAX,
    198     .avail_min = AUDIO_CAPTURE_PERIOD_SIZE / 4,
    199 };
    200 
    201 
    202 /*
    203  *  Local functions
    204  */
    205 
    206 static void init_once() {
    207     list_init(&created_effects_list);
    208     list_init(&active_outputs_list);
    209 
    210     pthread_mutex_init(&lock, NULL);
    211     pthread_mutex_init(&thread_lock, NULL);
    212     pthread_cond_init(&cond, NULL);
    213     exit_thread = false;
    214     thread_status = -1;
    215 
    216     init_status = 0;
    217 }
    218 
    219 int lib_init() {
    220     pthread_once(&once, init_once);
    221     return init_status;
    222 }
    223 
    224 bool effect_exists(effect_context_t *context) {
    225     struct listnode *node;
    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 == context) {
    232             return true;
    233         }
    234     }
    235     return false;
    236 }
    237 
    238 output_context_t *get_output(audio_io_handle_t output) {
    239     struct listnode *node;
    240 
    241     list_for_each(node, &active_outputs_list) {
    242         output_context_t *out_ctxt = node_to_item(node,
    243                                                   output_context_t,
    244                                                   outputs_list_node);
    245         if (out_ctxt->handle == output) {
    246             return out_ctxt;
    247         }
    248     }
    249     return NULL;
    250 }
    251 
    252 void add_effect_to_output(output_context_t * output, effect_context_t *context) {
    253     struct listnode *fx_node;
    254 
    255     list_for_each(fx_node, &output->effects_list) {
    256         effect_context_t *fx_ctxt = node_to_item(fx_node,
    257                                                      effect_context_t,
    258                                                      output_node);
    259         if (fx_ctxt == context)
    260             return;
    261     }
    262     list_add_tail(&output->effects_list, &context->output_node);
    263     if (context->ops.start)
    264         context->ops.start(context, output);
    265 }
    266 
    267 void remove_effect_from_output(output_context_t * output, effect_context_t *context) {
    268     struct listnode *fx_node;
    269 
    270     list_for_each(fx_node, &output->effects_list) {
    271         effect_context_t *fx_ctxt = node_to_item(fx_node,
    272                                                      effect_context_t,
    273                                                      output_node);
    274         if (fx_ctxt == context) {
    275             if (context->ops.stop)
    276                 context->ops.stop(context, output);
    277             list_remove(&context->output_node);
    278             return;
    279         }
    280     }
    281 }
    282 
    283 bool effects_enabled() {
    284     struct listnode *out_node;
    285 
    286     list_for_each(out_node, &active_outputs_list) {
    287         struct listnode *fx_node;
    288         output_context_t *out_ctxt = node_to_item(out_node,
    289                                                   output_context_t,
    290                                                   outputs_list_node);
    291 
    292         list_for_each(fx_node, &out_ctxt->effects_list) {
    293             effect_context_t *fx_ctxt = node_to_item(fx_node,
    294                                                          effect_context_t,
    295                                                          output_node);
    296             if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL)
    297                 return true;
    298         }
    299     }
    300     return false;
    301 }
    302 
    303 int configure_proxy_capture(struct mixer *mixer, int value) {
    304     const char *proxy_ctl_name = "AFE_PCM_RX Audio Mixer MultiMedia4";
    305     struct mixer_ctl *ctl;
    306 
    307     if (value && acdb_send_audio_cal)
    308         acdb_send_audio_cal(AFE_PROXY_ACDB_ID, ACDB_DEV_TYPE_OUT);
    309 
    310     ctl = mixer_get_ctl_by_name(mixer, proxy_ctl_name);
    311     if (ctl == NULL) {
    312         ALOGW("%s: could not get %s ctl", __func__, proxy_ctl_name);
    313         return -EINVAL;
    314     }
    315     if (mixer_ctl_set_value(ctl, 0, value) != 0)
    316         ALOGW("%s: error setting value %d on %s ", __func__, value, proxy_ctl_name);
    317 
    318     return 0;
    319 }
    320 
    321 
    322 void *capture_thread_loop(void *arg __unused)
    323 {
    324     int16_t data[AUDIO_CAPTURE_PERIOD_SIZE * AUDIO_CAPTURE_CHANNEL_COUNT * sizeof(int16_t)];
    325     audio_buffer_t buf;
    326     buf.frameCount = AUDIO_CAPTURE_PERIOD_SIZE;
    327     buf.s16 = data;
    328     bool capture_enabled = false;
    329     struct mixer *mixer;
    330     struct pcm *pcm = NULL;
    331     int ret;
    332     int retry_num = 0;
    333 
    334     ALOGD("thread enter");
    335 
    336     prctl(PR_SET_NAME, (unsigned long)"visualizer capture", 0, 0, 0);
    337 
    338     pthread_mutex_lock(&lock);
    339 
    340     mixer = mixer_open(MIXER_CARD);
    341     while (mixer == NULL && retry_num < RETRY_NUMBER) {
    342         usleep(RETRY_US);
    343         mixer = mixer_open(MIXER_CARD);
    344         retry_num++;
    345     }
    346     if (mixer == NULL) {
    347         pthread_mutex_unlock(&lock);
    348         return NULL;
    349     }
    350 
    351     for (;;) {
    352         if (exit_thread) {
    353             break;
    354         }
    355         if (effects_enabled()) {
    356             if (!capture_enabled) {
    357                 ret = configure_proxy_capture(mixer, 1);
    358                 if (ret == 0) {
    359                     pcm = pcm_open(SOUND_CARD, CAPTURE_DEVICE,
    360                                    PCM_IN|PCM_MMAP|PCM_NOIRQ, &pcm_config_capture);
    361                     if (pcm && !pcm_is_ready(pcm)) {
    362                         ALOGW("%s: %s", __func__, pcm_get_error(pcm));
    363                         pcm_close(pcm);
    364                         pcm = NULL;
    365                         configure_proxy_capture(mixer, 0);
    366                     } else {
    367                         capture_enabled = true;
    368                         ALOGD("%s: capture ENABLED", __func__);
    369                     }
    370                 }
    371             }
    372         } else {
    373             if (capture_enabled) {
    374                 if (pcm != NULL)
    375                     pcm_close(pcm);
    376                 configure_proxy_capture(mixer, 0);
    377                 ALOGD("%s: capture DISABLED", __func__);
    378                 capture_enabled = false;
    379             }
    380             pthread_cond_wait(&cond, &lock);
    381         }
    382         if (!capture_enabled)
    383             continue;
    384 
    385         pthread_mutex_unlock(&lock);
    386         ret = pcm_mmap_read(pcm, data, sizeof(data));
    387         pthread_mutex_lock(&lock);
    388 
    389         if (ret == 0) {
    390             struct listnode *out_node;
    391 
    392             list_for_each(out_node, &active_outputs_list) {
    393                 output_context_t *out_ctxt = node_to_item(out_node,
    394                                                           output_context_t,
    395                                                           outputs_list_node);
    396                 struct listnode *fx_node;
    397 
    398                 list_for_each(fx_node, &out_ctxt->effects_list) {
    399                     effect_context_t *fx_ctxt = node_to_item(fx_node,
    400                                                                 effect_context_t,
    401                                                                 output_node);
    402                     if (fx_ctxt->ops.process != NULL)
    403                         fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
    404                 }
    405             }
    406         } else {
    407             ALOGW("%s: read status %d %s", __func__, ret, pcm_get_error(pcm));
    408         }
    409     }
    410 
    411     if (capture_enabled) {
    412         if (pcm != NULL)
    413             pcm_close(pcm);
    414         configure_proxy_capture(mixer, 0);
    415     }
    416     mixer_close(mixer);
    417     pthread_mutex_unlock(&lock);
    418 
    419     ALOGD("thread exit");
    420 
    421     return NULL;
    422 }
    423 
    424 /*
    425  * Interface from audio HAL
    426  */
    427 
    428 __attribute__ ((visibility ("default")))
    429 int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) {
    430     int ret;
    431     struct listnode *node;
    432 
    433     ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
    434 
    435     if (lib_init() != 0)
    436         return init_status;
    437 
    438     pthread_mutex_lock(&thread_lock);
    439     pthread_mutex_lock(&lock);
    440     if (get_output(output) != NULL) {
    441         ALOGW("%s output already started", __func__);
    442         ret = -ENOSYS;
    443         goto exit;
    444     }
    445 
    446     output_context_t *out_ctxt = (output_context_t *)malloc(sizeof(output_context_t));
    447     out_ctxt->handle = output;
    448     list_init(&out_ctxt->effects_list);
    449 
    450     list_for_each(node, &created_effects_list) {
    451         effect_context_t *fx_ctxt = node_to_item(node,
    452                                                      effect_context_t,
    453                                                      effects_list_node);
    454         if (fx_ctxt->out_handle == output) {
    455             if (fx_ctxt->ops.start)
    456                 fx_ctxt->ops.start(fx_ctxt, out_ctxt);
    457             list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
    458         }
    459     }
    460     if (list_empty(&active_outputs_list)) {
    461         exit_thread = false;
    462         thread_status = pthread_create(&capture_thread, (const pthread_attr_t *) NULL,
    463                         capture_thread_loop, NULL);
    464     }
    465     list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
    466     pthread_cond_signal(&cond);
    467 
    468 exit:
    469     pthread_mutex_unlock(&lock);
    470     pthread_mutex_unlock(&thread_lock);
    471     return ret;
    472 }
    473 
    474 __attribute__ ((visibility ("default")))
    475 int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
    476     int ret;
    477     struct listnode *node;
    478     struct listnode *fx_node;
    479     output_context_t *out_ctxt;
    480 
    481     ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
    482 
    483     if (lib_init() != 0)
    484         return init_status;
    485 
    486     pthread_mutex_lock(&thread_lock);
    487     pthread_mutex_lock(&lock);
    488 
    489     out_ctxt = get_output(output);
    490     if (out_ctxt == NULL) {
    491         ALOGW("%s output not started", __func__);
    492         ret = -ENOSYS;
    493         goto exit;
    494     }
    495     list_for_each(fx_node, &out_ctxt->effects_list) {
    496         effect_context_t *fx_ctxt = node_to_item(fx_node,
    497                                                  effect_context_t,
    498                                                  output_node);
    499         if (fx_ctxt->ops.stop)
    500             fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
    501     }
    502     list_remove(&out_ctxt->outputs_list_node);
    503     pthread_cond_signal(&cond);
    504 
    505     if (list_empty(&active_outputs_list)) {
    506         if (thread_status == 0) {
    507             exit_thread = true;
    508             pthread_cond_signal(&cond);
    509             pthread_mutex_unlock(&lock);
    510             pthread_join(capture_thread, (void **) NULL);
    511             pthread_mutex_lock(&lock);
    512             thread_status = -1;
    513         }
    514     }
    515 
    516     free(out_ctxt);
    517 
    518 exit:
    519     pthread_mutex_unlock(&lock);
    520     pthread_mutex_unlock(&thread_lock);
    521     return ret;
    522 }
    523 
    524 
    525 /*
    526  * Effect operations
    527  */
    528 
    529 int set_config(effect_context_t *context, effect_config_t *config)
    530 {
    531     if (config->inputCfg.samplingRate != config->outputCfg.samplingRate) return -EINVAL;
    532     if (config->inputCfg.channels != config->outputCfg.channels) return -EINVAL;
    533     if (config->inputCfg.format != config->outputCfg.format) return -EINVAL;
    534     if (config->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
    535     if (config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
    536             config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
    537     if (config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
    538 
    539     context->config = *config;
    540 
    541     if (context->ops.reset)
    542         context->ops.reset(context);
    543 
    544     return 0;
    545 }
    546 
    547 void get_config(effect_context_t *context, effect_config_t *config)
    548 {
    549     *config = context->config;
    550 }
    551 
    552 
    553 /*
    554  * Visualizer operations
    555  */
    556 
    557 uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) {
    558     uint32_t delta_ms = 0;
    559     if (visu_ctxt->buffer_update_time.tv_sec != 0) {
    560         struct timespec ts;
    561         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
    562             time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
    563             long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
    564             if (nsec < 0) {
    565                 --secs;
    566                 nsec += 1000000000;
    567             }
    568             delta_ms = secs * 1000 + nsec / 1000000;
    569         }
    570     }
    571     return delta_ms;
    572 }
    573 
    574 int visualizer_reset(effect_context_t *context)
    575 {
    576     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
    577 
    578     visu_ctxt->capture_idx = 0;
    579     visu_ctxt->last_capture_idx = 0;
    580     visu_ctxt->buffer_update_time.tv_sec = 0;
    581     visu_ctxt->latency = DSP_OUTPUT_LATENCY_MS;
    582     memset(visu_ctxt->capture_buf, 0x80, CAPTURE_BUF_SIZE);
    583     return 0;
    584 }
    585 
    586 int visualizer_init(effect_context_t *context)
    587 {
    588     int32_t i;
    589 
    590     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
    591 
    592     context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
    593     context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    594     context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    595     context->config.inputCfg.samplingRate = 44100;
    596     context->config.inputCfg.bufferProvider.getBuffer = NULL;
    597     context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
    598     context->config.inputCfg.bufferProvider.cookie = NULL;
    599     context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
    600     context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
    601     context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    602     context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    603     context->config.outputCfg.samplingRate = 44100;
    604     context->config.outputCfg.bufferProvider.getBuffer = NULL;
    605     context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
    606     context->config.outputCfg.bufferProvider.cookie = NULL;
    607     context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
    608 
    609     visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX;
    610     visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
    611 
    612     // measurement initialization
    613     visu_ctxt->channel_count = audio_channel_count_from_out_mask(context->config.inputCfg.channels);
    614     visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
    615     visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
    616     visu_ctxt->meas_buffer_idx = 0;
    617     for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
    618         visu_ctxt->past_meas[i].is_valid = false;
    619         visu_ctxt->past_meas[i].peak_u16 = 0;
    620         visu_ctxt->past_meas[i].rms_squared = 0;
    621     }
    622 
    623     set_config(context, &context->config);
    624 
    625     if (acdb_handle == NULL) {
    626         acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
    627         if (acdb_handle == NULL) {
    628             ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER);
    629         } else {
    630             acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(acdb_handle,
    631                                                     "acdb_loader_send_audio_cal");
    632             if (!acdb_send_audio_cal)
    633                 ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
    634                       __func__, LIB_ACDB_LOADER);
    635             }
    636     }
    637 
    638     return 0;
    639 }
    640 
    641 int visualizer_get_parameter(effect_context_t *context, effect_param_t *p, uint32_t *size)
    642 {
    643     visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
    644 
    645     p->status = 0;
    646     *size = sizeof(effect_param_t) + sizeof(uint32_t);
    647     if (p->psize != sizeof(uint32_t)) {
    648         p->status = -EINVAL;
    649         return 0;
    650     }
    651     switch (*(uint32_t *)p->data) {
    652     case VISUALIZER_PARAM_CAPTURE_SIZE:
    653         ALOGV("%s get capture_size = %d", __func__, visu_ctxt->capture_size);
    654         *((uint32_t *)p->data + 1) = visu_ctxt->capture_size;
    655         p->vsize = sizeof(uint32_t);
    656         *size += sizeof(uint32_t);
    657         break;
    658     case VISUALIZER_PARAM_SCALING_MODE:
    659         ALOGV("%s get scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
    660         *((uint32_t *)p->data + 1) = visu_ctxt->scaling_mode;
    661         p->vsize = sizeof(uint32_t);
    662         *size += sizeof(uint32_t);
    663         break;
    664     case VISUALIZER_PARAM_MEASUREMENT_MODE:
    665         ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode);
    666         *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode;
    667         p->vsize = sizeof(uint32_t);
    668         *size += sizeof(uint32_t);
    669         break;
    670     default:
    671         p->status = -EINVAL;
    672     }
    673     return 0;
    674 }
    675 
    676 int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size __unused)
    677 {
    678     visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
    679 
    680     if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t))
    681         return -EINVAL;
    682 
    683     switch (*(uint32_t *)p->data) {
    684     case VISUALIZER_PARAM_CAPTURE_SIZE:
    685         visu_ctxt->capture_size = *((uint32_t *)p->data + 1);
    686         ALOGV("%s set capture_size = %d", __func__, visu_ctxt->capture_size);
    687         break;
    688     case VISUALIZER_PARAM_SCALING_MODE:
    689         visu_ctxt->scaling_mode = *((uint32_t *)p->data + 1);
    690         ALOGV("%s set scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
    691         break;
    692     case VISUALIZER_PARAM_LATENCY:
    693         /* Ignore latency as we capture at DSP output
    694          * visu_ctxt->latency = *((uint32_t *)p->data + 1); */
    695         ALOGV("%s set latency = %d", __func__, visu_ctxt->latency);
    696         break;
    697     case VISUALIZER_PARAM_MEASUREMENT_MODE:
    698         visu_ctxt->meas_mode = *((uint32_t *)p->data + 1);
    699         ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode);
    700         break;
    701     default:
    702         return -EINVAL;
    703     }
    704     return 0;
    705 }
    706 
    707 /* Real process function called from capture thread. Called with lock held */
    708 int visualizer_process(effect_context_t *context,
    709                        audio_buffer_t *inBuffer,
    710                        audio_buffer_t *outBuffer)
    711 {
    712     visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
    713 
    714     if (!effect_exists(context))
    715         return -EINVAL;
    716 
    717     if (inBuffer == NULL || inBuffer->raw == NULL ||
    718         outBuffer == NULL || outBuffer->raw == NULL ||
    719         inBuffer->frameCount != outBuffer->frameCount ||
    720         inBuffer->frameCount == 0) {
    721         return -EINVAL;
    722     }
    723 
    724     // perform measurements if needed
    725     if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) {
    726         // find the peak and RMS squared for the new buffer
    727         uint32_t inIdx;
    728         int16_t max_sample = 0;
    729         float rms_squared_acc = 0;
    730         for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) {
    731             if (inBuffer->s16[inIdx] > max_sample) {
    732                 max_sample = inBuffer->s16[inIdx];
    733             } else if (-inBuffer->s16[inIdx] > max_sample) {
    734                 max_sample = -inBuffer->s16[inIdx];
    735             }
    736             rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
    737         }
    738         // store the measurement
    739         visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample;
    740         visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared =
    741                 rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count);
    742         visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true;
    743         if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) {
    744             visu_ctxt->meas_buffer_idx = 0;
    745         }
    746     }
    747 
    748     /* all code below assumes stereo 16 bit PCM output and input */
    749     int32_t shift;
    750 
    751     if (visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_NORMALIZED) {
    752         /* derive capture scaling factor from peak value in current buffer
    753          * this gives more interesting captures for display. */
    754         shift = 32;
    755         int len = inBuffer->frameCount * 2;
    756         int i;
    757         for (i = 0; i < len; i++) {
    758             int32_t smp = inBuffer->s16[i];
    759             if (smp < 0) smp = -smp - 1; /* take care to keep the max negative in range */
    760             int32_t clz = __builtin_clz(smp);
    761             if (shift > clz) shift = clz;
    762         }
    763         /* A maximum amplitude signal will have 17 leading zeros, which we want to
    764          * translate to a shift of 8 (for converting 16 bit to 8 bit) */
    765         shift = 25 - shift;
    766         /* Never scale by less than 8 to avoid returning unaltered PCM signal. */
    767         if (shift < 3) {
    768             shift = 3;
    769         }
    770         /* add one to combine the division by 2 needed after summing
    771          * left and right channels below */
    772         shift++;
    773     } else {
    774         assert(visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_AS_PLAYED);
    775         shift = 9;
    776     }
    777 
    778     uint32_t capt_idx;
    779     uint32_t in_idx;
    780     uint8_t *buf = visu_ctxt->capture_buf;
    781     for (in_idx = 0, capt_idx = visu_ctxt->capture_idx;
    782          in_idx < inBuffer->frameCount;
    783          in_idx++, capt_idx++) {
    784         if (capt_idx >= CAPTURE_BUF_SIZE) {
    785             /* wrap around */
    786             capt_idx = 0;
    787         }
    788         int32_t smp = inBuffer->s16[2 * in_idx] + inBuffer->s16[2 * in_idx + 1];
    789         smp = smp >> shift;
    790         buf[capt_idx] = ((uint8_t)smp)^0x80;
    791     }
    792 
    793     /* XXX the following two should really be atomic, though it probably doesn't
    794      * matter much for visualization purposes */
    795     visu_ctxt->capture_idx = capt_idx;
    796     /* update last buffer update time stamp */
    797     if (clock_gettime(CLOCK_MONOTONIC, &visu_ctxt->buffer_update_time) < 0) {
    798         visu_ctxt->buffer_update_time.tv_sec = 0;
    799     }
    800 
    801     if (context->state != EFFECT_STATE_ACTIVE) {
    802         ALOGV("%s DONE inactive", __func__);
    803         return -ENODATA;
    804     }
    805 
    806     return 0;
    807 }
    808 
    809 int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize __unused,
    810         void *pCmdData __unused, uint32_t *replySize, void *pReplyData)
    811 {
    812     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
    813 
    814     switch (cmdCode) {
    815     case VISUALIZER_CMD_CAPTURE:
    816         if (pReplyData == NULL || *replySize != visu_ctxt->capture_size) {
    817             ALOGV("%s VISUALIZER_CMD_CAPTURE error *replySize %d context->capture_size %d",
    818                   __func__, *replySize, visu_ctxt->capture_size);
    819             return -EINVAL;
    820         }
    821 
    822         if (!context->offload_enabled)
    823             break;
    824 
    825         if (context->state == EFFECT_STATE_ACTIVE) {
    826             int32_t latency_ms = visu_ctxt->latency;
    827             const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
    828             latency_ms -= delta_ms;
    829             if (latency_ms < 0) {
    830                 latency_ms = 0;
    831             }
    832             const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
    833 
    834             int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
    835             int32_t capture_size = visu_ctxt->capture_size;
    836             if (capture_point < 0) {
    837                 int32_t size = -capture_point;
    838                 if (size > capture_size)
    839                     size = capture_size;
    840 
    841                 memcpy(pReplyData,
    842                        visu_ctxt->capture_buf + CAPTURE_BUF_SIZE + capture_point,
    843                        size);
    844                 pReplyData = (void *)((size_t)pReplyData + size);
    845                 capture_size -= size;
    846                 capture_point = 0;
    847             }
    848             memcpy(pReplyData,
    849                    visu_ctxt->capture_buf + capture_point,
    850                    capture_size);
    851 
    852 
    853             /* if audio framework has stopped playing audio although the effect is still
    854              * active we must clear the capture buffer to return silence */
    855             if ((visu_ctxt->last_capture_idx == visu_ctxt->capture_idx) &&
    856                     (visu_ctxt->buffer_update_time.tv_sec != 0)) {
    857                 if (delta_ms > MAX_STALL_TIME_MS) {
    858                     ALOGV("%s capture going to idle", __func__);
    859                     visu_ctxt->buffer_update_time.tv_sec = 0;
    860                     memset(pReplyData, 0x80, visu_ctxt->capture_size);
    861                 }
    862             }
    863             visu_ctxt->last_capture_idx = visu_ctxt->capture_idx;
    864         } else {
    865             memset(pReplyData, 0x80, visu_ctxt->capture_size);
    866         }
    867         break;
    868 
    869     case VISUALIZER_CMD_MEASURE: {
    870         uint16_t peak_u16 = 0;
    871         float sum_rms_squared = 0.0f;
    872         uint8_t nb_valid_meas = 0;
    873         /* reset measurements if last measurement was too long ago (which implies stored
    874          * measurements aren't relevant anymore and shouldn't bias the new one) */
    875         const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
    876         if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) {
    877             uint32_t i;
    878             ALOGV("Discarding measurements, last measurement is %dms old", delay_ms);
    879             for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
    880                 visu_ctxt->past_meas[i].is_valid = false;
    881                 visu_ctxt->past_meas[i].peak_u16 = 0;
    882                 visu_ctxt->past_meas[i].rms_squared = 0;
    883             }
    884             visu_ctxt->meas_buffer_idx = 0;
    885         } else {
    886             /* only use actual measurements, otherwise the first RMS measure happening before
    887              * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
    888              * low */
    889             uint32_t i;
    890             for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) {
    891                 if (visu_ctxt->past_meas[i].is_valid) {
    892                     if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) {
    893                         peak_u16 = visu_ctxt->past_meas[i].peak_u16;
    894                     }
    895                     sum_rms_squared += visu_ctxt->past_meas[i].rms_squared;
    896                     nb_valid_meas++;
    897                 }
    898             }
    899         }
    900         float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas);
    901         int32_t* p_int_reply_data = (int32_t*)pReplyData;
    902         /* convert from I16 sample values to mB and write results */
    903         if (rms < 0.000016f) {
    904             p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB
    905         } else {
    906             p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
    907         }
    908         if (peak_u16 == 0) {
    909             p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
    910         } else {
    911             p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f));
    912         }
    913         ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
    914                 peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK],
    915                 rms, p_int_reply_data[MEASUREMENT_IDX_RMS]);
    916         }
    917         break;
    918 
    919     default:
    920         ALOGW("%s invalid command %d", __func__, cmdCode);
    921         return -EINVAL;
    922     }
    923     return 0;
    924 }
    925 
    926 
    927 /*
    928  * Effect Library Interface Implementation
    929  */
    930 
    931 int effect_lib_create(const effect_uuid_t *uuid,
    932                          int32_t sessionId __unused,
    933                          int32_t ioId,
    934                          effect_handle_t *pHandle) {
    935     int ret;
    936     int i;
    937 
    938     if (lib_init() != 0)
    939         return init_status;
    940 
    941     if (pHandle == NULL || uuid == NULL)
    942         return -EINVAL;
    943 
    944     for (i = 0; descriptors[i] != NULL; i++) {
    945         if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
    946             break;
    947     }
    948 
    949     if (descriptors[i] == NULL)
    950         return -EINVAL;
    951 
    952     effect_context_t *context;
    953     if (memcmp(uuid, &visualizer_descriptor.uuid, sizeof(effect_uuid_t)) == 0) {
    954         visualizer_context_t *visu_ctxt = (visualizer_context_t *)calloc(1,
    955                                                                      sizeof(visualizer_context_t));
    956         context = (effect_context_t *)visu_ctxt;
    957         context->ops.init = visualizer_init;
    958         context->ops.reset = visualizer_reset;
    959         context->ops.process = visualizer_process;
    960         context->ops.set_parameter = visualizer_set_parameter;
    961         context->ops.get_parameter = visualizer_get_parameter;
    962         context->ops.command = visualizer_command;
    963         context->desc = &visualizer_descriptor;
    964     } else {
    965         return -EINVAL;
    966     }
    967 
    968     context->itfe = &effect_interface;
    969     context->state = EFFECT_STATE_UNINITIALIZED;
    970     context->out_handle = (audio_io_handle_t)ioId;
    971 
    972     ret = context->ops.init(context);
    973     if (ret < 0) {
    974         ALOGW("%s init failed", __func__);
    975         free(context);
    976         return ret;
    977     }
    978 
    979     context->state = EFFECT_STATE_INITIALIZED;
    980 
    981     pthread_mutex_lock(&lock);
    982     list_add_tail(&created_effects_list, &context->effects_list_node);
    983     output_context_t *out_ctxt = get_output(ioId);
    984     if (out_ctxt != NULL)
    985         add_effect_to_output(out_ctxt, context);
    986     pthread_mutex_unlock(&lock);
    987 
    988     *pHandle = (effect_handle_t)context;
    989 
    990     ALOGV("%s created context %p", __func__, context);
    991 
    992     return 0;
    993 
    994 }
    995 
    996 int effect_lib_release(effect_handle_t handle) {
    997     effect_context_t *context = (effect_context_t *)handle;
    998     int status;
    999 
   1000     if (lib_init() != 0)
   1001         return init_status;
   1002 
   1003     ALOGV("%s context %p", __func__, handle);
   1004     pthread_mutex_lock(&lock);
   1005     status = -EINVAL;
   1006     if (effect_exists(context)) {
   1007         output_context_t *out_ctxt = get_output(context->out_handle);
   1008         if (out_ctxt != NULL)
   1009             remove_effect_from_output(out_ctxt, context);
   1010         list_remove(&context->effects_list_node);
   1011         if (context->ops.release)
   1012             context->ops.release(context);
   1013         free(context);
   1014         status = 0;
   1015     }
   1016     pthread_mutex_unlock(&lock);
   1017 
   1018     return status;
   1019 }
   1020 
   1021 int effect_lib_get_descriptor(const effect_uuid_t *uuid,
   1022                                 effect_descriptor_t *descriptor) {
   1023     int i;
   1024 
   1025     if (lib_init() != 0)
   1026         return init_status;
   1027 
   1028     if (descriptor == NULL || uuid == NULL) {
   1029         ALOGV("%s called with NULL pointer", __func__);
   1030         return -EINVAL;
   1031     }
   1032 
   1033     for (i = 0; descriptors[i] != NULL; i++) {
   1034         if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
   1035             *descriptor = *descriptors[i];
   1036             return 0;
   1037         }
   1038     }
   1039 
   1040     return  -EINVAL;
   1041 }
   1042 
   1043 /*
   1044  * Effect Control Interface Implementation
   1045  */
   1046 
   1047  /* Stub function for effect interface: never called for offloaded effects */
   1048 int effect_process(effect_handle_t self,
   1049                        audio_buffer_t *inBuffer __unused,
   1050                        audio_buffer_t *outBuffer __unused)
   1051 {
   1052     effect_context_t * context = (effect_context_t *)self;
   1053     int status = 0;
   1054 
   1055     ALOGW("%s Called ?????", __func__);
   1056 
   1057     pthread_mutex_lock(&lock);
   1058     if (!effect_exists(context)) {
   1059         status = -EINVAL;
   1060         goto exit;
   1061     }
   1062 
   1063     if (context->state != EFFECT_STATE_ACTIVE) {
   1064         status = -EINVAL;
   1065         goto exit;
   1066     }
   1067 
   1068 exit:
   1069     pthread_mutex_unlock(&lock);
   1070     return status;
   1071 }
   1072 
   1073 int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
   1074         void *pCmdData, uint32_t *replySize, void *pReplyData)
   1075 {
   1076 
   1077     effect_context_t * context = (effect_context_t *)self;
   1078     int retsize;
   1079     int status = 0;
   1080 
   1081     pthread_mutex_lock(&lock);
   1082 
   1083     if (!effect_exists(context)) {
   1084         status = -EINVAL;
   1085         goto exit;
   1086     }
   1087 
   1088     if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
   1089         status = -EINVAL;
   1090         goto exit;
   1091     }
   1092 
   1093 //    ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,
   1094 //             "%s command %d cmdSize %d", __func__, cmdCode, cmdSize);
   1095 
   1096     switch (cmdCode) {
   1097     case EFFECT_CMD_INIT:
   1098         if (pReplyData == NULL || *replySize != sizeof(int)) {
   1099             status = -EINVAL;
   1100             goto exit;
   1101         }
   1102         if (context->ops.init)
   1103             *(int *) pReplyData = context->ops.init(context);
   1104         else
   1105             *(int *) pReplyData = 0;
   1106         break;
   1107     case EFFECT_CMD_SET_CONFIG:
   1108         if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
   1109                 || pReplyData == NULL || *replySize != sizeof(int)) {
   1110             status = -EINVAL;
   1111             goto exit;
   1112         }
   1113         *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
   1114         break;
   1115     case EFFECT_CMD_GET_CONFIG:
   1116         if (pReplyData == NULL ||
   1117             *replySize != sizeof(effect_config_t)) {
   1118             status = -EINVAL;
   1119             goto exit;
   1120         }
   1121         if (!context->offload_enabled) {
   1122             status = -EINVAL;
   1123             goto exit;
   1124         }
   1125 
   1126         get_config(context, (effect_config_t *)pReplyData);
   1127         break;
   1128     case EFFECT_CMD_RESET:
   1129         if (context->ops.reset)
   1130             context->ops.reset(context);
   1131         break;
   1132     case EFFECT_CMD_ENABLE:
   1133         if (pReplyData == NULL || *replySize != sizeof(int)) {
   1134             status = -EINVAL;
   1135             goto exit;
   1136         }
   1137         if (context->state != EFFECT_STATE_INITIALIZED) {
   1138             status = -ENOSYS;
   1139             goto exit;
   1140         }
   1141         context->state = EFFECT_STATE_ACTIVE;
   1142         if (context->ops.enable)
   1143             context->ops.enable(context);
   1144         pthread_cond_signal(&cond);
   1145         ALOGV("%s EFFECT_CMD_ENABLE", __func__);
   1146         *(int *)pReplyData = 0;
   1147         break;
   1148     case EFFECT_CMD_DISABLE:
   1149         if (pReplyData == NULL || *replySize != sizeof(int)) {
   1150             status = -EINVAL;
   1151             goto exit;
   1152         }
   1153         if (context->state != EFFECT_STATE_ACTIVE) {
   1154             status = -ENOSYS;
   1155             goto exit;
   1156         }
   1157         context->state = EFFECT_STATE_INITIALIZED;
   1158         if (context->ops.disable)
   1159             context->ops.disable(context);
   1160         pthread_cond_signal(&cond);
   1161         ALOGV("%s EFFECT_CMD_DISABLE", __func__);
   1162         *(int *)pReplyData = 0;
   1163         break;
   1164     case EFFECT_CMD_GET_PARAM: {
   1165         if (pCmdData == NULL ||
   1166             cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
   1167             pReplyData == NULL ||
   1168             *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
   1169             status = -EINVAL;
   1170             goto exit;
   1171         }
   1172         if (!context->offload_enabled) {
   1173             status = -EINVAL;
   1174             goto exit;
   1175         }
   1176         memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
   1177         effect_param_t *p = (effect_param_t *)pReplyData;
   1178         if (context->ops.get_parameter)
   1179             context->ops.get_parameter(context, p, replySize);
   1180         } break;
   1181     case EFFECT_CMD_SET_PARAM: {
   1182         if (pCmdData == NULL ||
   1183             cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
   1184             pReplyData == NULL || *replySize != sizeof(int32_t)) {
   1185             status = -EINVAL;
   1186             goto exit;
   1187         }
   1188         *(int32_t *)pReplyData = 0;
   1189         effect_param_t *p = (effect_param_t *)pCmdData;
   1190         if (context->ops.set_parameter)
   1191             *(int32_t *)pReplyData = context->ops.set_parameter(context, p, *replySize);
   1192 
   1193         } break;
   1194     case EFFECT_CMD_SET_DEVICE:
   1195     case EFFECT_CMD_SET_VOLUME:
   1196     case EFFECT_CMD_SET_AUDIO_MODE:
   1197         break;
   1198 
   1199     case EFFECT_CMD_OFFLOAD: {
   1200         output_context_t *out_ctxt;
   1201 
   1202         if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
   1203                 || pReplyData == NULL || *replySize != sizeof(int)) {
   1204             ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
   1205             status = -EINVAL;
   1206             break;
   1207         }
   1208 
   1209         effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
   1210 
   1211         ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d",
   1212               __func__, offload_param->isOffload, offload_param->ioHandle);
   1213 
   1214         *(int *)pReplyData = 0;
   1215 
   1216         context->offload_enabled = offload_param->isOffload;
   1217         if (context->out_handle == offload_param->ioHandle)
   1218             break;
   1219 
   1220         out_ctxt = get_output(context->out_handle);
   1221         if (out_ctxt != NULL)
   1222             remove_effect_from_output(out_ctxt, context);
   1223 
   1224         context->out_handle = offload_param->ioHandle;
   1225         out_ctxt = get_output(offload_param->ioHandle);
   1226         if (out_ctxt != NULL)
   1227             add_effect_to_output(out_ctxt, context);
   1228 
   1229         } break;
   1230 
   1231 
   1232     default:
   1233         if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
   1234             status = context->ops.command(context, cmdCode, cmdSize,
   1235                                           pCmdData, replySize, pReplyData);
   1236         else {
   1237             ALOGW("%s invalid command %d", __func__, cmdCode);
   1238             status = -EINVAL;
   1239         }
   1240         break;
   1241     }
   1242 
   1243 exit:
   1244     pthread_mutex_unlock(&lock);
   1245 
   1246 //    ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,"%s DONE", __func__);
   1247     return status;
   1248 }
   1249 
   1250 /* Effect Control Interface Implementation: get_descriptor */
   1251 int effect_get_descriptor(effect_handle_t   self,
   1252                                     effect_descriptor_t *descriptor)
   1253 {
   1254     effect_context_t *context = (effect_context_t *)self;
   1255 
   1256     if (!effect_exists(context))
   1257         return -EINVAL;
   1258 
   1259     if (descriptor == NULL)
   1260         return -EINVAL;
   1261 
   1262     *descriptor = *context->desc;
   1263 
   1264     return 0;
   1265 }
   1266 
   1267 /* effect_handle_t interface implementation for visualizer effect */
   1268 const struct effect_interface_s effect_interface = {
   1269         effect_process,
   1270         effect_command,
   1271         effect_get_descriptor,
   1272         NULL,
   1273 };
   1274 
   1275 __attribute__ ((visibility ("default")))
   1276 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
   1277     .tag = AUDIO_EFFECT_LIBRARY_TAG,
   1278     .version = EFFECT_LIBRARY_API_VERSION,
   1279     .name = "Visualizer Library",
   1280     .implementor = "The Android Open Source Project",
   1281     .create_effect = effect_lib_create,
   1282     .release_effect = effect_lib_release,
   1283     .get_descriptor = effect_lib_get_descriptor,
   1284 };
   1285