AudioPolicyEffects.cpp revision 56ec4ffcbae8aeac6c5245fc7b825d02e2e6cefd
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, &effect->mUuid, -1, 0, 0, 113 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, &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}; 328 329// returns the audio_source_t enum corresponding to the input source name or 330// AUDIO_SOURCE_CNT is no match found 331/*static*/ audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name) 332{ 333 int i; 334 for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { 335 if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { 336 ALOGV("inputSourceNameToEnum found source %s %d", name, i); 337 break; 338 } 339 } 340 return (audio_source_t)i; 341} 342 343const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = { 344 AUDIO_STREAM_DEFAULT_TAG, 345 AUDIO_STREAM_VOICE_CALL_TAG, 346 AUDIO_STREAM_SYSTEM_TAG, 347 AUDIO_STREAM_RING_TAG, 348 AUDIO_STREAM_MUSIC_TAG, 349 AUDIO_STREAM_ALARM_TAG, 350 AUDIO_STREAM_NOTIFICATION_TAG, 351 AUDIO_STREAM_BLUETOOTH_SCO_TAG, 352 AUDIO_STREAM_ENFORCED_AUDIBLE_TAG, 353 AUDIO_STREAM_DTMF_TAG, 354 AUDIO_STREAM_TTS_TAG 355}; 356 357// returns the audio_stream_t enum corresponding to the output stream name or 358// AUDIO_STREAM_PUBLIC_CNT is no match found 359audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) 360{ 361 int i; 362 for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) { 363 if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { 364 ALOGV("streamNameToEnum found stream %s %d", name, i); 365 break; 366 } 367 } 368 return (audio_stream_type_t)i; 369} 370 371// ---------------------------------------------------------------------------- 372// Audio Effect Config parser 373// ---------------------------------------------------------------------------- 374 375size_t AudioPolicyEffects::growParamSize(char *param, 376 size_t size, 377 size_t *curSize, 378 size_t *totSize) 379{ 380 // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) 381 size_t pos = ((*curSize - 1 ) / size + 1) * size; 382 383 if (pos + size > *totSize) { 384 while (pos + size > *totSize) { 385 *totSize += ((*totSize + 7) / 8) * 4; 386 } 387 param = (char *)realloc(param, *totSize); 388 } 389 *curSize = pos + size; 390 return pos; 391} 392 393size_t AudioPolicyEffects::readParamValue(cnode *node, 394 char *param, 395 size_t *curSize, 396 size_t *totSize) 397{ 398 if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { 399 size_t pos = growParamSize(param, sizeof(short), curSize, totSize); 400 *(short *)((char *)param + pos) = (short)atoi(node->value); 401 ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos)); 402 return sizeof(short); 403 } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { 404 size_t pos = growParamSize(param, sizeof(int), curSize, totSize); 405 *(int *)((char *)param + pos) = atoi(node->value); 406 ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos)); 407 return sizeof(int); 408 } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { 409 size_t pos = growParamSize(param, sizeof(float), curSize, totSize); 410 *(float *)((char *)param + pos) = (float)atof(node->value); 411 ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos)); 412 return sizeof(float); 413 } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { 414 size_t pos = growParamSize(param, sizeof(bool), curSize, totSize); 415 if (strncmp(node->value, "false", strlen("false") + 1) == 0) { 416 *(bool *)((char *)param + pos) = false; 417 } else { 418 *(bool *)((char *)param + pos) = true; 419 } 420 ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false"); 421 return sizeof(bool); 422 } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { 423 size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX); 424 if (*curSize + len + 1 > *totSize) { 425 *totSize = *curSize + len + 1; 426 param = (char *)realloc(param, *totSize); 427 } 428 strncpy(param + *curSize, node->value, len); 429 *curSize += len; 430 param[*curSize] = '\0'; 431 ALOGV("readParamValue() reading string %s", param + *curSize - len); 432 return len; 433 } 434 ALOGW("readParamValue() unknown param type %s", node->name); 435 return 0; 436} 437 438effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) 439{ 440 cnode *param; 441 cnode *value; 442 size_t curSize = sizeof(effect_param_t); 443 size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); 444 effect_param_t *fx_param = (effect_param_t *)malloc(totSize); 445 446 param = config_find(root, PARAM_TAG); 447 value = config_find(root, VALUE_TAG); 448 if (param == NULL && value == NULL) { 449 // try to parse simple parameter form {int int} 450 param = root->first_child; 451 if (param != NULL) { 452 // Note: that a pair of random strings is read as 0 0 453 int *ptr = (int *)fx_param->data; 454 int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); 455 ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); 456 *ptr++ = atoi(param->name); 457 *ptr = atoi(param->value); 458 fx_param->psize = sizeof(int); 459 fx_param->vsize = sizeof(int); 460 return fx_param; 461 } 462 } 463 if (param == NULL || value == NULL) { 464 ALOGW("loadEffectParameter() invalid parameter description %s", root->name); 465 goto error; 466 } 467 468 fx_param->psize = 0; 469 param = param->first_child; 470 while (param) { 471 ALOGV("loadEffectParameter() reading param of type %s", param->name); 472 size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize); 473 if (size == 0) { 474 goto error; 475 } 476 fx_param->psize += size; 477 param = param->next; 478 } 479 480 // align start of value field on 32 bit boundary 481 curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); 482 483 fx_param->vsize = 0; 484 value = value->first_child; 485 while (value) { 486 ALOGV("loadEffectParameter() reading value of type %s", value->name); 487 size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize); 488 if (size == 0) { 489 goto error; 490 } 491 fx_param->vsize += size; 492 value = value->next; 493 } 494 495 return fx_param; 496 497error: 498 delete fx_param; 499 return NULL; 500} 501 502void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params) 503{ 504 cnode *node = root->first_child; 505 while (node) { 506 ALOGV("loadEffectParameters() loading param %s", node->name); 507 effect_param_t *param = loadEffectParameter(node); 508 if (param == NULL) { 509 node = node->next; 510 continue; 511 } 512 params.add(param); 513 node = node->next; 514 } 515} 516 517 518AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig( 519 cnode *root, 520 const Vector <EffectDesc *>& effects) 521{ 522 cnode *node = root->first_child; 523 if (node == NULL) { 524 ALOGW("loadInputSource() empty element %s", root->name); 525 return NULL; 526 } 527 EffectDescVector *desc = new EffectDescVector(); 528 while (node) { 529 size_t i; 530 for (i = 0; i < effects.size(); i++) { 531 if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { 532 ALOGV("loadEffectConfig() found effect %s in list", node->name); 533 break; 534 } 535 } 536 if (i == effects.size()) { 537 ALOGV("loadEffectConfig() effect %s not in list", node->name); 538 node = node->next; 539 continue; 540 } 541 EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy 542 loadEffectParameters(node, effect->mParams); 543 ALOGV("loadEffectConfig() adding effect %s uuid %08x", 544 effect->mName, effect->mUuid.timeLow); 545 desc->mEffects.add(effect); 546 node = node->next; 547 } 548 if (desc->mEffects.size() == 0) { 549 ALOGW("loadEffectConfig() no valid effects found in config %s", root->name); 550 delete desc; 551 return NULL; 552 } 553 return desc; 554} 555 556status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root, 557 const Vector <EffectDesc *>& effects) 558{ 559 cnode *node = config_find(root, PREPROCESSING_TAG); 560 if (node == NULL) { 561 return -ENOENT; 562 } 563 node = node->first_child; 564 while (node) { 565 audio_source_t source = inputSourceNameToEnum(node->name); 566 if (source == AUDIO_SOURCE_CNT) { 567 ALOGW("loadInputSources() invalid input source %s", node->name); 568 node = node->next; 569 continue; 570 } 571 ALOGV("loadInputSources() loading input source %s", node->name); 572 EffectDescVector *desc = loadEffectConfig(node, effects); 573 if (desc == NULL) { 574 node = node->next; 575 continue; 576 } 577 mInputSources.add(source, desc); 578 node = node->next; 579 } 580 return NO_ERROR; 581} 582 583status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, 584 const Vector <EffectDesc *>& effects) 585{ 586 cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG); 587 if (node == NULL) { 588 return -ENOENT; 589 } 590 node = node->first_child; 591 while (node) { 592 audio_stream_type_t stream = streamNameToEnum(node->name); 593 if (stream == AUDIO_STREAM_PUBLIC_CNT) { 594 ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); 595 node = node->next; 596 continue; 597 } 598 ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name); 599 EffectDescVector *desc = loadEffectConfig(node, effects); 600 if (desc == NULL) { 601 node = node->next; 602 continue; 603 } 604 mOutputStreams.add(stream, desc); 605 node = node->next; 606 } 607 return NO_ERROR; 608} 609 610AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root) 611{ 612 cnode *node = config_find(root, UUID_TAG); 613 if (node == NULL) { 614 return NULL; 615 } 616 effect_uuid_t uuid; 617 if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { 618 ALOGW("loadEffect() invalid uuid %s", node->value); 619 return NULL; 620 } 621 return new EffectDesc(root->name, uuid); 622} 623 624status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects) 625{ 626 cnode *node = config_find(root, EFFECTS_TAG); 627 if (node == NULL) { 628 return -ENOENT; 629 } 630 node = node->first_child; 631 while (node) { 632 ALOGV("loadEffects() loading effect %s", node->name); 633 EffectDesc *effect = loadEffect(node); 634 if (effect == NULL) { 635 node = node->next; 636 continue; 637 } 638 effects.add(effect); 639 node = node->next; 640 } 641 return NO_ERROR; 642} 643 644status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) 645{ 646 cnode *root; 647 char *data; 648 649 data = (char *)load_file(path, NULL); 650 if (data == NULL) { 651 return -ENODEV; 652 } 653 root = config_node("", ""); 654 config_load(root, data); 655 656 Vector <EffectDesc *> effects; 657 loadEffects(root, effects); 658 loadInputEffectConfigurations(root, effects); 659 loadStreamEffectConfigurations(root, effects); 660 661 for (size_t i = 0; i < effects.size(); i++) { 662 delete effects[i]; 663 } 664 665 config_free(root); 666 free(root); 667 free(data); 668 669 return NO_ERROR; 670} 671 672 673}; // namespace android 674