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