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