| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "AudioPolicyEffects" |
| //#define LOG_NDEBUG 0 |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <cutils/misc.h> |
| #include <media/AudioEffect.h> |
| #include <system/audio.h> |
| #include <hardware/audio_effect.h> |
| #include <audio_effects/audio_effects_conf.h> |
| #include <utils/Vector.h> |
| #include <utils/SortedVector.h> |
| #include <cutils/config_utils.h> |
| #include "AudioPolicyEffects.h" |
| #include "ServiceUtilities.h" |
| |
| namespace android { |
| |
| // ---------------------------------------------------------------------------- |
| // AudioPolicyEffects Implementation |
| // ---------------------------------------------------------------------------- |
| |
| AudioPolicyEffects::AudioPolicyEffects() |
| { |
| // load automatic audio effect modules |
| if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { |
| loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE); |
| } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { |
| loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); |
| } |
| } |
| |
| |
| AudioPolicyEffects::~AudioPolicyEffects() |
| { |
| size_t i = 0; |
| // release audio input processing resources |
| for (i = 0; i < mInputSources.size(); i++) { |
| delete mInputSources.valueAt(i); |
| } |
| mInputSources.clear(); |
| |
| for (i = 0; i < mInputs.size(); i++) { |
| mInputs.valueAt(i)->mEffects.clear(); |
| delete mInputs.valueAt(i); |
| } |
| mInputs.clear(); |
| |
| // release audio output processing resources |
| for (i = 0; i < mOutputStreams.size(); i++) { |
| delete mOutputStreams.valueAt(i); |
| } |
| mOutputStreams.clear(); |
| |
| for (i = 0; i < mOutputSessions.size(); i++) { |
| mOutputSessions.valueAt(i)->mEffects.clear(); |
| delete mOutputSessions.valueAt(i); |
| } |
| mOutputSessions.clear(); |
| } |
| |
| |
| status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input, |
| audio_source_t inputSource, |
| audio_session_t audioSession) |
| { |
| status_t status = NO_ERROR; |
| |
| // create audio pre processors according to input source |
| audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? |
| AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; |
| |
| Mutex::Autolock _l(mLock); |
| ssize_t index = mInputSources.indexOfKey(aliasSource); |
| if (index < 0) { |
| ALOGV("addInputEffects(): no processing needs to be attached to this source"); |
| return status; |
| } |
| ssize_t idx = mInputs.indexOfKey(input); |
| EffectVector *inputDesc; |
| if (idx < 0) { |
| inputDesc = new EffectVector(audioSession); |
| mInputs.add(input, inputDesc); |
| } else { |
| // EffectVector is existing and we just need to increase ref count |
| inputDesc = mInputs.valueAt(idx); |
| } |
| inputDesc->mRefCount++; |
| |
| ALOGV("addInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); |
| if (inputDesc->mRefCount == 1) { |
| Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; |
| for (size_t i = 0; i < effects.size(); i++) { |
| EffectDesc *effect = effects[i]; |
| sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0, |
| 0, audioSession, input); |
| status_t status = fx->initCheck(); |
| if (status != NO_ERROR && status != ALREADY_EXISTS) { |
| ALOGW("addInputEffects(): failed to create Fx %s on source %d", |
| effect->mName, (int32_t)aliasSource); |
| // fx goes out of scope and strong ref on AudioEffect is released |
| continue; |
| } |
| for (size_t j = 0; j < effect->mParams.size(); j++) { |
| fx->setParameter(effect->mParams[j]); |
| } |
| ALOGV("addInputEffects(): added Fx %s on source: %d", |
| effect->mName, (int32_t)aliasSource); |
| inputDesc->mEffects.add(fx); |
| } |
| inputDesc->setProcessorEnabled(true); |
| } |
| return status; |
| } |
| |
| |
| status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input) |
| { |
| status_t status = NO_ERROR; |
| |
| Mutex::Autolock _l(mLock); |
| ssize_t index = mInputs.indexOfKey(input); |
| if (index < 0) { |
| return status; |
| } |
| EffectVector *inputDesc = mInputs.valueAt(index); |
| inputDesc->mRefCount--; |
| ALOGV("releaseInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); |
| if (inputDesc->mRefCount == 0) { |
| inputDesc->setProcessorEnabled(false); |
| delete inputDesc; |
| mInputs.removeItemsAt(index); |
| ALOGV("releaseInputEffects(): all effects released"); |
| } |
| return status; |
| } |
| |
| status_t AudioPolicyEffects::queryDefaultInputEffects(audio_session_t audioSession, |
| effect_descriptor_t *descriptors, |
| uint32_t *count) |
| { |
| status_t status = NO_ERROR; |
| |
| Mutex::Autolock _l(mLock); |
| size_t index; |
| for (index = 0; index < mInputs.size(); index++) { |
| if (mInputs.valueAt(index)->mSessionId == audioSession) { |
| break; |
| } |
| } |
| if (index == mInputs.size()) { |
| *count = 0; |
| return BAD_VALUE; |
| } |
| Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; |
| |
| for (size_t i = 0; i < effects.size(); i++) { |
| effect_descriptor_t desc = effects[i]->descriptor(); |
| if (i < *count) { |
| descriptors[i] = desc; |
| } |
| } |
| if (effects.size() > *count) { |
| status = NO_MEMORY; |
| } |
| *count = effects.size(); |
| return status; |
| } |
| |
| |
| status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(audio_session_t audioSession, |
| effect_descriptor_t *descriptors, |
| uint32_t *count) |
| { |
| status_t status = NO_ERROR; |
| |
| Mutex::Autolock _l(mLock); |
| size_t index; |
| for (index = 0; index < mOutputSessions.size(); index++) { |
| if (mOutputSessions.valueAt(index)->mSessionId == audioSession) { |
| break; |
| } |
| } |
| if (index == mOutputSessions.size()) { |
| *count = 0; |
| return BAD_VALUE; |
| } |
| Vector< sp<AudioEffect> > effects = mOutputSessions.valueAt(index)->mEffects; |
| |
| for (size_t i = 0; i < effects.size(); i++) { |
| effect_descriptor_t desc = effects[i]->descriptor(); |
| if (i < *count) { |
| descriptors[i] = desc; |
| } |
| } |
| if (effects.size() > *count) { |
| status = NO_MEMORY; |
| } |
| *count = effects.size(); |
| return status; |
| } |
| |
| |
| status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, |
| audio_stream_type_t stream, |
| audio_session_t audioSession) |
| { |
| status_t status = NO_ERROR; |
| |
| Mutex::Autolock _l(mLock); |
| // create audio processors according to stream |
| // FIXME: should we have specific post processing settings for internal streams? |
| // default to media for now. |
| if (stream >= AUDIO_STREAM_PUBLIC_CNT) { |
| stream = AUDIO_STREAM_MUSIC; |
| } |
| ssize_t index = mOutputStreams.indexOfKey(stream); |
| if (index < 0) { |
| ALOGV("addOutputSessionEffects(): no output processing needed for this stream"); |
| return NO_ERROR; |
| } |
| |
| ssize_t idx = mOutputSessions.indexOfKey(audioSession); |
| EffectVector *procDesc; |
| if (idx < 0) { |
| procDesc = new EffectVector(audioSession); |
| mOutputSessions.add(audioSession, procDesc); |
| } else { |
| // EffectVector is existing and we just need to increase ref count |
| procDesc = mOutputSessions.valueAt(idx); |
| } |
| procDesc->mRefCount++; |
| |
| ALOGV("addOutputSessionEffects(): session: %d, refCount: %d", |
| audioSession, procDesc->mRefCount); |
| if (procDesc->mRefCount == 1) { |
| Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects; |
| for (size_t i = 0; i < effects.size(); i++) { |
| EffectDesc *effect = effects[i]; |
| sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0, |
| audioSession, output); |
| status_t status = fx->initCheck(); |
| if (status != NO_ERROR && status != ALREADY_EXISTS) { |
| ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d", |
| effect->mName, audioSession); |
| // fx goes out of scope and strong ref on AudioEffect is released |
| continue; |
| } |
| ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d", |
| effect->mName, audioSession, (int32_t)stream); |
| procDesc->mEffects.add(fx); |
| } |
| |
| procDesc->setProcessorEnabled(true); |
| } |
| return status; |
| } |
| |
| status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output, |
| audio_stream_type_t stream, |
| audio_session_t audioSession) |
| { |
| status_t status = NO_ERROR; |
| (void) output; // argument not used for now |
| (void) stream; // argument not used for now |
| |
| Mutex::Autolock _l(mLock); |
| ssize_t index = mOutputSessions.indexOfKey(audioSession); |
| if (index < 0) { |
| ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream"); |
| return NO_ERROR; |
| } |
| |
| EffectVector *procDesc = mOutputSessions.valueAt(index); |
| procDesc->mRefCount--; |
| ALOGV("releaseOutputSessionEffects(): session: %d, refCount: %d", |
| audioSession, procDesc->mRefCount); |
| if (procDesc->mRefCount == 0) { |
| procDesc->setProcessorEnabled(false); |
| procDesc->mEffects.clear(); |
| delete procDesc; |
| mOutputSessions.removeItemsAt(index); |
| ALOGV("releaseOutputSessionEffects(): output processing released from session: %d", |
| audioSession); |
| } |
| return status; |
| } |
| |
| |
| void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) |
| { |
| for (size_t i = 0; i < mEffects.size(); i++) { |
| mEffects.itemAt(i)->setEnabled(enabled); |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Audio processing configuration |
| // ---------------------------------------------------------------------------- |
| |
| /*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = { |
| MIC_SRC_TAG, |
| VOICE_UL_SRC_TAG, |
| VOICE_DL_SRC_TAG, |
| VOICE_CALL_SRC_TAG, |
| CAMCORDER_SRC_TAG, |
| VOICE_REC_SRC_TAG, |
| VOICE_COMM_SRC_TAG, |
| UNPROCESSED_SRC_TAG |
| }; |
| |
| // returns the audio_source_t enum corresponding to the input source name or |
| // AUDIO_SOURCE_CNT is no match found |
| /*static*/ audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name) |
| { |
| int i; |
| for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { |
| if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { |
| ALOGV("inputSourceNameToEnum found source %s %d", name, i); |
| break; |
| } |
| } |
| return (audio_source_t)i; |
| } |
| |
| const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = { |
| AUDIO_STREAM_DEFAULT_TAG, |
| AUDIO_STREAM_VOICE_CALL_TAG, |
| AUDIO_STREAM_SYSTEM_TAG, |
| AUDIO_STREAM_RING_TAG, |
| AUDIO_STREAM_MUSIC_TAG, |
| AUDIO_STREAM_ALARM_TAG, |
| AUDIO_STREAM_NOTIFICATION_TAG, |
| AUDIO_STREAM_BLUETOOTH_SCO_TAG, |
| AUDIO_STREAM_ENFORCED_AUDIBLE_TAG, |
| AUDIO_STREAM_DTMF_TAG, |
| AUDIO_STREAM_TTS_TAG |
| }; |
| |
| // returns the audio_stream_t enum corresponding to the output stream name or |
| // AUDIO_STREAM_PUBLIC_CNT is no match found |
| audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) |
| { |
| int i; |
| for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) { |
| if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { |
| ALOGV("streamNameToEnum found stream %s %d", name, i); |
| break; |
| } |
| } |
| return (audio_stream_type_t)i; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Audio Effect Config parser |
| // ---------------------------------------------------------------------------- |
| |
| size_t AudioPolicyEffects::growParamSize(char **param, |
| size_t size, |
| size_t *curSize, |
| size_t *totSize) |
| { |
| // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) |
| size_t pos = ((*curSize - 1 ) / size + 1) * size; |
| |
| if (pos + size > *totSize) { |
| while (pos + size > *totSize) { |
| *totSize += ((*totSize + 7) / 8) * 4; |
| } |
| *param = (char *)realloc(*param, *totSize); |
| if (*param == NULL) { |
| ALOGE("%s realloc error for size %zu", __func__, *totSize); |
| return 0; |
| } |
| } |
| *curSize = pos + size; |
| return pos; |
| } |
| |
| |
| size_t AudioPolicyEffects::readParamValue(cnode *node, |
| char **param, |
| size_t *curSize, |
| size_t *totSize) |
| { |
| size_t len = 0; |
| size_t pos; |
| |
| if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { |
| pos = growParamSize(param, sizeof(short), curSize, totSize); |
| if (pos == 0) { |
| goto exit; |
| } |
| *(short *)(*param + pos) = (short)atoi(node->value); |
| ALOGV("readParamValue() reading short %d", *(short *)(*param + pos)); |
| len = sizeof(short); |
| } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { |
| pos = growParamSize(param, sizeof(int), curSize, totSize); |
| if (pos == 0) { |
| goto exit; |
| } |
| *(int *)(*param + pos) = atoi(node->value); |
| ALOGV("readParamValue() reading int %d", *(int *)(*param + pos)); |
| len = sizeof(int); |
| } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { |
| pos = growParamSize(param, sizeof(float), curSize, totSize); |
| if (pos == 0) { |
| goto exit; |
| } |
| *(float *)(*param + pos) = (float)atof(node->value); |
| ALOGV("readParamValue() reading float %f",*(float *)(*param + pos)); |
| len = sizeof(float); |
| } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { |
| pos = growParamSize(param, sizeof(bool), curSize, totSize); |
| if (pos == 0) { |
| goto exit; |
| } |
| if (strncmp(node->value, "true", strlen("true") + 1) == 0) { |
| *(bool *)(*param + pos) = true; |
| } else { |
| *(bool *)(*param + pos) = false; |
| } |
| ALOGV("readParamValue() reading bool %s", |
| *(bool *)(*param + pos) ? "true" : "false"); |
| len = sizeof(bool); |
| } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { |
| len = strnlen(node->value, EFFECT_STRING_LEN_MAX); |
| if (*curSize + len + 1 > *totSize) { |
| *totSize = *curSize + len + 1; |
| *param = (char *)realloc(*param, *totSize); |
| if (*param == NULL) { |
| len = 0; |
| ALOGE("%s realloc error for string len %zu", __func__, *totSize); |
| goto exit; |
| } |
| } |
| strncpy(*param + *curSize, node->value, len); |
| *curSize += len; |
| (*param)[*curSize] = '\0'; |
| ALOGV("readParamValue() reading string %s", *param + *curSize - len); |
| } else { |
| ALOGW("readParamValue() unknown param type %s", node->name); |
| } |
| exit: |
| return len; |
| } |
| |
| effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) |
| { |
| cnode *param; |
| cnode *value; |
| size_t curSize = sizeof(effect_param_t); |
| size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); |
| effect_param_t *fx_param = (effect_param_t *)malloc(totSize); |
| |
| if (fx_param == NULL) { |
| ALOGE("%s malloc error for effect structure of size %zu", |
| __func__, totSize); |
| return NULL; |
| } |
| |
| param = config_find(root, PARAM_TAG); |
| value = config_find(root, VALUE_TAG); |
| if (param == NULL && value == NULL) { |
| // try to parse simple parameter form {int int} |
| param = root->first_child; |
| if (param != NULL) { |
| // Note: that a pair of random strings is read as 0 0 |
| int *ptr = (int *)fx_param->data; |
| #if LOG_NDEBUG == 0 |
| int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); |
| ALOGV("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); |
| #endif |
| *ptr++ = atoi(param->name); |
| *ptr = atoi(param->value); |
| fx_param->psize = sizeof(int); |
| fx_param->vsize = sizeof(int); |
| return fx_param; |
| } |
| } |
| if (param == NULL || value == NULL) { |
| ALOGW("loadEffectParameter() invalid parameter description %s", |
| root->name); |
| goto error; |
| } |
| |
| fx_param->psize = 0; |
| param = param->first_child; |
| while (param) { |
| ALOGV("loadEffectParameter() reading param of type %s", param->name); |
| size_t size = |
| readParamValue(param, (char **)&fx_param, &curSize, &totSize); |
| if (size == 0) { |
| goto error; |
| } |
| fx_param->psize += size; |
| param = param->next; |
| } |
| |
| // align start of value field on 32 bit boundary |
| curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); |
| |
| fx_param->vsize = 0; |
| value = value->first_child; |
| while (value) { |
| ALOGV("loadEffectParameter() reading value of type %s", value->name); |
| size_t size = |
| readParamValue(value, (char **)&fx_param, &curSize, &totSize); |
| if (size == 0) { |
| goto error; |
| } |
| fx_param->vsize += size; |
| value = value->next; |
| } |
| |
| return fx_param; |
| |
| error: |
| free(fx_param); |
| return NULL; |
| } |
| |
| void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params) |
| { |
| cnode *node = root->first_child; |
| while (node) { |
| ALOGV("loadEffectParameters() loading param %s", node->name); |
| effect_param_t *param = loadEffectParameter(node); |
| if (param != NULL) { |
| params.add(param); |
| } |
| node = node->next; |
| } |
| } |
| |
| |
| AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig( |
| cnode *root, |
| const Vector <EffectDesc *>& effects) |
| { |
| cnode *node = root->first_child; |
| if (node == NULL) { |
| ALOGW("loadInputSource() empty element %s", root->name); |
| return NULL; |
| } |
| EffectDescVector *desc = new EffectDescVector(); |
| while (node) { |
| size_t i; |
| |
| for (i = 0; i < effects.size(); i++) { |
| if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { |
| ALOGV("loadEffectConfig() found effect %s in list", node->name); |
| break; |
| } |
| } |
| if (i == effects.size()) { |
| ALOGV("loadEffectConfig() effect %s not in list", node->name); |
| node = node->next; |
| continue; |
| } |
| EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy |
| loadEffectParameters(node, effect->mParams); |
| ALOGV("loadEffectConfig() adding effect %s uuid %08x", |
| effect->mName, effect->mUuid.timeLow); |
| desc->mEffects.add(effect); |
| node = node->next; |
| } |
| if (desc->mEffects.size() == 0) { |
| ALOGW("loadEffectConfig() no valid effects found in config %s", root->name); |
| delete desc; |
| return NULL; |
| } |
| return desc; |
| } |
| |
| status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root, |
| const Vector <EffectDesc *>& effects) |
| { |
| cnode *node = config_find(root, PREPROCESSING_TAG); |
| if (node == NULL) { |
| return -ENOENT; |
| } |
| node = node->first_child; |
| while (node) { |
| audio_source_t source = inputSourceNameToEnum(node->name); |
| if (source == AUDIO_SOURCE_CNT) { |
| ALOGW("loadInputSources() invalid input source %s", node->name); |
| node = node->next; |
| continue; |
| } |
| ALOGV("loadInputSources() loading input source %s", node->name); |
| EffectDescVector *desc = loadEffectConfig(node, effects); |
| if (desc == NULL) { |
| node = node->next; |
| continue; |
| } |
| mInputSources.add(source, desc); |
| node = node->next; |
| } |
| return NO_ERROR; |
| } |
| |
| status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, |
| const Vector <EffectDesc *>& effects) |
| { |
| cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG); |
| if (node == NULL) { |
| return -ENOENT; |
| } |
| node = node->first_child; |
| while (node) { |
| audio_stream_type_t stream = streamNameToEnum(node->name); |
| if (stream == AUDIO_STREAM_PUBLIC_CNT) { |
| ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); |
| node = node->next; |
| continue; |
| } |
| ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name); |
| EffectDescVector *desc = loadEffectConfig(node, effects); |
| if (desc == NULL) { |
| node = node->next; |
| continue; |
| } |
| mOutputStreams.add(stream, desc); |
| node = node->next; |
| } |
| return NO_ERROR; |
| } |
| |
| AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root) |
| { |
| cnode *node = config_find(root, UUID_TAG); |
| if (node == NULL) { |
| return NULL; |
| } |
| effect_uuid_t uuid; |
| if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { |
| ALOGW("loadEffect() invalid uuid %s", node->value); |
| return NULL; |
| } |
| return new EffectDesc(root->name, uuid); |
| } |
| |
| status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects) |
| { |
| cnode *node = config_find(root, EFFECTS_TAG); |
| if (node == NULL) { |
| return -ENOENT; |
| } |
| node = node->first_child; |
| while (node) { |
| ALOGV("loadEffects() loading effect %s", node->name); |
| EffectDesc *effect = loadEffect(node); |
| if (effect == NULL) { |
| node = node->next; |
| continue; |
| } |
| effects.add(effect); |
| node = node->next; |
| } |
| return NO_ERROR; |
| } |
| |
| status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) |
| { |
| cnode *root; |
| char *data; |
| |
| data = (char *)load_file(path, NULL); |
| if (data == NULL) { |
| return -ENODEV; |
| } |
| root = config_node("", ""); |
| config_load(root, data); |
| |
| Vector <EffectDesc *> effects; |
| loadEffects(root, effects); |
| loadInputEffectConfigurations(root, effects); |
| loadStreamEffectConfigurations(root, effects); |
| |
| for (size_t i = 0; i < effects.size(); i++) { |
| delete effects[i]; |
| } |
| |
| config_free(root); |
| free(root); |
| free(data); |
| |
| return NO_ERROR; |
| } |
| |
| |
| }; // namespace android |