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