frameworks: av: services: audiopolicy: Added AudioPolicyEffects

Manage pre- and postprocessing effects defined in audio_effects.conf

Change-Id: Idea1478e3d434f8ff66907adb3c77663db78d583
diff --git a/services/audiopolicy/AudioPolicyEffects.cpp b/services/audiopolicy/AudioPolicyEffects.cpp
new file mode 100755
index 0000000..185e1cc
--- /dev/null
+++ b/services/audiopolicy/AudioPolicyEffects.cpp
@@ -0,0 +1,638 @@
+/*
+ * 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,
+                             int 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;
+
+    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 {
+        inputDesc = mInputs.valueAt(idx);
+    }
+
+    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, &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);
+    }
+    setProcessorEnabled(inputDesc, true);
+
+    return status;
+}
+
+
+status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input)
+{
+    status_t status = NO_ERROR;
+
+    ssize_t index = mInputs.indexOfKey(input);
+    if (index < 0) {
+        return status;
+    }
+    EffectVector *inputDesc = mInputs.valueAt(index);
+    setProcessorEnabled(inputDesc, false);
+    delete inputDesc;
+    mInputs.removeItemsAt(index);
+    ALOGV("releaseInputEffects(): all effects released");
+    return status;
+}
+
+status_t AudioPolicyEffects::queryDefaultInputEffects(int audioSession,
+                                                      effect_descriptor_t *descriptors,
+                                                      uint32_t *count)
+{
+    status_t status = NO_ERROR;
+
+    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(int audioSession,
+                         effect_descriptor_t *descriptors,
+                         uint32_t *count)
+{
+    status_t status = NO_ERROR;
+
+    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,
+                         int audioSession)
+{
+    status_t status = NO_ERROR;
+
+    // create audio processors according to stream
+    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 {
+        procDesc = mOutputSessions.valueAt(idx);
+    }
+
+    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, &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);
+    }
+
+    setProcessorEnabled(procDesc, true);
+
+    return status;
+}
+
+status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output,
+                         audio_stream_type_t stream,
+                         int audioSession)
+{
+    status_t status = NO_ERROR;
+    (void) output; // argument not used for now
+    (void) stream; // argument not used for now
+
+    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);
+    setProcessorEnabled(procDesc, false);
+    procDesc->mEffects.clear();
+    delete procDesc;
+    mOutputSessions.removeItemsAt(index);
+    ALOGV("releaseOutputSessionEffects(): output processing released from session: %d",
+          audioSession);
+    return status;
+}
+
+
+void AudioPolicyEffects::setProcessorEnabled(const EffectVector *effectVector, bool enabled)
+{
+    const Vector<sp<AudioEffect> > &fxVector = effectVector->mEffects;
+    for (size_t i = 0; i < fxVector.size(); i++) {
+        fxVector.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
+};
+
+// returns the audio_source_t enum corresponding to the input source name or
+// AUDIO_SOURCE_CNT is no match found
+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_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_CNT is no match found
+audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name)
+{
+    int i;
+    for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_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);
+    }
+    *curSize = pos + size;
+    return pos;
+}
+
+size_t AudioPolicyEffects::readParamValue(cnode *node,
+                                          char *param,
+                                          size_t *curSize,
+                                          size_t *totSize)
+{
+    if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(short), curSize, totSize);
+        *(short *)((char *)param + pos) = (short)atoi(node->value);
+        ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos));
+        return sizeof(short);
+    } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(int), curSize, totSize);
+        *(int *)((char *)param + pos) = atoi(node->value);
+        ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos));
+        return sizeof(int);
+    } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(float), curSize, totSize);
+        *(float *)((char *)param + pos) = (float)atof(node->value);
+        ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos));
+        return sizeof(float);
+    } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(bool), curSize, totSize);
+        if (strncmp(node->value, "false", strlen("false") + 1) == 0) {
+            *(bool *)((char *)param + pos) = false;
+        } else {
+            *(bool *)((char *)param + pos) = true;
+        }
+        ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false");
+        return sizeof(bool);
+    } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) {
+        size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
+        if (*curSize + len + 1 > *totSize) {
+            *totSize = *curSize + len + 1;
+            param = (char *)realloc(param, *totSize);
+        }
+        strncpy(param + *curSize, node->value, len);
+        *curSize += len;
+        param[*curSize] = '\0';
+        ALOGV("readParamValue() reading string %s", param + *curSize - len);
+        return len;
+    }
+    ALOGW("readParamValue() unknown param type %s", node->name);
+    return 0;
+}
+
+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);
+
+    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;
+            int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
+            ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2);
+            *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:
+    delete 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) {
+            node = node->next;
+            continue;
+        }
+        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_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);
+
+    config_free(root);
+    free(root);
+    free(data);
+
+    return NO_ERROR;
+}
+
+
+}; // namespace android