AudioPolicyEffects.cpp revision 84332aaa807037baca05340875f2d94fcc519ac4
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 "AudioPolicyEffects" 18//#define LOG_NDEBUG 0 19 20#include <stdlib.h> 21#include <stdio.h> 22#include <string.h> 23#include <cutils/misc.h> 24#include <media/AudioEffect.h> 25#include <system/audio.h> 26#include <hardware/audio_effect.h> 27#include <audio_effects/audio_effects_conf.h> 28#include <utils/Vector.h> 29#include <utils/SortedVector.h> 30#include <cutils/config_utils.h> 31#include "AudioPolicyEffects.h" 32#include "ServiceUtilities.h" 33 34namespace android { 35 36// ---------------------------------------------------------------------------- 37// AudioPolicyEffects Implementation 38// ---------------------------------------------------------------------------- 39 40AudioPolicyEffects::AudioPolicyEffects() 41{ 42 // load automatic audio effect modules 43 if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { 44 loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE); 45 } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { 46 loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); 47 } 48} 49 50 51AudioPolicyEffects::~AudioPolicyEffects() 52{ 53 size_t i = 0; 54 // release audio input processing resources 55 for (i = 0; i < mInputSources.size(); i++) { 56 delete mInputSources.valueAt(i); 57 } 58 mInputSources.clear(); 59 60 for (i = 0; i < mInputs.size(); i++) { 61 mInputs.valueAt(i)->mEffects.clear(); 62 delete mInputs.valueAt(i); 63 } 64 mInputs.clear(); 65 66 // release audio output processing resources 67 for (i = 0; i < mOutputStreams.size(); i++) { 68 delete mOutputStreams.valueAt(i); 69 } 70 mOutputStreams.clear(); 71 72 for (i = 0; i < mOutputSessions.size(); i++) { 73 mOutputSessions.valueAt(i)->mEffects.clear(); 74 delete mOutputSessions.valueAt(i); 75 } 76 mOutputSessions.clear(); 77} 78 79 80status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input, 81 audio_source_t inputSource, 82 int audioSession) 83{ 84 status_t status = NO_ERROR; 85 86 // create audio pre processors according to input source 87 audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? 88 AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; 89 90 Mutex::Autolock _l(mLock); 91 ssize_t index = mInputSources.indexOfKey(aliasSource); 92 if (index < 0) { 93 ALOGV("addInputEffects(): no processing needs to be attached to this source"); 94 return status; 95 } 96 ssize_t idx = mInputs.indexOfKey(input); 97 EffectVector *inputDesc; 98 if (idx < 0) { 99 inputDesc = new EffectVector(audioSession); 100 mInputs.add(input, inputDesc); 101 } else { 102 // EffectVector is existing and we just need to increase ref count 103 inputDesc = mInputs.valueAt(idx); 104 } 105 inputDesc->mRefCount++; 106 107 ALOGV("addInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); 108 if (inputDesc->mRefCount == 1) { 109 Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; 110 for (size_t i = 0; i < effects.size(); i++) { 111 EffectDesc *effect = effects[i]; 112 sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0, 113 0, audioSession, input); 114 status_t status = fx->initCheck(); 115 if (status != NO_ERROR && status != ALREADY_EXISTS) { 116 ALOGW("addInputEffects(): failed to create Fx %s on source %d", 117 effect->mName, (int32_t)aliasSource); 118 // fx goes out of scope and strong ref on AudioEffect is released 119 continue; 120 } 121 for (size_t j = 0; j < effect->mParams.size(); j++) { 122 fx->setParameter(effect->mParams[j]); 123 } 124 ALOGV("addInputEffects(): added Fx %s on source: %d", 125 effect->mName, (int32_t)aliasSource); 126 inputDesc->mEffects.add(fx); 127 } 128 inputDesc->setProcessorEnabled(true); 129 } 130 return status; 131} 132 133 134status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input) 135{ 136 status_t status = NO_ERROR; 137 138 Mutex::Autolock _l(mLock); 139 ssize_t index = mInputs.indexOfKey(input); 140 if (index < 0) { 141 return status; 142 } 143 EffectVector *inputDesc = mInputs.valueAt(index); 144 inputDesc->mRefCount--; 145 ALOGV("releaseInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); 146 if (inputDesc->mRefCount == 0) { 147 inputDesc->setProcessorEnabled(false); 148 delete inputDesc; 149 mInputs.removeItemsAt(index); 150 ALOGV("releaseInputEffects(): all effects released"); 151 } 152 return status; 153} 154 155status_t AudioPolicyEffects::queryDefaultInputEffects(int audioSession, 156 effect_descriptor_t *descriptors, 157 uint32_t *count) 158{ 159 status_t status = NO_ERROR; 160 161 Mutex::Autolock _l(mLock); 162 size_t index; 163 for (index = 0; index < mInputs.size(); index++) { 164 if (mInputs.valueAt(index)->mSessionId == audioSession) { 165 break; 166 } 167 } 168 if (index == mInputs.size()) { 169 *count = 0; 170 return BAD_VALUE; 171 } 172 Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; 173 174 for (size_t i = 0; i < effects.size(); i++) { 175 effect_descriptor_t desc = effects[i]->descriptor(); 176 if (i < *count) { 177 descriptors[i] = desc; 178 } 179 } 180 if (effects.size() > *count) { 181 status = NO_MEMORY; 182 } 183 *count = effects.size(); 184 return status; 185} 186 187 188status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(int audioSession, 189 effect_descriptor_t *descriptors, 190 uint32_t *count) 191{ 192 status_t status = NO_ERROR; 193 194 Mutex::Autolock _l(mLock); 195 size_t index; 196 for (index = 0; index < mOutputSessions.size(); index++) { 197 if (mOutputSessions.valueAt(index)->mSessionId == audioSession) { 198 break; 199 } 200 } 201 if (index == mOutputSessions.size()) { 202 *count = 0; 203 return BAD_VALUE; 204 } 205 Vector< sp<AudioEffect> > effects = mOutputSessions.valueAt(index)->mEffects; 206 207 for (size_t i = 0; i < effects.size(); i++) { 208 effect_descriptor_t desc = effects[i]->descriptor(); 209 if (i < *count) { 210 descriptors[i] = desc; 211 } 212 } 213 if (effects.size() > *count) { 214 status = NO_MEMORY; 215 } 216 *count = effects.size(); 217 return status; 218} 219 220 221status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, 222 audio_stream_type_t stream, 223 int audioSession) 224{ 225 status_t status = NO_ERROR; 226 227 Mutex::Autolock _l(mLock); 228 // create audio processors according to stream 229 // FIXME: should we have specific post processing settings for internal streams? 230 // default to media for now. 231 if (stream >= AUDIO_STREAM_PUBLIC_CNT) { 232 stream = AUDIO_STREAM_MUSIC; 233 } 234 ssize_t index = mOutputStreams.indexOfKey(stream); 235 if (index < 0) { 236 ALOGV("addOutputSessionEffects(): no output processing needed for this stream"); 237 return NO_ERROR; 238 } 239 240 ssize_t idx = mOutputSessions.indexOfKey(audioSession); 241 EffectVector *procDesc; 242 if (idx < 0) { 243 procDesc = new EffectVector(audioSession); 244 mOutputSessions.add(audioSession, procDesc); 245 } else { 246 // EffectVector is existing and we just need to increase ref count 247 procDesc = mOutputSessions.valueAt(idx); 248 } 249 procDesc->mRefCount++; 250 251 ALOGV("addOutputSessionEffects(): session: %d, refCount: %d", 252 audioSession, procDesc->mRefCount); 253 if (procDesc->mRefCount == 1) { 254 Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects; 255 for (size_t i = 0; i < effects.size(); i++) { 256 EffectDesc *effect = effects[i]; 257 sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0, 258 audioSession, output); 259 status_t status = fx->initCheck(); 260 if (status != NO_ERROR && status != ALREADY_EXISTS) { 261 ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d", 262 effect->mName, audioSession); 263 // fx goes out of scope and strong ref on AudioEffect is released 264 continue; 265 } 266 ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d", 267 effect->mName, audioSession, (int32_t)stream); 268 procDesc->mEffects.add(fx); 269 } 270 271 procDesc->setProcessorEnabled(true); 272 } 273 return status; 274} 275 276status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output, 277 audio_stream_type_t stream, 278 int audioSession) 279{ 280 status_t status = NO_ERROR; 281 (void) output; // argument not used for now 282 (void) stream; // argument not used for now 283 284 Mutex::Autolock _l(mLock); 285 ssize_t index = mOutputSessions.indexOfKey(audioSession); 286 if (index < 0) { 287 ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream"); 288 return NO_ERROR; 289 } 290 291 EffectVector *procDesc = mOutputSessions.valueAt(index); 292 procDesc->mRefCount--; 293 ALOGV("releaseOutputSessionEffects(): session: %d, refCount: %d", 294 audioSession, procDesc->mRefCount); 295 if (procDesc->mRefCount == 0) { 296 procDesc->setProcessorEnabled(false); 297 procDesc->mEffects.clear(); 298 delete procDesc; 299 mOutputSessions.removeItemsAt(index); 300 ALOGV("releaseOutputSessionEffects(): output processing released from session: %d", 301 audioSession); 302 } 303 return status; 304} 305 306 307void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) 308{ 309 for (size_t i = 0; i < mEffects.size(); i++) { 310 mEffects.itemAt(i)->setEnabled(enabled); 311 } 312} 313 314 315// ---------------------------------------------------------------------------- 316// Audio processing configuration 317// ---------------------------------------------------------------------------- 318 319/*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = { 320 MIC_SRC_TAG, 321 VOICE_UL_SRC_TAG, 322 VOICE_DL_SRC_TAG, 323 VOICE_CALL_SRC_TAG, 324 CAMCORDER_SRC_TAG, 325 VOICE_REC_SRC_TAG, 326 VOICE_COMM_SRC_TAG, 327 UNPROCESSED_SRC_TAG 328}; 329 330// returns the audio_source_t enum corresponding to the input source name or 331// AUDIO_SOURCE_CNT is no match found 332/*static*/ audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name) 333{ 334 int i; 335 for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { 336 if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { 337 ALOGV("inputSourceNameToEnum found source %s %d", name, i); 338 break; 339 } 340 } 341 return (audio_source_t)i; 342} 343 344const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = { 345 AUDIO_STREAM_DEFAULT_TAG, 346 AUDIO_STREAM_VOICE_CALL_TAG, 347 AUDIO_STREAM_SYSTEM_TAG, 348 AUDIO_STREAM_RING_TAG, 349 AUDIO_STREAM_MUSIC_TAG, 350 AUDIO_STREAM_ALARM_TAG, 351 AUDIO_STREAM_NOTIFICATION_TAG, 352 AUDIO_STREAM_BLUETOOTH_SCO_TAG, 353 AUDIO_STREAM_ENFORCED_AUDIBLE_TAG, 354 AUDIO_STREAM_DTMF_TAG, 355 AUDIO_STREAM_TTS_TAG 356}; 357 358// returns the audio_stream_t enum corresponding to the output stream name or 359// AUDIO_STREAM_PUBLIC_CNT is no match found 360audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) 361{ 362 int i; 363 for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) { 364 if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { 365 ALOGV("streamNameToEnum found stream %s %d", name, i); 366 break; 367 } 368 } 369 return (audio_stream_type_t)i; 370} 371 372// ---------------------------------------------------------------------------- 373// Audio Effect Config parser 374// ---------------------------------------------------------------------------- 375 376size_t AudioPolicyEffects::growParamSize(char *param, 377 size_t size, 378 size_t *curSize, 379 size_t *totSize) 380{ 381 // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) 382 size_t pos = ((*curSize - 1 ) / size + 1) * size; 383 384 if (pos + size > *totSize) { 385 while (pos + size > *totSize) { 386 *totSize += ((*totSize + 7) / 8) * 4; 387 } 388 param = (char *)realloc(param, *totSize); 389 } 390 *curSize = pos + size; 391 return pos; 392} 393 394size_t AudioPolicyEffects::readParamValue(cnode *node, 395 char *param, 396 size_t *curSize, 397 size_t *totSize) 398{ 399 if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { 400 size_t pos = growParamSize(param, sizeof(short), curSize, totSize); 401 *(short *)((char *)param + pos) = (short)atoi(node->value); 402 ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos)); 403 return sizeof(short); 404 } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { 405 size_t pos = growParamSize(param, sizeof(int), curSize, totSize); 406 *(int *)((char *)param + pos) = atoi(node->value); 407 ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos)); 408 return sizeof(int); 409 } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { 410 size_t pos = growParamSize(param, sizeof(float), curSize, totSize); 411 *(float *)((char *)param + pos) = (float)atof(node->value); 412 ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos)); 413 return sizeof(float); 414 } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { 415 size_t pos = growParamSize(param, sizeof(bool), curSize, totSize); 416 if (strncmp(node->value, "false", strlen("false") + 1) == 0) { 417 *(bool *)((char *)param + pos) = false; 418 } else { 419 *(bool *)((char *)param + pos) = true; 420 } 421 ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false"); 422 return sizeof(bool); 423 } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { 424 size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX); 425 if (*curSize + len + 1 > *totSize) { 426 *totSize = *curSize + len + 1; 427 param = (char *)realloc(param, *totSize); 428 } 429 strncpy(param + *curSize, node->value, len); 430 *curSize += len; 431 param[*curSize] = '\0'; 432 ALOGV("readParamValue() reading string %s", param + *curSize - len); 433 return len; 434 } 435 ALOGW("readParamValue() unknown param type %s", node->name); 436 return 0; 437} 438 439effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) 440{ 441 cnode *param; 442 cnode *value; 443 size_t curSize = sizeof(effect_param_t); 444 size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); 445 effect_param_t *fx_param = (effect_param_t *)malloc(totSize); 446 447 param = config_find(root, PARAM_TAG); 448 value = config_find(root, VALUE_TAG); 449 if (param == NULL && value == NULL) { 450 // try to parse simple parameter form {int int} 451 param = root->first_child; 452 if (param != NULL) { 453 // Note: that a pair of random strings is read as 0 0 454 int *ptr = (int *)fx_param->data; 455 int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); 456 ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); 457 *ptr++ = atoi(param->name); 458 *ptr = atoi(param->value); 459 fx_param->psize = sizeof(int); 460 fx_param->vsize = sizeof(int); 461 return fx_param; 462 } 463 } 464 if (param == NULL || value == NULL) { 465 ALOGW("loadEffectParameter() invalid parameter description %s", root->name); 466 goto error; 467 } 468 469 fx_param->psize = 0; 470 param = param->first_child; 471 while (param) { 472 ALOGV("loadEffectParameter() reading param of type %s", param->name); 473 size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize); 474 if (size == 0) { 475 goto error; 476 } 477 fx_param->psize += size; 478 param = param->next; 479 } 480 481 // align start of value field on 32 bit boundary 482 curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); 483 484 fx_param->vsize = 0; 485 value = value->first_child; 486 while (value) { 487 ALOGV("loadEffectParameter() reading value of type %s", value->name); 488 size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize); 489 if (size == 0) { 490 goto error; 491 } 492 fx_param->vsize += size; 493 value = value->next; 494 } 495 496 return fx_param; 497 498error: 499 delete fx_param; 500 return NULL; 501} 502 503void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params) 504{ 505 cnode *node = root->first_child; 506 while (node) { 507 ALOGV("loadEffectParameters() loading param %s", node->name); 508 effect_param_t *param = loadEffectParameter(node); 509 if (param == NULL) { 510 node = node->next; 511 continue; 512 } 513 params.add(param); 514 node = node->next; 515 } 516} 517 518 519AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig( 520 cnode *root, 521 const Vector <EffectDesc *>& effects) 522{ 523 cnode *node = root->first_child; 524 if (node == NULL) { 525 ALOGW("loadInputSource() empty element %s", root->name); 526 return NULL; 527 } 528 EffectDescVector *desc = new EffectDescVector(); 529 while (node) { 530 size_t i; 531 for (i = 0; i < effects.size(); i++) { 532 if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { 533 ALOGV("loadEffectConfig() found effect %s in list", node->name); 534 break; 535 } 536 } 537 if (i == effects.size()) { 538 ALOGV("loadEffectConfig() effect %s not in list", node->name); 539 node = node->next; 540 continue; 541 } 542 EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy 543 loadEffectParameters(node, effect->mParams); 544 ALOGV("loadEffectConfig() adding effect %s uuid %08x", 545 effect->mName, effect->mUuid.timeLow); 546 desc->mEffects.add(effect); 547 node = node->next; 548 } 549 if (desc->mEffects.size() == 0) { 550 ALOGW("loadEffectConfig() no valid effects found in config %s", root->name); 551 delete desc; 552 return NULL; 553 } 554 return desc; 555} 556 557status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root, 558 const Vector <EffectDesc *>& effects) 559{ 560 cnode *node = config_find(root, PREPROCESSING_TAG); 561 if (node == NULL) { 562 return -ENOENT; 563 } 564 node = node->first_child; 565 while (node) { 566 audio_source_t source = inputSourceNameToEnum(node->name); 567 if (source == AUDIO_SOURCE_CNT) { 568 ALOGW("loadInputSources() invalid input source %s", node->name); 569 node = node->next; 570 continue; 571 } 572 ALOGV("loadInputSources() loading input source %s", node->name); 573 EffectDescVector *desc = loadEffectConfig(node, effects); 574 if (desc == NULL) { 575 node = node->next; 576 continue; 577 } 578 mInputSources.add(source, desc); 579 node = node->next; 580 } 581 return NO_ERROR; 582} 583 584status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, 585 const Vector <EffectDesc *>& effects) 586{ 587 cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG); 588 if (node == NULL) { 589 return -ENOENT; 590 } 591 node = node->first_child; 592 while (node) { 593 audio_stream_type_t stream = streamNameToEnum(node->name); 594 if (stream == AUDIO_STREAM_PUBLIC_CNT) { 595 ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); 596 node = node->next; 597 continue; 598 } 599 ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name); 600 EffectDescVector *desc = loadEffectConfig(node, effects); 601 if (desc == NULL) { 602 node = node->next; 603 continue; 604 } 605 mOutputStreams.add(stream, desc); 606 node = node->next; 607 } 608 return NO_ERROR; 609} 610 611AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root) 612{ 613 cnode *node = config_find(root, UUID_TAG); 614 if (node == NULL) { 615 return NULL; 616 } 617 effect_uuid_t uuid; 618 if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { 619 ALOGW("loadEffect() invalid uuid %s", node->value); 620 return NULL; 621 } 622 return new EffectDesc(root->name, uuid); 623} 624 625status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects) 626{ 627 cnode *node = config_find(root, EFFECTS_TAG); 628 if (node == NULL) { 629 return -ENOENT; 630 } 631 node = node->first_child; 632 while (node) { 633 ALOGV("loadEffects() loading effect %s", node->name); 634 EffectDesc *effect = loadEffect(node); 635 if (effect == NULL) { 636 node = node->next; 637 continue; 638 } 639 effects.add(effect); 640 node = node->next; 641 } 642 return NO_ERROR; 643} 644 645status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) 646{ 647 cnode *root; 648 char *data; 649 650 data = (char *)load_file(path, NULL); 651 if (data == NULL) { 652 return -ENODEV; 653 } 654 root = config_node("", ""); 655 config_load(root, data); 656 657 Vector <EffectDesc *> effects; 658 loadEffects(root, effects); 659 loadInputEffectConfigurations(root, effects); 660 loadStreamEffectConfigurations(root, effects); 661 662 for (size_t i = 0; i < effects.size(); i++) { 663 delete effects[i]; 664 } 665 666 config_free(root); 667 free(root); 668 free(data); 669 670 return NO_ERROR; 671} 672 673 674}; // namespace android 675