1 /* 2 * Copyright (C) 2014 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_effect_bundle" 18 //#define LOG_NDEBUG 0 19 20 #include <pthread.h> 21 #include <stdlib.h> 22 23 #include <cutils/list.h> 24 #include <cutils/log.h> 25 #include <system/thread_defs.h> 26 #include <tinyalsa/asoundlib.h> 27 #include <hardware/audio_effect.h> 28 29 #include "bundle.h" 30 #include "equalizer.h" 31 #include "bass_boost.h" 32 #include "virtualizer.h" 33 #include "reverb.h" 34 35 enum { 36 EFFECT_STATE_UNINITIALIZED, 37 EFFECT_STATE_INITIALIZED, 38 EFFECT_STATE_ACTIVE, 39 }; 40 41 const effect_descriptor_t *descriptors[] = { 42 &equalizer_descriptor, 43 &bassboost_descriptor, 44 &virtualizer_descriptor, 45 &aux_env_reverb_descriptor, 46 &ins_env_reverb_descriptor, 47 &aux_preset_reverb_descriptor, 48 &ins_preset_reverb_descriptor, 49 NULL, 50 }; 51 52 pthread_once_t once = PTHREAD_ONCE_INIT; 53 int init_status; 54 /* 55 * list of created effects. 56 * Updated by offload_effects_bundle_hal_start_output() 57 * and offload_effects_bundle_hal_stop_output() 58 */ 59 struct listnode created_effects_list; 60 /* 61 * list of active output streams. 62 * Updated by offload_effects_bundle_hal_start_output() 63 * and offload_effects_bundle_hal_stop_output() 64 */ 65 struct listnode active_outputs_list; 66 /* 67 * lock must be held when modifying or accessing 68 * created_effects_list or active_outputs_list 69 */ 70 pthread_mutex_t lock; 71 72 73 /* 74 * Local functions 75 */ 76 static void init_once() { 77 list_init(&created_effects_list); 78 list_init(&active_outputs_list); 79 80 pthread_mutex_init(&lock, NULL); 81 82 init_status = 0; 83 } 84 85 int lib_init() 86 { 87 pthread_once(&once, init_once); 88 return init_status; 89 } 90 91 bool effect_exists(effect_context_t *context) 92 { 93 struct listnode *node; 94 95 list_for_each(node, &created_effects_list) { 96 effect_context_t *fx_ctxt = node_to_item(node, 97 effect_context_t, 98 effects_list_node); 99 if (fx_ctxt == context) { 100 return true; 101 } 102 } 103 return false; 104 } 105 106 output_context_t *get_output(audio_io_handle_t output) 107 { 108 struct listnode *node; 109 110 list_for_each(node, &active_outputs_list) { 111 output_context_t *out_ctxt = node_to_item(node, 112 output_context_t, 113 outputs_list_node); 114 if (out_ctxt->handle == output) 115 return out_ctxt; 116 } 117 return NULL; 118 } 119 120 void add_effect_to_output(output_context_t * output, effect_context_t *context) 121 { 122 struct listnode *fx_node; 123 124 list_for_each(fx_node, &output->effects_list) { 125 effect_context_t *fx_ctxt = node_to_item(fx_node, 126 effect_context_t, 127 output_node); 128 if (fx_ctxt == context) 129 return; 130 } 131 list_add_tail(&output->effects_list, &context->output_node); 132 if (context->ops.start) 133 context->ops.start(context, output); 134 135 } 136 137 void remove_effect_from_output(output_context_t * output, 138 effect_context_t *context) 139 { 140 struct listnode *fx_node; 141 142 list_for_each(fx_node, &output->effects_list) { 143 effect_context_t *fx_ctxt = node_to_item(fx_node, 144 effect_context_t, 145 output_node); 146 if (fx_ctxt == context) { 147 if (context->ops.stop) 148 context->ops.stop(context, output); 149 list_remove(&context->output_node); 150 return; 151 } 152 } 153 } 154 155 bool effects_enabled() 156 { 157 struct listnode *out_node; 158 159 list_for_each(out_node, &active_outputs_list) { 160 struct listnode *fx_node; 161 output_context_t *out_ctxt = node_to_item(out_node, 162 output_context_t, 163 outputs_list_node); 164 165 list_for_each(fx_node, &out_ctxt->effects_list) { 166 effect_context_t *fx_ctxt = node_to_item(fx_node, 167 effect_context_t, 168 output_node); 169 if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) && 170 (fx_ctxt->ops.process != NULL)) 171 return true; 172 } 173 } 174 return false; 175 } 176 177 178 /* 179 * Interface from audio HAL 180 */ 181 __attribute__ ((visibility ("default"))) 182 int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id) 183 { 184 int ret = 0; 185 struct listnode *node; 186 char mixer_string[128]; 187 output_context_t * out_ctxt = NULL; 188 189 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); 190 191 if (lib_init() != 0) 192 return init_status; 193 194 pthread_mutex_lock(&lock); 195 if (get_output(output) != NULL) { 196 ALOGW("%s output already started", __func__); 197 ret = -ENOSYS; 198 goto exit; 199 } 200 201 out_ctxt = (output_context_t *) 202 malloc(sizeof(output_context_t)); 203 out_ctxt->handle = output; 204 out_ctxt->pcm_device_id = pcm_id; 205 206 /* populate the mixer control to send offload parameters */ 207 snprintf(mixer_string, sizeof(mixer_string), 208 "%s %d", "Audio Effects Config", out_ctxt->pcm_device_id); 209 out_ctxt->mixer = mixer_open(MIXER_CARD); 210 if (!out_ctxt->mixer) { 211 ALOGE("Failed to open mixer"); 212 out_ctxt->ctl = NULL; 213 ret = -EINVAL; 214 free(out_ctxt); 215 goto exit; 216 } else { 217 out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string); 218 if (!out_ctxt->ctl) { 219 ALOGE("mixer_get_ctl_by_name failed"); 220 mixer_close(out_ctxt->mixer); 221 out_ctxt->mixer = NULL; 222 ret = -EINVAL; 223 free(out_ctxt); 224 goto exit; 225 } 226 } 227 228 list_init(&out_ctxt->effects_list); 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->out_handle == output) { 235 if (fx_ctxt->ops.start) 236 fx_ctxt->ops.start(fx_ctxt, out_ctxt); 237 list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node); 238 } 239 } 240 list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node); 241 exit: 242 pthread_mutex_unlock(&lock); 243 return ret; 244 } 245 246 __attribute__ ((visibility ("default"))) 247 int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id) 248 { 249 int ret = 0; 250 struct listnode *node; 251 struct listnode *fx_node; 252 output_context_t *out_ctxt; 253 254 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); 255 256 if (lib_init() != 0) 257 return init_status; 258 259 pthread_mutex_lock(&lock); 260 261 out_ctxt = get_output(output); 262 if (out_ctxt == NULL) { 263 ALOGW("%s output not started", __func__); 264 ret = -ENOSYS; 265 goto exit; 266 } 267 268 if (out_ctxt->mixer) 269 mixer_close(out_ctxt->mixer); 270 271 list_for_each(fx_node, &out_ctxt->effects_list) { 272 effect_context_t *fx_ctxt = node_to_item(fx_node, 273 effect_context_t, 274 output_node); 275 if (fx_ctxt->ops.stop) 276 fx_ctxt->ops.stop(fx_ctxt, out_ctxt); 277 } 278 279 list_remove(&out_ctxt->outputs_list_node); 280 281 free(out_ctxt); 282 283 exit: 284 pthread_mutex_unlock(&lock); 285 return ret; 286 } 287 288 289 /* 290 * Effect operations 291 */ 292 int set_config(effect_context_t *context, effect_config_t *config) 293 { 294 context->config = *config; 295 296 if (context->ops.reset) 297 context->ops.reset(context); 298 299 return 0; 300 } 301 302 void get_config(effect_context_t *context, effect_config_t *config) 303 { 304 *config = context->config; 305 } 306 307 308 /* 309 * Effect Library Interface Implementation 310 */ 311 int effect_lib_create(const effect_uuid_t *uuid, 312 int32_t sessionId, 313 int32_t ioId, 314 effect_handle_t *pHandle) { 315 int ret; 316 int i; 317 318 ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId); 319 if (lib_init() != 0) 320 return init_status; 321 322 if (pHandle == NULL || uuid == NULL) 323 return -EINVAL; 324 325 for (i = 0; descriptors[i] != NULL; i++) { 326 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) 327 break; 328 } 329 330 if (descriptors[i] == NULL) 331 return -EINVAL; 332 333 effect_context_t *context; 334 if (memcmp(uuid, &equalizer_descriptor.uuid, 335 sizeof(effect_uuid_t)) == 0) { 336 equalizer_context_t *eq_ctxt = (equalizer_context_t *) 337 calloc(1, sizeof(equalizer_context_t)); 338 context = (effect_context_t *)eq_ctxt; 339 context->ops.init = equalizer_init; 340 context->ops.reset = equalizer_reset; 341 context->ops.set_parameter = equalizer_set_parameter; 342 context->ops.get_parameter = equalizer_get_parameter; 343 context->ops.set_device = equalizer_set_device; 344 context->ops.enable = equalizer_enable; 345 context->ops.disable = equalizer_disable; 346 context->ops.start = equalizer_start; 347 context->ops.stop = equalizer_stop; 348 349 context->desc = &equalizer_descriptor; 350 eq_ctxt->ctl = NULL; 351 } else if (memcmp(uuid, &bassboost_descriptor.uuid, 352 sizeof(effect_uuid_t)) == 0) { 353 bassboost_context_t *bass_ctxt = (bassboost_context_t *) 354 calloc(1, sizeof(bassboost_context_t)); 355 context = (effect_context_t *)bass_ctxt; 356 context->ops.init = bassboost_init; 357 context->ops.reset = bassboost_reset; 358 context->ops.set_parameter = bassboost_set_parameter; 359 context->ops.get_parameter = bassboost_get_parameter; 360 context->ops.set_device = bassboost_set_device; 361 context->ops.enable = bassboost_enable; 362 context->ops.disable = bassboost_disable; 363 context->ops.start = bassboost_start; 364 context->ops.stop = bassboost_stop; 365 366 context->desc = &bassboost_descriptor; 367 bass_ctxt->ctl = NULL; 368 } else if (memcmp(uuid, &virtualizer_descriptor.uuid, 369 sizeof(effect_uuid_t)) == 0) { 370 virtualizer_context_t *virt_ctxt = (virtualizer_context_t *) 371 calloc(1, sizeof(virtualizer_context_t)); 372 context = (effect_context_t *)virt_ctxt; 373 context->ops.init = virtualizer_init; 374 context->ops.reset = virtualizer_reset; 375 context->ops.set_parameter = virtualizer_set_parameter; 376 context->ops.get_parameter = virtualizer_get_parameter; 377 context->ops.set_device = virtualizer_set_device; 378 context->ops.enable = virtualizer_enable; 379 context->ops.disable = virtualizer_disable; 380 context->ops.start = virtualizer_start; 381 context->ops.stop = virtualizer_stop; 382 383 context->desc = &virtualizer_descriptor; 384 virt_ctxt->ctl = NULL; 385 } else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid, 386 sizeof(effect_uuid_t)) == 0) || 387 (memcmp(uuid, &ins_env_reverb_descriptor.uuid, 388 sizeof(effect_uuid_t)) == 0) || 389 (memcmp(uuid, &aux_preset_reverb_descriptor.uuid, 390 sizeof(effect_uuid_t)) == 0) || 391 (memcmp(uuid, &ins_preset_reverb_descriptor.uuid, 392 sizeof(effect_uuid_t)) == 0)) { 393 reverb_context_t *reverb_ctxt = (reverb_context_t *) 394 calloc(1, sizeof(reverb_context_t)); 395 context = (effect_context_t *)reverb_ctxt; 396 context->ops.init = reverb_init; 397 context->ops.reset = reverb_reset; 398 context->ops.set_parameter = reverb_set_parameter; 399 context->ops.get_parameter = reverb_get_parameter; 400 context->ops.set_device = reverb_set_device; 401 context->ops.enable = reverb_enable; 402 context->ops.disable = reverb_disable; 403 context->ops.start = reverb_start; 404 context->ops.stop = reverb_stop; 405 406 if (memcmp(uuid, &aux_env_reverb_descriptor.uuid, 407 sizeof(effect_uuid_t)) == 0) { 408 context->desc = &aux_env_reverb_descriptor; 409 reverb_auxiliary_init(reverb_ctxt); 410 } else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid, 411 sizeof(effect_uuid_t)) == 0) { 412 context->desc = &ins_env_reverb_descriptor; 413 reverb_insert_init(reverb_ctxt); 414 } else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid, 415 sizeof(effect_uuid_t)) == 0) { 416 context->desc = &aux_preset_reverb_descriptor; 417 reverb_auxiliary_init(reverb_ctxt); 418 } else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid, 419 sizeof(effect_uuid_t)) == 0) { 420 context->desc = &ins_preset_reverb_descriptor; 421 reverb_preset_init(reverb_ctxt); 422 } 423 reverb_ctxt->ctl = NULL; 424 } else { 425 return -EINVAL; 426 } 427 428 context->itfe = &effect_interface; 429 context->state = EFFECT_STATE_UNINITIALIZED; 430 context->out_handle = (audio_io_handle_t)ioId; 431 432 ret = context->ops.init(context); 433 if (ret < 0) { 434 ALOGW("%s init failed", __func__); 435 free(context); 436 return ret; 437 } 438 439 context->state = EFFECT_STATE_INITIALIZED; 440 441 pthread_mutex_lock(&lock); 442 list_add_tail(&created_effects_list, &context->effects_list_node); 443 output_context_t *out_ctxt = get_output(ioId); 444 if (out_ctxt != NULL) 445 add_effect_to_output(out_ctxt, context); 446 pthread_mutex_unlock(&lock); 447 448 *pHandle = (effect_handle_t)context; 449 450 ALOGV("%s created context %p", __func__, context); 451 452 return 0; 453 454 } 455 456 int effect_lib_release(effect_handle_t handle) 457 { 458 effect_context_t *context = (effect_context_t *)handle; 459 int status; 460 461 if (lib_init() != 0) 462 return init_status; 463 464 ALOGV("%s context %p", __func__, handle); 465 pthread_mutex_lock(&lock); 466 status = -EINVAL; 467 if (effect_exists(context)) { 468 output_context_t *out_ctxt = get_output(context->out_handle); 469 if (out_ctxt != NULL) 470 remove_effect_from_output(out_ctxt, context); 471 list_remove(&context->effects_list_node); 472 if (context->ops.release) 473 context->ops.release(context); 474 free(context); 475 status = 0; 476 } 477 pthread_mutex_unlock(&lock); 478 479 return status; 480 } 481 482 int effect_lib_get_descriptor(const effect_uuid_t *uuid, 483 effect_descriptor_t *descriptor) 484 { 485 int i; 486 487 if (lib_init() != 0) 488 return init_status; 489 490 if (descriptor == NULL || uuid == NULL) { 491 ALOGV("%s called with NULL pointer", __func__); 492 return -EINVAL; 493 } 494 495 for (i = 0; descriptors[i] != NULL; i++) { 496 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { 497 *descriptor = *descriptors[i]; 498 return 0; 499 } 500 } 501 502 return -EINVAL; 503 } 504 505 506 /* 507 * Effect Control Interface Implementation 508 */ 509 510 /* Stub function for effect interface: never called for offloaded effects */ 511 int effect_process(effect_handle_t self, 512 audio_buffer_t *inBuffer __unused, 513 audio_buffer_t *outBuffer __unused) 514 { 515 effect_context_t * context = (effect_context_t *)self; 516 int status = 0; 517 518 ALOGW("%s Called ?????", __func__); 519 520 pthread_mutex_lock(&lock); 521 if (!effect_exists(context)) { 522 status = -ENOSYS; 523 goto exit; 524 } 525 526 if (context->state != EFFECT_STATE_ACTIVE) { 527 status = -ENODATA; 528 goto exit; 529 } 530 531 exit: 532 pthread_mutex_unlock(&lock); 533 return status; 534 } 535 536 int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 537 void *pCmdData, uint32_t *replySize, void *pReplyData) 538 { 539 540 effect_context_t * context = (effect_context_t *)self; 541 int retsize; 542 int status = 0; 543 544 pthread_mutex_lock(&lock); 545 546 if (!effect_exists(context)) { 547 status = -ENOSYS; 548 goto exit; 549 } 550 551 if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) { 552 status = -ENOSYS; 553 goto exit; 554 } 555 556 switch (cmdCode) { 557 case EFFECT_CMD_INIT: 558 if (pReplyData == NULL || *replySize != sizeof(int)) { 559 status = -EINVAL; 560 goto exit; 561 } 562 if (context->ops.init) 563 *(int *) pReplyData = context->ops.init(context); 564 else 565 *(int *) pReplyData = 0; 566 break; 567 case EFFECT_CMD_SET_CONFIG: 568 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 569 || pReplyData == NULL || *replySize != sizeof(int)) { 570 status = -EINVAL; 571 goto exit; 572 } 573 *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData); 574 break; 575 case EFFECT_CMD_GET_CONFIG: 576 if (pReplyData == NULL || 577 *replySize != sizeof(effect_config_t)) { 578 status = -EINVAL; 579 goto exit; 580 } 581 if (!context->offload_enabled) { 582 status = -EINVAL; 583 goto exit; 584 } 585 586 get_config(context, (effect_config_t *)pReplyData); 587 break; 588 case EFFECT_CMD_RESET: 589 if (context->ops.reset) 590 context->ops.reset(context); 591 break; 592 case EFFECT_CMD_ENABLE: 593 if (pReplyData == NULL || *replySize != sizeof(int)) { 594 status = -EINVAL; 595 goto exit; 596 } 597 if (context->state != EFFECT_STATE_INITIALIZED) { 598 status = -ENOSYS; 599 goto exit; 600 } 601 context->state = EFFECT_STATE_ACTIVE; 602 if (context->ops.enable) 603 context->ops.enable(context); 604 ALOGV("%s EFFECT_CMD_ENABLE", __func__); 605 *(int *)pReplyData = 0; 606 break; 607 case EFFECT_CMD_DISABLE: 608 if (pReplyData == NULL || *replySize != sizeof(int)) { 609 status = -EINVAL; 610 goto exit; 611 } 612 if (context->state != EFFECT_STATE_ACTIVE) { 613 status = -ENOSYS; 614 goto exit; 615 } 616 context->state = EFFECT_STATE_INITIALIZED; 617 if (context->ops.disable) 618 context->ops.disable(context); 619 ALOGV("%s EFFECT_CMD_DISABLE", __func__); 620 *(int *)pReplyData = 0; 621 break; 622 case EFFECT_CMD_GET_PARAM: { 623 if (pCmdData == NULL || 624 cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 625 pReplyData == NULL || 626 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint16_t)) || 627 // constrain memcpy below 628 ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) { 629 status = -EINVAL; 630 ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d", 631 cmdSize, *replySize); 632 goto exit; 633 } 634 if (!context->offload_enabled) { 635 status = -EINVAL; 636 goto exit; 637 } 638 effect_param_t *q = (effect_param_t *)pCmdData; 639 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize); 640 effect_param_t *p = (effect_param_t *)pReplyData; 641 if (context->ops.get_parameter) 642 context->ops.get_parameter(context, p, replySize); 643 } break; 644 case EFFECT_CMD_SET_PARAM: { 645 if (pCmdData == NULL || 646 cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + 647 sizeof(uint16_t)) || 648 pReplyData == NULL || *replySize != sizeof(int32_t)) { 649 status = -EINVAL; 650 ALOGV("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d", 651 cmdSize, *replySize); 652 goto exit; 653 } 654 *(int32_t *)pReplyData = 0; 655 effect_param_t *p = (effect_param_t *)pCmdData; 656 if (context->ops.set_parameter) 657 *(int32_t *)pReplyData = context->ops.set_parameter(context, p, 658 *replySize); 659 660 } break; 661 case EFFECT_CMD_SET_DEVICE: { 662 uint32_t device; 663 ALOGV("\t EFFECT_CMD_SET_DEVICE start"); 664 if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) { 665 status = -EINVAL; 666 ALOGV("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize); 667 goto exit; 668 } 669 device = *(uint32_t *)pCmdData; 670 if (context->ops.set_device) 671 context->ops.set_device(context, device); 672 } break; 673 case EFFECT_CMD_SET_VOLUME: { 674 // if pReplyData is NULL, VOL_CTRL is delegated to another effect 675 if (pReplyData == NULL) { 676 break; 677 } 678 if (pCmdData == NULL || cmdSize != 2 * sizeof(uint32_t) || 679 replySize == NULL || *replySize < 2*sizeof(int32_t)) { 680 return -EINVAL; 681 } 682 memcpy(pReplyData, pCmdData, sizeof(int32_t)*2); 683 } break; 684 case EFFECT_CMD_SET_AUDIO_MODE: 685 break; 686 case EFFECT_CMD_OFFLOAD: { 687 output_context_t *out_ctxt; 688 689 if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL 690 || pReplyData == NULL || *replySize != sizeof(int)) { 691 ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__); 692 status = -EINVAL; 693 break; 694 } 695 696 effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData; 697 698 ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__, 699 offload_param->isOffload, offload_param->ioHandle); 700 701 *(int *)pReplyData = 0; 702 703 context->offload_enabled = offload_param->isOffload; 704 if (context->out_handle == offload_param->ioHandle) 705 break; 706 707 out_ctxt = get_output(context->out_handle); 708 if (out_ctxt != NULL) 709 remove_effect_from_output(out_ctxt, context); 710 711 context->out_handle = offload_param->ioHandle; 712 out_ctxt = get_output(context->out_handle); 713 if (out_ctxt != NULL) 714 add_effect_to_output(out_ctxt, context); 715 716 } break; 717 default: 718 if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command) 719 status = context->ops.command(context, cmdCode, cmdSize, 720 pCmdData, replySize, pReplyData); 721 else { 722 ALOGW("%s invalid command %d", __func__, cmdCode); 723 status = -EINVAL; 724 } 725 break; 726 } 727 728 exit: 729 pthread_mutex_unlock(&lock); 730 731 return status; 732 } 733 734 /* Effect Control Interface Implementation: get_descriptor */ 735 int effect_get_descriptor(effect_handle_t self, 736 effect_descriptor_t *descriptor) 737 { 738 effect_context_t *context = (effect_context_t *)self; 739 740 if (!effect_exists(context) || (descriptor == NULL)) 741 return -EINVAL; 742 743 *descriptor = *context->desc; 744 745 return 0; 746 } 747 748 bool effect_is_active(effect_context_t * ctxt) { 749 return ctxt->state == EFFECT_STATE_ACTIVE; 750 } 751 752 /* effect_handle_t interface implementation for offload effects */ 753 const struct effect_interface_s effect_interface = { 754 effect_process, 755 effect_command, 756 effect_get_descriptor, 757 NULL, 758 }; 759 760 __attribute__ ((visibility ("default"))) 761 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 762 .tag = AUDIO_EFFECT_LIBRARY_TAG, 763 .version = EFFECT_LIBRARY_API_VERSION, 764 .name = "Offload Effects Bundle Library", 765 .implementor = "The Android Open Source Project", 766 .create_effect = effect_lib_create, 767 .release_effect = effect_lib_release, 768 .get_descriptor = effect_lib_get_descriptor, 769 }; 770