Add Spatializer support in libAudioHal
Bug: 273373363
Test: atest EffectsFactoryHalInterfaceTest
Change-Id: I68bfc8388dac0f070a58351bc1d8836f8c50fc6c
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index c4841c5..0c0184e 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -69,9 +69,6 @@
void* pReplyData);
private:
- const aidl::android::media::audio::common::AudioFormatDescription kDefaultFormatDescription = {
- .type = aidl::android::media::audio::common::AudioFormatType::PCM,
- .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT};
const bool mIsProxyEffect;
static constexpr int kDefaultframeCount = 0x100;
@@ -81,13 +78,16 @@
return pt ? std::to_string(*pt) : "nullptr";
}
- using AudioChannelLayout = aidl::android::media::audio::common::AudioChannelLayout;
const aidl::android::media::audio::common::AudioConfig kDefaultAudioConfig = {
.base = {.sampleRate = 44100,
- .channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
- AudioChannelLayout::LAYOUT_STEREO),
- .format = kDefaultFormatDescription},
+ .channelMask = aidl::android::media::audio::common::AudioChannelLayout::make<
+ aidl::android::media::audio::common::AudioChannelLayout::layoutMask>(
+ aidl::android::media::audio::common::AudioChannelLayout::
+ LAYOUT_STEREO),
+ .format = {.type = aidl::android::media::audio::common::AudioFormatType::PCM,
+ .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT}},
.frameCount = kDefaultframeCount};
+
// command handler map
typedef status_t (EffectConversionHelperAidl::*CommandHandler)(uint32_t /* cmdSize */,
const void* /* pCmdData */,
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
index 49e6827..d1794f0 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
@@ -16,17 +16,17 @@
#include <cstdint>
#include <cstring>
-#include <optional>
#define LOG_TAG "AidlConversionSpatializer"
//#define LOG_NDEBUG 0
#include <aidl/android/hardware/audio/effect/DefaultExtension.h>
#include <aidl/android/hardware/audio/effect/VendorExtension.h>
#include <error/expected_utils.h>
-#include <media/AidlConversionNdk.h>
+#include <media/AidlConversionCppNdk.h>
#include <media/AidlConversionEffect.h>
+#include <media/AidlConversionNdk.h>
+#include <system/audio_effects/aidl_effects_utils.h>
#include <system/audio_effects/effect_spatializer.h>
-
#include <utils/Log.h>
#include "AidlConversionSpatializer.h"
@@ -34,38 +34,321 @@
namespace android {
namespace effect {
-using ::aidl::android::aidl_utils::statusTFromBinderStatus;
-using ::aidl::android::hardware::audio::effect::DefaultExtension;
-using ::aidl::android::hardware::audio::effect::Parameter;
-using ::aidl::android::hardware::audio::effect::VendorExtension;
-using ::android::status_t;
+using aidl::android::getParameterSpecificField;
+using aidl::android::aidl_utils::statusTFromBinderStatus;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::effect::DefaultExtension;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
+using aidl::android::hardware::audio::effect::Spatializer;
+using aidl::android::hardware::audio::effect::VendorExtension;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::HeadTracking;
+using aidl::android::media::audio::common::Spatialization;
+using aidl::android::media::audio::common::toString;
+using android::status_t;
using utils::EffectParamReader;
using utils::EffectParamWriter;
+bool AidlConversionSpatializer::isSpatializerParameterSupported() {
+ return mIsSpatializerAidlParamSupported.value_or(
+ (mIsSpatializerAidlParamSupported =
+ [&]() {
+ ::aidl::android::hardware::audio::effect::Parameter aidlParam;
+ auto id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::vendor);
+ // No range defined in descriptor capability means no Spatializer AIDL
+ // implementation BAD_VALUE return from getParameter indicates the
+ // parameter is not supported by HAL
+ return mDesc.capability.range.getTag() == Range::spatializer &&
+ mEffect->getParameter(id, &aidlParam).getStatus() !=
+ android::BAD_VALUE;
+ }())
+ .value());
+}
+
status_t AidlConversionSpatializer::setParameter(EffectParamReader& param) {
- Parameter aidlParam = VALUE_OR_RETURN_STATUS(
- ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+ Parameter aidlParam;
+ if (isSpatializerParameterSupported()) {
+ uint32_t command = 0;
+ if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+ OK != param.readFromParameter(&command)) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ switch (command) {
+ case SPATIALIZER_PARAM_LEVEL: {
+ Spatialization::Level level = Spatialization::Level::NONE;
+ if (OK != param.readFromValue(&level)) {
+ ALOGE("%s invalid level value %s", __func__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ spatializationLevel, level);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+ HeadTracking::Mode mode = HeadTracking::Mode::DISABLED;
+ if (OK != param.readFromValue(&mode)) {
+ ALOGE("%s invalid mode value %s", __func__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingMode,
+ mode);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEAD_TO_STAGE: {
+ const size_t valueSize = param.getValueSize();
+ if (valueSize / sizeof(float) > 6 || valueSize % sizeof(float) != 0) {
+ ALOGE("%s invalid parameter value size %zu", __func__, valueSize);
+ return BAD_VALUE;
+ }
+ std::array<float, 6> headToStage = {};
+ for (size_t i = 0; i < valueSize / sizeof(float); i++) {
+ if (OK != param.readFromValue(&headToStage[i])) {
+ ALOGE("%s failed to read headToStage from %s", __func__,
+ param.toString().c_str());
+ return BAD_VALUE;
+ }
+ }
+ HeadTracking::SensorData sensorData =
+ HeadTracking::SensorData::make<HeadTracking::SensorData::headToStage>(
+ headToStage);
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ headTrackingSensorData, sensorData);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+ int32_t modeInt32 = 0;
+ int32_t sensorId = -1;
+ if (OK != param.readFromValue(&modeInt32) || OK != param.readFromValue(&sensorId)) {
+ ALOGE("%s %d invalid parameter value %s", __func__, __LINE__,
+ param.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ const auto mode = static_cast<HeadTracking::ConnectionMode>(modeInt32);
+ if (mode < *ndk::enum_range<HeadTracking::ConnectionMode>().begin() ||
+ mode > *ndk::enum_range<HeadTracking::ConnectionMode>().end()) {
+ ALOGE("%s %d invalid mode %d", __func__, __LINE__, modeInt32);
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ headTrackingConnectionMode, mode);
+ if (status_t status = statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+ status != OK) {
+ ALOGE("%s failed to set headTrackingConnectionMode %s", __func__,
+ toString(mode).c_str());
+ return status;
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingSensorId,
+ sensorId);
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+ }
+ default: {
+ ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+ return BAD_VALUE;
+ }
+ }
+ } else {
+ aidlParam = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+ }
+
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
}
status_t AidlConversionSpatializer::getParameter(EffectParamWriter& param) {
- DefaultExtension defaultExt;
- // read parameters into DefaultExtension vector<uint8_t>
- defaultExt.bytes.resize(param.getParameterSize());
- if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
- ALOGE("%s invalid param %s", __func__, param.toString().c_str());
- param.setStatus(BAD_VALUE);
- return BAD_VALUE;
- }
+ if (isSpatializerParameterSupported()) {
+ uint32_t command = 0;
+ if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+ OK != param.readFromParameter(&command)) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ return BAD_VALUE;
+ }
- VendorExtension idTag;
- idTag.extension.setParcelable(defaultExt);
- Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
- Parameter aidlParam;
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
- // copy the AIDL extension data back to effect_param_t
- return VALUE_OR_RETURN_STATUS(
- ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+ switch (command) {
+ case SPATIALIZER_PARAM_SUPPORTED_LEVELS: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationLevel);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto level : ::ndk::enum_range<Spatialization::Level>()) {
+ const auto spatializer =
+ Spatializer::make<Spatializer::spatializationLevel>(level);
+ if (spatializer >= range->min && spatializer <= range->max) {
+ if (status_t status = param.writeToValue(&level); status != OK) {
+ ALOGI("%s %d: write level %s to value failed %d", __func__, __LINE__,
+ toString(level).c_str(), status);
+ return status;
+ }
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_LEVEL: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::spatializationLevel);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto level = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::spatializationLevel,
+ Spatialization::Level));
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return param.writeToValue(&level);
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationLevel);
+ if (!range) {
+ ALOGE("%s %d: range not defined for spatializationLevel", __func__, __LINE__);
+ return BAD_VALUE;
+ }
+ const auto& nonSupport = Spatializer::make<Spatializer::spatializationLevel>(
+ Spatialization::Level::NONE);
+ const bool support = (range->min > range->max ||
+ (range->min == nonSupport && range->max == nonSupport))
+ ? false
+ : true;
+ return param.writeToValue(&support);
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::headTrackingMode);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::headTrackingMode,
+ HeadTracking::Mode));
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return param.writeToValue(&mode);
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::supportedChannelLayout);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto& supportedLayouts = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::supportedChannelLayout,
+ std::vector<AudioChannelLayout>));
+ for (const auto& layout : supportedLayouts) {
+ audio_channel_mask_t mask = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ layout, false /* isInput */));
+ if (status_t status = param.writeToValue(&mask); status != OK) {
+ ALOGI("%s %d: write mask %s to value failed %d", __func__, __LINE__,
+ layout.toString().c_str(), status);
+ return status;
+ }
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return OK;
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationMode);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto mode : ::ndk::enum_range<Spatialization::Mode>()) {
+ if (const auto spatializer =
+ Spatializer::make<Spatializer::spatializationMode>(mode);
+ spatializer >= range->min && spatializer <= range->max) {
+ if (status_t status = param.writeToValue(&mode); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::headTrackingConnectionMode);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto mode : ::ndk::enum_range<HeadTracking::ConnectionMode>()) {
+ if (const auto spatializer =
+ Spatializer::make<Spatializer::headTrackingConnectionMode>(mode);
+ spatializer < range->min || spatializer > range->max) {
+ continue;
+ }
+ if (status_t status = param.writeToValue(&mode); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+ status_t status = OK;
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(
+ Spatializer, spatializerTag, Spatializer::headTrackingConnectionMode);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer,
+ Spatializer::headTrackingConnectionMode, HeadTracking::ConnectionMode));
+
+ id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::headTrackingSensorId);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto sensorId = VALUE_OR_RETURN_STATUS(
+ GET_PARAMETER_SPECIFIC_FIELD(aidlParam, Spatializer, spatializer,
+ Spatializer::headTrackingSensorId, int32_t));
+ uint32_t modeInt32 = static_cast<int32_t>(mode);
+ if (status = param.writeToValue(&modeInt32); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ if (status = param.writeToValue(&sensorId); status != OK) {
+ ALOGI("%s %d: write sensorId %d to value failed %d", __func__, __LINE__,
+ sensorId, status);
+ return status;
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return OK;
+ }
+ default: {
+ ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+ return BAD_VALUE;
+ }
+ }
+ } else {
+ Parameter aidlParam;
+ DefaultExtension defaultExt;
+ // read parameters into DefaultExtension vector<uint8_t>
+ defaultExt.bytes.resize(param.getParameterSize());
+ if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ param.setStatus(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ VendorExtension idTag;
+ idTag.extension.setParcelable(defaultExt);
+ Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ ALOGI("%s %d: %s", __func__, __LINE__,
+ aidlParam.get<Parameter::specific>().toString().c_str());
+ // copy the AIDL extension data back to effect_param_t
+ return VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+ }
}
} // namespace effect
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
index 7c60b14..444e5a7 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
@@ -32,6 +32,8 @@
~AidlConversionSpatializer() {}
private:
+ std::optional<bool> mIsSpatializerAidlParamSupported;
+ bool isSpatializerParameterSupported();
status_t setParameter(utils::EffectParamReader& param) override;
status_t getParameter(utils::EffectParamWriter& param) override;
};
diff --git a/media/libaudiohal/tests/Android.bp b/media/libaudiohal/tests/Android.bp
index 97510d6..1a54500 100644
--- a/media/libaudiohal/tests/Android.bp
+++ b/media/libaudiohal/tests/Android.bp
@@ -49,6 +49,7 @@
shared_libs: [
"libvibrator",
],
+ test_config_template: "AudioHalTestTemplate.xml",
}
cc_test {
diff --git a/media/libaudiohal/tests/AudioHalTestTemplate.xml b/media/libaudiohal/tests/AudioHalTestTemplate.xml
new file mode 100644
index 0000000..b1cb2f0
--- /dev/null
+++ b/media/libaudiohal/tests/AudioHalTestTemplate.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+<configuration description="Runs {MODULE}.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="native-test-timeout" value="10m" />
+ </test>
+</configuration>
diff --git a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
index 0cb654c..d783c64 100644
--- a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
+++ b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
@@ -21,10 +21,13 @@
#include <cstdint>
#include <cstring>
#include <memory>
+#include <string>
#include <utility>
#define LOG_TAG "EffectsFactoryHalInterfaceTest"
#include <aidl/android/media/audio/common/AudioUuid.h>
+#include <android/media/audio/common/HeadTracking.h>
+#include <android/media/audio/common/Spatialization.h>
#include <gtest/gtest.h>
#include <media/AidlConversionCppNdk.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -40,15 +43,18 @@
#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_loudnessenhancer.h>
#include <system/audio_effects/effect_ns.h>
+#include <system/audio_effects/effect_spatializer.h>
#include <utils/RefBase.h>
#include <vibrator/ExternalVibrationUtils.h>
namespace android {
-using ::aidl::android::media::audio::common::AudioUuid;
-using ::android::audio::utils::toString;
+using aidl::android::media::audio::common::AudioUuid;
+using android::audio::utils::toString;
using effect::utils::EffectParamReader;
using effect::utils::EffectParamWriter;
+using media::audio::common::HeadTracking;
+using media::audio::common::Spatialization;
// EffectsFactoryHalInterface
TEST(libAudioHalTest, createEffectsFactoryHalInterface) {
@@ -144,34 +150,68 @@
EXPECT_NE(0, version.getMajorVersion());
}
+enum ParamSetGetType { SET_N_GET, SET_ONLY, GET_ONLY };
class EffectParamCombination {
public:
template <typename P, typename V>
- void init(const P& p, const V& v, size_t len) {
- setBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4);
- getBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
- expectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
- parameterSet =
- std::make_shared<EffectParamReader>(createEffectParam(setBuffer.data(), p, v));
- parameterGet =
- std::make_shared<EffectParamReader>(createEffectParam(getBuffer.data(), p, v));
- parameterExpect =
- std::make_shared<EffectParamReader>(createEffectParam(expectBuffer.data(), p, v));
- valueSize = len;
+ void init(const P& p, const V& v, size_t len, ParamSetGetType type) {
+ if (type != GET_ONLY) {
+ mSetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4);
+ mParameterSet =
+ std::make_shared<EffectParamReader>(createEffectParam(mSetBuffer.data(), p, v));
+ }
+
+ if (type != SET_ONLY) {
+ mGetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
+ mExpectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
+ mParameterGet =
+ std::make_shared<EffectParamReader>(createEffectParam(mGetBuffer.data(), p, v));
+ mParameterExpect = std::make_shared<EffectParamReader>(
+ createEffectParam(mExpectBuffer.data(), p, v));
+ mValueSize = len;
+ }
+ mType = type;
}
- std::shared_ptr<EffectParamReader> parameterSet; /* setParameter */
- std::shared_ptr<EffectParamReader> parameterGet; /* getParameter */
- std::shared_ptr<EffectParamReader> parameterExpect; /* expected from getParameter */
- size_t valueSize; /* ValueSize expect to write in reply data buffer */
+ std::shared_ptr<EffectParamReader> mParameterSet; /* setParameter */
+ std::shared_ptr<EffectParamReader> mParameterGet; /* getParameter */
+ std::shared_ptr<EffectParamReader> mParameterExpect; /* expected from getParameter */
+ size_t mValueSize = 0ul; /* ValueSize expect to write in reply data buffer */
+ ParamSetGetType mType = SET_N_GET;
+
+ std::string toString() {
+ uint32_t command = 0;
+ std::string str = "Command: ";
+ if (mType != GET_ONLY) {
+ str += (OK == mParameterSet->readFromParameter(&command) ? std::to_string(command)
+ : mParameterSet->toString());
+ } else {
+ str += (OK == mParameterGet->readFromParameter(&command) ? std::to_string(command)
+ : mParameterSet->toString());
+ }
+ str += "_";
+ str += toString(mType);
+ return str;
+ }
+
+ static std::string toString(ParamSetGetType type) {
+ switch (type) {
+ case SET_N_GET:
+ return "Type:SetAndGet";
+ case SET_ONLY:
+ return "Type:SetOnly";
+ case GET_ONLY:
+ return "Type:GetOnly";
+ }
+ }
private:
- std::vector<uint8_t> setBuffer;
- std::vector<uint8_t> getBuffer;
- std::vector<uint8_t> expectBuffer;
+ std::vector<uint8_t> mSetBuffer;
+ std::vector<uint8_t> mGetBuffer;
+ std::vector<uint8_t> mExpectBuffer;
template <typename P, typename V>
- EffectParamReader createEffectParam(void* buf, const P& p, const V& v) {
+ static EffectParamReader createEffectParam(void* buf, const P& p, const V& v) {
effect_param_t* paramRet = (effect_param_t*)buf;
paramRet->psize = sizeof(P);
paramRet->vsize = sizeof(V);
@@ -184,48 +224,106 @@
};
template <typename P, typename V>
-std::shared_ptr<EffectParamCombination> createEffectParamCombination(const P& p, const V& v,
- size_t len) {
+std::shared_ptr<EffectParamCombination> createEffectParamCombination(
+ const P& p, const V& v, size_t len, ParamSetGetType type = SET_N_GET) {
auto comb = std::make_shared<EffectParamCombination>();
- comb->init(p, v, len);
+ comb->init(p, v, len, type);
return comb;
}
-enum ParamName { TUPLE_UUID, TUPLE_PARAM_COMBINATION };
-using EffectParamTestTuple =
- std::tuple<const effect_uuid_t* /* type UUID */, std::shared_ptr<EffectParamCombination>>;
-
+enum ParamName { TUPLE_UUID, TUPLE_IS_INPUT, TUPLE_PARAM_COMBINATION };
+using EffectParamTestTuple = std::tuple<const effect_uuid_t* /* type UUID */, bool /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>>;
static const effect_uuid_t EXTEND_EFFECT_TYPE_UUID = {
0xfa81dbde, 0x588b, 0x11ed, 0x9b6a, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
constexpr std::array<uint8_t, 10> kVendorExtensionData({0xff, 0x5, 0x50, 0xab, 0xcd, 0x00, 0xbd,
0xdb, 0xee, 0xff});
-std::vector<EffectParamTestTuple> testPairs = {
- std::make_tuple(FX_IID_AEC,
- createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(FX_IID_AGC,
- createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(SL_IID_BASSBOOST,
- createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(EFFECT_UIID_DOWNMIX,
- createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(SL_IID_DYNAMICSPROCESSING,
- createEffectParamCombination(
- std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}),
- 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)),
+static std::vector<EffectParamTestTuple> testPairs = {
std::make_tuple(
- FX_IID_LOUDNESS_ENHANCER,
- createEffectParamCombination(LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(FX_IID_NS,
- createEffectParamCombination(NS_PARAM_LEVEL, 1 /* level */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(&EXTEND_EFFECT_TYPE_UUID,
- createEffectParamCombination(8, kVendorExtensionData,
- sizeof(kVendorExtensionData)))};
+ FX_IID_AEC, true /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */,
+ sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_AGC, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ SL_IID_BASSBOOST, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ EFFECT_UIID_DOWNMIX, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ SL_IID_DYNAMICSPROCESSING, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}),
+ 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_LOUDNESS_ENHANCER, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */,
+ sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_NS, true /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ NS_PARAM_LEVEL, 1 /* level */, sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_SPATIALIZER, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(SPATIALIZER_PARAM_LEVEL,
+ SPATIALIZATION_LEVEL_MULTICHANNEL,
+ sizeof(uint8_t), SET_N_GET),
+ createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_MODE,
+ HeadTracking::Mode::RELATIVE_WORLD,
+ sizeof(uint8_t), SET_N_GET),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_HEAD_TO_STAGE,
+ std::array<float, 6>{.55f, 0.2f, 1.f, .999f, .43f, 19.f},
+ sizeof(std::array<float, 6>), SET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_HEADTRACKING_CONNECTION,
+ std::array<uint32_t, 2>{
+ static_cast<uint32_t>(HeadTracking::ConnectionMode::
+ DIRECT_TO_SENSOR_TUNNEL),
+ 0x5e /* sensorId */},
+ sizeof(std::array<uint32_t, 2>), SET_N_GET),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_LEVELS,
+ std::array<Spatialization::Level, 3>{
+ Spatialization::Level::NONE,
+ Spatialization::Level::MULTICHANNEL,
+ Spatialization::Level::BED_PLUS_OBJECTS},
+ sizeof(std::array<uint8_t, 3>), GET_ONLY),
+ createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED, true,
+ sizeof(bool), GET_ONLY),
+ createEffectParamCombination(SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS,
+ AUDIO_CHANNEL_OUT_5POINT1, sizeof(uint8_t),
+ GET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES,
+ std::array<Spatialization::Mode, 2>{
+ Spatialization::Mode::BINAURAL,
+ Spatialization::Mode::TRANSAURAL},
+ sizeof(std::array<uint8_t, 2>), GET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION,
+ std::array<HeadTracking::ConnectionMode, 3>{
+ HeadTracking::ConnectionMode::FRAMEWORK_PROCESSED,
+ HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_SW,
+ HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_TUNNEL},
+ sizeof(std::array<uint8_t, 3>), GET_ONLY),
+ }),
+ std::make_tuple(
+ &EXTEND_EFFECT_TYPE_UUID, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ uint32_t{8}, kVendorExtensionData, sizeof(kVendorExtensionData))}),
+};
class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTestTuple> {
public:
@@ -233,13 +331,8 @@
: mParamTuple(GetParam()),
mFactory(EffectsFactoryHalInterface::create()),
mTypeUuid(std::get<TUPLE_UUID>(mParamTuple)),
- mCombination(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)),
- mExpectedValue([&]() {
- std::vector<uint8_t> expectData(mCombination->valueSize);
- mCombination->parameterExpect->readFromValue(expectData.data(),
- mCombination->valueSize);
- return expectData;
- }()),
+ mCombinations(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)),
+ mIsInput(std::get<TUPLE_IS_INPUT>(mParamTuple)),
mDescs([&]() {
std::vector<effect_descriptor_t> descs;
if (mFactory && mTypeUuid && OK == mFactory->getDescriptors(mTypeUuid, &descs)) {
@@ -263,7 +356,8 @@
uint32_t reply = 0;
uint32_t replySize = sizeof(reply);
ASSERT_EQ(OK, interface->command(EFFECT_CMD_INIT, 0, nullptr, &replySize, &reply));
- ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(mEffectConfig),
+
+ ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
&mEffectConfig, &replySize, &reply));
}
@@ -284,60 +378,85 @@
}
void setAndGetParameter(const sp<EffectHalInterface>& interface) {
- uint32_t replySize = sizeof(uint32_t);
- uint8_t reply[replySize];
- auto parameterSet = mCombination->parameterSet;
- ASSERT_EQ(OK,
- interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)parameterSet->getTotalSize(),
- const_cast<effect_param_t*>(¶meterSet->getEffectParam()),
- &replySize, &reply))
- << parameterSet->toString();
- ASSERT_EQ(replySize, sizeof(uint32_t));
+ for (const auto combination : mCombinations) {
+ uint32_t replySize = kSetParamReplySize;
+ uint8_t reply[replySize];
+ const auto type = combination->mType;
+ if (type != GET_ONLY) {
+ const auto& set = combination->mParameterSet;
+ ASSERT_EQ(OK,
+ interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)set->getTotalSize(),
+ const_cast<effect_param_t*>(&set->getEffectParam()),
+ &replySize, &reply))
+ << set->toString();
+ ASSERT_EQ(replySize, kSetParamReplySize);
+ }
- effect_param_t* getParam =
- const_cast<effect_param_t*>(&mCombination->parameterGet->getEffectParam());
- size_t maxReplySize = mCombination->valueSize + sizeof(effect_param_t) +
- sizeof(parameterSet->getPaddedParameterSize());
- replySize = maxReplySize;
- EXPECT_EQ(OK,
- interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)parameterSet->getTotalSize(),
- const_cast<effect_param_t*>(¶meterSet->getEffectParam()),
- &replySize, getParam));
- EffectParamReader parameterGet(*getParam);
- EXPECT_EQ(replySize, parameterGet.getTotalSize()) << parameterGet.toString();
- if (mCombination->valueSize) {
- std::vector<uint8_t> response(mCombination->valueSize);
- EXPECT_EQ(OK, parameterGet.readFromValue(response.data(), mCombination->valueSize))
- << " try get valueSize " << mCombination->valueSize << " from "
- << parameterGet.toString() << " set " << parameterSet->toString();
- EXPECT_EQ(response, mExpectedValue);
+ if (type != SET_ONLY) {
+ auto get = combination->mParameterGet;
+ auto expect = combination->mParameterExpect;
+ effect_param_t* getParam = const_cast<effect_param_t*>(&get->getEffectParam());
+ size_t maxReplySize = combination->mValueSize + sizeof(effect_param_t) +
+ sizeof(expect->getPaddedParameterSize());
+ replySize = maxReplySize;
+ EXPECT_EQ(OK,
+ interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)expect->getTotalSize(),
+ const_cast<effect_param_t*>(&expect->getEffectParam()),
+ &replySize, getParam));
+
+ EffectParamReader getReader(*getParam);
+ EXPECT_EQ(replySize, getReader.getTotalSize()) << getReader.toString();
+ if (combination->mValueSize) {
+ std::vector<uint8_t> expectedData(combination->mValueSize);
+ EXPECT_EQ(OK, expect->readFromValue(expectedData.data(), expectedData.size()))
+ << combination->toString();
+ std::vector<uint8_t> response(combination->mValueSize);
+ EXPECT_EQ(OK, getReader.readFromValue(response.data(), combination->mValueSize))
+ << " try get valueSize " << combination->mValueSize << " from:\n"
+ << getReader.toString() << "\nexpect:\n"
+ << expect->toString();
+ EXPECT_EQ(expectedData, response) << combination->toString();
+ }
+ }
}
}
+ static constexpr size_t kSetParamReplySize = sizeof(uint32_t);
const EffectParamTestTuple mParamTuple;
const sp<EffectsFactoryHalInterface> mFactory;
const effect_uuid_t* mTypeUuid;
- std::shared_ptr<EffectParamCombination> mCombination;
- const std::vector<uint8_t> mExpectedValue;
+ std::vector<std::shared_ptr<EffectParamCombination>> mCombinations{};
+ const bool mIsInput;
const std::vector<effect_descriptor_t> mDescs;
- std::vector<sp<EffectHalInterface>> mHalInterfaces;
- effect_config_t mEffectConfig = {.inputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_READ,
- .format = AUDIO_FORMAT_PCM_FLOAT,
- .bufferProvider.getBuffer = nullptr,
- .bufferProvider.releaseBuffer = nullptr,
- .bufferProvider.cookie = nullptr,
- .mask = EFFECT_CONFIG_ALL,
- .samplingRate = 48000,
- .channels = AUDIO_CHANNEL_IN_STEREO},
-
- .outputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_WRITE,
- .format = AUDIO_FORMAT_PCM_FLOAT,
- .bufferProvider.getBuffer = nullptr,
- .bufferProvider.releaseBuffer = nullptr,
- .bufferProvider.cookie = nullptr,
- .mask = EFFECT_CONFIG_ALL,
- .samplingRate = 48000,
- .channels = AUDIO_CHANNEL_OUT_STEREO}};
+ std::vector<sp<EffectHalInterface>> mHalInterfaces{};
+ effect_config_t mEffectConfig = {
+ .inputCfg =
+ {
+ .buffer = {.frameCount = 0x100},
+ .samplingRate = 48000,
+ .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO
+ : AUDIO_CHANNEL_IN_STEREO,
+ .bufferProvider = {.getBuffer = nullptr,
+ .releaseBuffer = nullptr,
+ .cookie = nullptr},
+ .format = AUDIO_FORMAT_PCM_FLOAT,
+ .accessMode = EFFECT_BUFFER_ACCESS_READ,
+ .mask = EFFECT_CONFIG_ALL,
+ },
+ .outputCfg =
+ {
+ .buffer = {.frameCount = 0x100},
+ .samplingRate = 48000,
+ .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO
+ : AUDIO_CHANNEL_OUT_STEREO,
+ .bufferProvider = {.getBuffer = nullptr,
+ .releaseBuffer = nullptr,
+ .cookie = nullptr},
+ .format = AUDIO_FORMAT_PCM_FLOAT,
+ .accessMode = EFFECT_BUFFER_ACCESS_WRITE,
+ .mask = EFFECT_CONFIG_ALL,
+ },
+ };
};
TEST_P(libAudioHalEffectParamTest, setAndGetParam) {
@@ -392,7 +511,8 @@
AudioUuid uuid = ::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid(
*std::get<TUPLE_UUID>(info.param))
.value();
- std::string name = "UUID_" + toString(uuid);
+ std::string name = "UUID_" + toString(uuid) + "_";
+ name += std::get<TUPLE_IS_INPUT>(info.param) ? "_input" : "_output";
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
@@ -404,6 +524,4 @@
return RUN_ALL_TESTS();
}
-// TODO: b/263986405 Add multi-thread testing
-
} // namespace android