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