AudioPolicyEffects.cpp revision 138ed1796f8c8edd318488911a9b056877191778
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        if (*param == NULL) {
391            ALOGE("%s realloc error for size %zu", __func__, *totSize);
392            return 0;
393        }
394    }
395    *curSize = pos + size;
396    return pos;
397}
398
399
400size_t AudioPolicyEffects::readParamValue(cnode *node,
401                                          char **param,
402                                          size_t *curSize,
403                                          size_t *totSize)
404{
405    size_t len = 0;
406    size_t pos;
407
408    if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) {
409        pos = growParamSize(param, sizeof(short), curSize, totSize);
410        if (pos == 0) {
411            goto exit;
412        }
413        *(short *)(*param + pos) = (short)atoi(node->value);
414        ALOGV("readParamValue() reading short %d", *(short *)(*param + pos));
415        len = sizeof(short);
416    } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) {
417        pos = growParamSize(param, sizeof(int), curSize, totSize);
418        if (pos == 0) {
419            goto exit;
420        }
421        *(int *)(*param + pos) = atoi(node->value);
422        ALOGV("readParamValue() reading int %d", *(int *)(*param + pos));
423        len = sizeof(int);
424    } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) {
425        pos = growParamSize(param, sizeof(float), curSize, totSize);
426        if (pos == 0) {
427            goto exit;
428        }
429        *(float *)(*param + pos) = (float)atof(node->value);
430        ALOGV("readParamValue() reading float %f",*(float *)(*param + pos));
431        len = sizeof(float);
432    } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) {
433        pos = growParamSize(param, sizeof(bool), curSize, totSize);
434        if (pos == 0) {
435            goto exit;
436        }
437        if (strncmp(node->value, "true", strlen("true") + 1) == 0) {
438            *(bool *)(*param + pos) = true;
439        } else {
440            *(bool *)(*param + pos) = false;
441        }
442        ALOGV("readParamValue() reading bool %s",
443              *(bool *)(*param + pos) ? "true" : "false");
444        len = sizeof(bool);
445    } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) {
446        len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
447        if (*curSize + len + 1 > *totSize) {
448            *totSize = *curSize + len + 1;
449            *param = (char *)realloc(*param, *totSize);
450            if (*param == NULL) {
451                len = 0;
452                ALOGE("%s realloc error for string len %zu", __func__, *totSize);
453                goto exit;
454            }
455        }
456        strncpy(*param + *curSize, node->value, len);
457        *curSize += len;
458        (*param)[*curSize] = '\0';
459        ALOGV("readParamValue() reading string %s", *param + *curSize - len);
460    } else {
461        ALOGW("readParamValue() unknown param type %s", node->name);
462    }
463exit:
464    return len;
465}
466
467effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root)
468{
469    cnode *param;
470    cnode *value;
471    size_t curSize = sizeof(effect_param_t);
472    size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int);
473    effect_param_t *fx_param = (effect_param_t *)malloc(totSize);
474
475    if (fx_param == NULL) {
476        ALOGE("%s malloc error for effect structure of size %zu",
477              __func__, totSize);
478        return NULL;
479    }
480
481    param = config_find(root, PARAM_TAG);
482    value = config_find(root, VALUE_TAG);
483    if (param == NULL && value == NULL) {
484        // try to parse simple parameter form {int int}
485        param = root->first_child;
486        if (param != NULL) {
487            // Note: that a pair of random strings is read as 0 0
488            int *ptr = (int *)fx_param->data;
489#if LOG_NDEBUG == 0
490            int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
491            ALOGV("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2);
492#endif
493            *ptr++ = atoi(param->name);
494            *ptr = atoi(param->value);
495            fx_param->psize = sizeof(int);
496            fx_param->vsize = sizeof(int);
497            return fx_param;
498        }
499    }
500    if (param == NULL || value == NULL) {
501        ALOGW("loadEffectParameter() invalid parameter description %s",
502              root->name);
503        goto error;
504    }
505
506    fx_param->psize = 0;
507    param = param->first_child;
508    while (param) {
509        ALOGV("loadEffectParameter() reading param of type %s", param->name);
510        size_t size =
511                readParamValue(param, (char **)&fx_param, &curSize, &totSize);
512        if (size == 0) {
513            goto error;
514        }
515        fx_param->psize += size;
516        param = param->next;
517    }
518
519    // align start of value field on 32 bit boundary
520    curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int);
521
522    fx_param->vsize = 0;
523    value = value->first_child;
524    while (value) {
525        ALOGV("loadEffectParameter() reading value of type %s", value->name);
526        size_t size =
527                readParamValue(value, (char **)&fx_param, &curSize, &totSize);
528        if (size == 0) {
529            goto error;
530        }
531        fx_param->vsize += size;
532        value = value->next;
533    }
534
535    return fx_param;
536
537error:
538    free(fx_param);
539    return NULL;
540}
541
542void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params)
543{
544    cnode *node = root->first_child;
545    while (node) {
546        ALOGV("loadEffectParameters() loading param %s", node->name);
547        effect_param_t *param = loadEffectParameter(node);
548        if (param != NULL) {
549            params.add(param);
550        }
551        node = node->next;
552    }
553}
554
555
556AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig(
557                                                            cnode *root,
558                                                            const Vector <EffectDesc *>& effects)
559{
560    cnode *node = root->first_child;
561    if (node == NULL) {
562        ALOGW("loadInputSource() empty element %s", root->name);
563        return NULL;
564    }
565    EffectDescVector *desc = new EffectDescVector();
566    while (node) {
567        size_t i;
568
569        for (i = 0; i < effects.size(); i++) {
570            if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) {
571                ALOGV("loadEffectConfig() found effect %s in list", node->name);
572                break;
573            }
574        }
575        if (i == effects.size()) {
576            ALOGV("loadEffectConfig() effect %s not in list", node->name);
577            node = node->next;
578            continue;
579        }
580        EffectDesc *effect = new EffectDesc(*effects[i]);   // deep copy
581        loadEffectParameters(node, effect->mParams);
582        ALOGV("loadEffectConfig() adding effect %s uuid %08x",
583              effect->mName, effect->mUuid.timeLow);
584        desc->mEffects.add(effect);
585        node = node->next;
586    }
587    if (desc->mEffects.size() == 0) {
588        ALOGW("loadEffectConfig() no valid effects found in config %s", root->name);
589        delete desc;
590        return NULL;
591    }
592    return desc;
593}
594
595status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root,
596                                                           const Vector <EffectDesc *>& effects)
597{
598    cnode *node = config_find(root, PREPROCESSING_TAG);
599    if (node == NULL) {
600        return -ENOENT;
601    }
602    node = node->first_child;
603    while (node) {
604        audio_source_t source = inputSourceNameToEnum(node->name);
605        if (source == AUDIO_SOURCE_CNT) {
606            ALOGW("loadInputSources() invalid input source %s", node->name);
607            node = node->next;
608            continue;
609        }
610        ALOGV("loadInputSources() loading input source %s", node->name);
611        EffectDescVector *desc = loadEffectConfig(node, effects);
612        if (desc == NULL) {
613            node = node->next;
614            continue;
615        }
616        mInputSources.add(source, desc);
617        node = node->next;
618    }
619    return NO_ERROR;
620}
621
622status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root,
623                                                            const Vector <EffectDesc *>& effects)
624{
625    cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG);
626    if (node == NULL) {
627        return -ENOENT;
628    }
629    node = node->first_child;
630    while (node) {
631        audio_stream_type_t stream = streamNameToEnum(node->name);
632        if (stream == AUDIO_STREAM_PUBLIC_CNT) {
633            ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name);
634            node = node->next;
635            continue;
636        }
637        ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name);
638        EffectDescVector *desc = loadEffectConfig(node, effects);
639        if (desc == NULL) {
640            node = node->next;
641            continue;
642        }
643        mOutputStreams.add(stream, desc);
644        node = node->next;
645    }
646    return NO_ERROR;
647}
648
649AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root)
650{
651    cnode *node = config_find(root, UUID_TAG);
652    if (node == NULL) {
653        return NULL;
654    }
655    effect_uuid_t uuid;
656    if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) {
657        ALOGW("loadEffect() invalid uuid %s", node->value);
658        return NULL;
659    }
660    return new EffectDesc(root->name, uuid);
661}
662
663status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects)
664{
665    cnode *node = config_find(root, EFFECTS_TAG);
666    if (node == NULL) {
667        return -ENOENT;
668    }
669    node = node->first_child;
670    while (node) {
671        ALOGV("loadEffects() loading effect %s", node->name);
672        EffectDesc *effect = loadEffect(node);
673        if (effect == NULL) {
674            node = node->next;
675            continue;
676        }
677        effects.add(effect);
678        node = node->next;
679    }
680    return NO_ERROR;
681}
682
683status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path)
684{
685    cnode *root;
686    char *data;
687
688    data = (char *)load_file(path, NULL);
689    if (data == NULL) {
690        return -ENODEV;
691    }
692    root = config_node("", "");
693    config_load(root, data);
694
695    Vector <EffectDesc *> effects;
696    loadEffects(root, effects);
697    loadInputEffectConfigurations(root, effects);
698    loadStreamEffectConfigurations(root, effects);
699
700    for (size_t i = 0; i < effects.size(); i++) {
701        delete effects[i];
702    }
703
704    config_free(root);
705    free(root);
706    free(data);
707
708    return NO_ERROR;
709}
710
711
712}; // namespace android
713