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