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