Support effect config parser in effect AIDL
Bug: 255361653
Test: parse an example audio_effects.xml
atest VtsHalAudioEffectFactoryTargetTest
atest VtsHalAudioEffectTargetTest
atest VtsHalEqualizerTargetTest
Change-Id: I086905bcbe113a56767cae45102c84f5250d348e
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index d34d68c..2a4d663 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -81,6 +81,7 @@
],
header_libs: [
"libaudioaidl_headers",
+ "libaudio_system_headers",
"libsystem_headers",
],
cflags: [
@@ -112,13 +113,15 @@
"libhapticgeneratorsw",
"libloudnessenhancersw",
"libreverbsw",
+ "libtinyxml2",
"libvirtualizersw",
"libvisualizersw",
"libvolumesw",
],
srcs: [
- "EffectMain.cpp",
+ "EffectConfig.cpp",
"EffectFactory.cpp",
+ "EffectMain.cpp",
],
}
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
new file mode 100644
index 0000000..e1427ec
--- /dev/null
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 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 "AHAL_EffectConfig"
+#include <android-base/logging.h>
+
+#include "effectFactory-impl/EffectConfig.h"
+
+using aidl::android::media::audio::common::AudioUuid;
+
+namespace aidl::android::hardware::audio::effect {
+
+EffectConfig::EffectConfig(const std::string& file) {
+ tinyxml2::XMLDocument doc;
+ doc.LoadFile(file.c_str());
+ LOG(DEBUG) << __func__ << " loading " << file;
+ // parse the xml file into maps
+ if (doc.Error()) {
+ LOG(ERROR) << __func__ << " tinyxml2 failed to load " << file
+ << " error: " << doc.ErrorStr();
+ return;
+ }
+
+ auto registerFailure = [&](bool result) { mSkippedElements += result ? 0 : 1; };
+
+ for (auto& xmlConfig : getChildren(doc, "audio_effects_conf")) {
+ // Parse library
+ for (auto& xmlLibraries : getChildren(xmlConfig, "libraries")) {
+ for (auto& xmlLibrary : getChildren(xmlLibraries, "library")) {
+ registerFailure(parseLibrary(xmlLibrary));
+ }
+ }
+
+ // Parse effects
+ for (auto& xmlEffects : getChildren(xmlConfig, "effects")) {
+ for (auto& xmlEffect : getChildren(xmlEffects)) {
+ registerFailure(parseEffect(xmlEffect));
+ }
+ }
+
+ // Parse pre processing chains
+ for (auto& xmlPreprocess : getChildren(xmlConfig, "preprocess")) {
+ for (auto& xmlStream : getChildren(xmlPreprocess, "stream")) {
+ registerFailure(parseStream(xmlStream));
+ }
+ }
+
+ // Parse post processing chains
+ for (auto& xmlPostprocess : getChildren(xmlConfig, "postprocess")) {
+ for (auto& xmlStream : getChildren(xmlPostprocess, "stream")) {
+ registerFailure(parseStream(xmlStream));
+ }
+ }
+ }
+ LOG(DEBUG) << __func__ << " successfully parsed " << file << ", skipping " << mSkippedElements
+ << " element(s)";
+}
+
+std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> EffectConfig::getChildren(
+ const tinyxml2::XMLNode& node, const char* childTag) {
+ std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> children;
+ for (auto* child = node.FirstChildElement(childTag); child != nullptr;
+ child = child->NextSiblingElement(childTag)) {
+ children.emplace_back(*child);
+ }
+ return children;
+}
+
+bool EffectConfig::parseLibrary(const tinyxml2::XMLElement& xml) {
+ const char* name = xml.Attribute("name");
+ RETURN_VALUE_IF(!name, false, "noNameAttribute");
+ const char* path = xml.Attribute("path");
+ RETURN_VALUE_IF(!path, false, "noPathAttribute");
+
+ mLibraryMap[name] = path;
+ LOG(DEBUG) << __func__ << " " << name << " : " << path;
+ return true;
+}
+
+bool EffectConfig::parseEffect(const tinyxml2::XMLElement& xml) {
+ struct EffectLibraries effectLibraries;
+ std::vector<LibraryUuid> libraryUuids;
+ std::string name = xml.Attribute("name");
+ RETURN_VALUE_IF(name == "", false, "effectsNoName");
+
+ LOG(DEBUG) << __func__ << dump(xml);
+ struct LibraryUuid libraryUuid;
+ if (std::strcmp(xml.Name(), "effectProxy") == 0) {
+ // proxy lib and uuid
+ RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid, true), false, "parseProxyLibFailed");
+ effectLibraries.proxyLibrary = libraryUuid;
+ // proxy effect libs and UUID
+ auto xmlProxyLib = xml.FirstChildElement();
+ RETURN_VALUE_IF(!xmlProxyLib, false, "noLibForProxy");
+ while (xmlProxyLib) {
+ struct LibraryUuid tempLibraryUuid;
+ RETURN_VALUE_IF(!parseLibraryUuid(*xmlProxyLib, tempLibraryUuid), false,
+ "parseEffectLibFailed");
+ libraryUuids.push_back(std::move(tempLibraryUuid));
+ xmlProxyLib = xmlProxyLib->NextSiblingElement();
+ }
+ } else {
+ // expect only one library if not proxy
+ RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid), false, "parseEffectLibFailed");
+ libraryUuids.push_back(std::move(libraryUuid));
+ }
+
+ effectLibraries.libraries = std::move(libraryUuids);
+ mEffectsMap[name] = std::move(effectLibraries);
+ return true;
+}
+
+bool EffectConfig::parseStream(const tinyxml2::XMLElement& xml) {
+ LOG(DEBUG) << __func__ << dump(xml);
+ const char* type = xml.Attribute("type");
+ RETURN_VALUE_IF(!type, false, "noTypeInProcess");
+ RETURN_VALUE_IF(0 != mProcessingMap.count(type), false, "duplicateType");
+
+ for (auto& apply : getChildren(xml, "apply")) {
+ const char* name = apply.get().Attribute("effect");
+ RETURN_VALUE_IF(!name, false, "noEffectAttribute");
+ mProcessingMap[type].push_back(name);
+ LOG(DEBUG) << __func__ << " " << type << " : " << name;
+ }
+ return true;
+}
+
+bool EffectConfig::parseLibraryUuid(const tinyxml2::XMLElement& xml,
+ struct LibraryUuid& libraryUuid, bool isProxy) {
+ // Retrieve library name only if not effectProxy element
+ if (!isProxy) {
+ const char* name = xml.Attribute("library");
+ RETURN_VALUE_IF(!name, false, "noLibraryAttribute");
+ libraryUuid.name = name;
+ }
+
+ const char* uuid = xml.Attribute("uuid");
+ RETURN_VALUE_IF(!uuid, false, "noUuidAttribute");
+ RETURN_VALUE_IF(!stringToUuid(uuid, &libraryUuid.uuid), false, "invalidUuidAttribute");
+
+ LOG(DEBUG) << __func__ << (isProxy ? " proxy " : libraryUuid.name) << " : "
+ << libraryUuid.uuid.toString();
+ return true;
+}
+
+const char* EffectConfig::dump(const tinyxml2::XMLElement& element,
+ tinyxml2::XMLPrinter&& printer) const {
+ element.Accept(&printer);
+ return printer.CStr();
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
index edce26b..fc6a01d 100644
--- a/audio/aidl/default/include/effect-impl/EffectTypes.h
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -19,6 +19,7 @@
#include <string>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <android-base/logging.h>
typedef binder_exception_t (*EffectCreateFunctor)(
const ::aidl::android::media::audio::common::AudioUuid*,
@@ -101,4 +102,23 @@
} \
} while (0)
+static inline bool stringToUuid(const char* str,
+ ::aidl::android::media::audio::common::AudioUuid* uuid) {
+ RETURN_VALUE_IF(!uuid || !str, false, "nullPtr");
+
+ uint32_t tmp[10];
+ if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", tmp, tmp + 1, tmp + 2, tmp + 3,
+ tmp + 4, tmp + 5, tmp + 6, tmp + 7, tmp + 8, tmp + 9) < 10) {
+ return false;
+ }
+
+ uuid->timeLow = (uint32_t)tmp[0];
+ uuid->timeMid = (uint16_t)tmp[1];
+ uuid->timeHiAndVersion = (uint16_t)tmp[2];
+ uuid->clockSeq = (uint16_t)tmp[3];
+ uuid->node.insert(uuid->node.end(), {(uint8_t)tmp[4], (uint8_t)tmp[5], (uint8_t)tmp[6],
+ (uint8_t)tmp[7], (uint8_t)tmp[8], (uint8_t)tmp[9]});
+ return true;
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 767cf6c..184a587 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -15,6 +15,8 @@
*/
#pragma once
+#include <map>
+
#include <aidl/android/media/audio/common/AudioUuid.h>
namespace aidl::android::hardware::audio::effect {
@@ -39,14 +41,14 @@
0x8f34,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-// Equalizer implementation UUID.
+// 0bed4300-847d-11df-bb17-0002a5d5c51b
static const AudioUuid EqualizerSwImplUUID = {static_cast<int32_t>(0x0bed4300),
0x847d,
0x11df,
0xbb17,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-// Equalizer bundle implementation UUID.
+// ce772f20-847d-11df-bb17-0002a5d5c51b
static const AudioUuid EqualizerBundleImplUUID = {static_cast<int32_t>(0xce772f20),
0x847d,
0x11df,
@@ -166,4 +168,26 @@
0x9b6a,
{0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+/**
+ * @brief A map between effect name and effect type UUID.
+ * All <name> attribution in effect/effectProxy of audio_effects.xml should be listed in this map.
+ * We need this map is because existing audio_effects.xml don't have a type UUID defined.
+ */
+static const std::map<const std::string /* effect type */, const AudioUuid&> kUuidNameTypeMap = {
+ {"bassboost", BassBoostTypeUUID},
+ {"downmix", DownmixTypeUUID},
+ {"dynamics_processing", DynamicsProcessingTypeUUID},
+ {"equalizer", EqualizerTypeUUID},
+ {"haptic_generator", HapticGeneratorTypeUUID},
+ {"loudness_enhancer", LoudnessEnhancerTypeUUID},
+ {"reverb", ReverbTypeUUID},
+ {"reverb_env_aux", ReverbTypeUUID},
+ {"reverb_env_ins", ReverbTypeUUID},
+ {"reverb_pre_aux", ReverbTypeUUID},
+ {"reverb_pre_ins", ReverbTypeUUID},
+ {"virtualizer", VirtualizerTypeUUID},
+ {"visualizer", VisualizerTypeUUID},
+ {"volume", VolumeTypeUUID},
+};
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
new file mode 100644
index 0000000..2b904f5
--- /dev/null
+++ b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <cutils/properties.h>
+#include <tinyxml2.h>
+
+#include "effect-impl/EffectTypes.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+/**
+ * Library contains a mapping from library name to path.
+ * Effect contains a mapping from effect name to Libraries and implementation UUID.
+ * Pre/post processor contains a mapping from processing name to effect names.
+ */
+class EffectConfig {
+ public:
+ explicit EffectConfig(const std::string& file);
+
+ // <library>
+ struct Library {
+ std::string name;
+ std::string path;
+ };
+ struct LibraryUuid {
+ std::string name; // library name
+ ::aidl::android::media::audio::common::AudioUuid uuid;
+ };
+ // <effects>
+ struct EffectLibraries {
+ std::optional<struct LibraryUuid> proxyLibrary;
+ std::vector<struct LibraryUuid> libraries;
+ };
+
+ int getSkippedElements() const { return mSkippedElements; }
+ const std::unordered_map<std::string, std::string> getLibraryMap() const { return mLibraryMap; }
+ const std::unordered_map<std::string, struct EffectLibraries> getEffectsMap() const {
+ return mEffectsMap;
+ }
+ const std::unordered_map<std::string, std::vector<std::string>> getProcessingMap() const {
+ return mProcessingMap;
+ }
+
+ private:
+ int mSkippedElements;
+ /* Parsed Libraries result */
+ std::unordered_map<std::string, std::string> mLibraryMap;
+ /* Parsed Effects result */
+ std::unordered_map<std::string, struct EffectLibraries> mEffectsMap;
+ /* Parsed pre/post processing result */
+ std::unordered_map<std::string, std::vector<std::string>> mProcessingMap;
+
+ /** @return all `node`s children that are elements and match the tag if provided. */
+ std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> getChildren(
+ const tinyxml2::XMLNode& node, const char* childTag = nullptr);
+
+ /** Parse a library xml note and push the result in mLibraryMap or return false on failure. */
+ bool parseLibrary(const tinyxml2::XMLElement& xml);
+
+ /** Parse an effect from an xml element describing it.
+ * @return true and pushes the effect in mEffectsMap on success, false on failure.
+ */
+ bool parseEffect(const tinyxml2::XMLElement& xml);
+
+ bool parseStream(const tinyxml2::XMLElement& xml);
+
+ // Function to parse effect.library name and effect.uuid from xml
+ bool parseLibraryUuid(const tinyxml2::XMLElement& xml, struct LibraryUuid& libraryUuid,
+ bool isProxy = false);
+
+ const char* dump(const tinyxml2::XMLElement& element,
+ tinyxml2::XMLPrinter&& printer = {}) const;
+};
+
+} // namespace aidl::android::hardware::audio::effect