/* * Copyright (C) 2023 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. */ #include #include #define LOG_TAG "AidlConversionSpatializer" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include "AidlConversionSpatializer.h" namespace android { namespace effect { 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; 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 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( 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(modeInt32); if (mode < *ndk::enum_range().begin() || mode > *ndk::enum_range().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; } aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingSensorId, sensorId); return statusTFromBinderStatus(mEffect->setParameter(aidlParam)); } default: { // for vendor extension, copy data area to the DefaultExtension, parameter ignored VendorExtension ext = VALUE_OR_RETURN_STATUS( aidl::android::legacy2aidl_EffectParameterReader_VendorExtension(param)); aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, vendor, ext); break; } } } else { aidlParam = VALUE_OR_RETURN_STATUS( ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param)); } return statusTFromBinderStatus(mEffect->setParameter(aidlParam)); } status_t AidlConversionSpatializer::getParameter(EffectParamWriter& param) { 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_SUPPORTED_LEVELS: { const auto& range = getRange( mDesc.capability, Spatializer::spatializationLevel); if (!range) { return BAD_VALUE; } std::vector levels; for (const auto level : ::ndk::enum_range()) { const auto spatializer = Spatializer::make(level); if (spatializer >= range->min && spatializer <= range->max) { levels.emplace_back(level); } } const uint8_t num = levels.size(); RETURN_STATUS_IF_ERROR(param.writeToValue(&num)); for (const auto level : levels) { RETURN_STATUS_IF_ERROR(param.writeToValue(&level)); } 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)); return param.writeToValue(&level); } case SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED: { const auto& range = getRange( mDesc.capability, Spatializer::spatializationLevel); if (!range) { ALOGE("%s %d: range not defined for spatializationLevel", __func__, __LINE__); return BAD_VALUE; } const auto& nonSupport = Spatializer::make( 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)); 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)); // audio_channel_mask_t is uint32_t enum, write number in 32bit const uint32_t num = supportedLayouts.size(); RETURN_STATUS_IF_ERROR(param.writeToValue(&num)); 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 */)); RETURN_STATUS_IF_ERROR(param.writeToValue(&mask)); } return OK; } case SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES: { const auto& range = getRange( mDesc.capability, Spatializer::spatializationMode); if (!range) { return BAD_VALUE; } std::vector modes; for (const auto mode : ::ndk::enum_range()) { if (const auto spatializer = Spatializer::make(mode); spatializer >= range->min && spatializer <= range->max) { modes.emplace_back(mode); } } const uint8_t num = modes.size(); RETURN_STATUS_IF_ERROR(param.writeToValue(&num)); for (const auto mode : modes) { RETURN_STATUS_IF_ERROR(param.writeToValue(&mode)); } return OK; } case SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION: { const auto& range = getRange( mDesc.capability, Spatializer::headTrackingConnectionMode); if (!range) { return BAD_VALUE; } std::vector modes; for (const auto mode : ::ndk::enum_range()) { if (const auto spatializer = Spatializer::make(mode); spatializer < range->min || spatializer > range->max) { modes.emplace_back(mode); } } const uint8_t num = modes.size(); RETURN_STATUS_IF_ERROR(param.writeToValue(&num)); for (const auto mode : modes) { RETURN_STATUS_IF_ERROR(param.writeToValue(&mode)); } 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(mode); if (status = param.writeToValue(&modeInt32); status != OK) { ALOGW("%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) { ALOGW("%s %d: write sensorId %d to value failed %d", __func__, __LINE__, sensorId, status); return status; } return OK; } default: { VENDOR_EXTENSION_GET_AND_RETURN(Spatializer, spatializer, param); } } } else { Parameter aidlParam; DefaultExtension defaultExt; // read parameters into DefaultExtension vector 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))); // copy the AIDL extension data back to effect_param_t return VALUE_OR_RETURN_STATUS( ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param)); } } } // namespace effect } // namespace android