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