// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundFrontendLiteral.h" #include "CoreMinimal.h" #include "MetasoundLog.h" #include "MetasoundFrontendDataTypeRegistry.h" #include "MetasoundFrontendProxyDataCache.h" #include "MetasoundFrontendRegistries.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MetasoundFrontendLiteral) FString LexToString(Metasound::FLiteral::FNone InValue) { return FString(TEXT("")); } namespace MetasoundFrontendLiteralPrivate { // Default size of string for string builder stack allocation. Heap allocation is used if the string exceeds this size. constexpr int32 ArrayStringAllocatedBytes = 256; // Defaults to calling LexToString for most literal data types. template struct TLiteralValueToStringHelper { static FString Convert(const Type& InType) { return LexToString(InType); } }; // String conversion specialization for UObject* template<> struct TLiteralValueToStringHelper> { static FString Convert(const TObjectPtr InObject) { // Use empty string for null object as this is recognized // as empty value in editor context (the string "nullptr" // by contrast is not). if (!InObject) { return FString(); } return InObject->GetPathName(); } }; // String conversion specialization for TArray<> template struct TLiteralValueToStringHelper> { using FArrayType = TArray; static FString Convert(const FArrayType& InArray) { TStringBuilder Builder; Builder << TEXT("["); for (int32 i = 0; i < InArray.Num(); i++) { if (i> 0) { Builder << TEXT(", "); } Builder << TLiteralValueToStringHelper::Convert(InArray[i]); } Builder << TEXT("]"); return FString(Builder); } }; template FString TLiteralValueToString(const Type& InType) { // Uses a helper struct to allow for partial specialization. // (c++ does not allow partial specialization of functions) return TLiteralValueToStringHelper::Convert(InType); } } FMetasoundFrontendLiteral::FMetasoundFrontendLiteral(const FAudioParameter& InParameter) { switch (InParameter.ParamType) { case EAudioParameterType::Trigger: case EAudioParameterType::Boolean: { Set(InParameter.BoolParam); } break; case EAudioParameterType::BooleanArray: { Set(InParameter.ArrayBoolParam); } break; case EAudioParameterType::Float: { Set(InParameter.FloatParam); } break; case EAudioParameterType::FloatArray: { Set(InParameter.ArrayFloatParam); } break; case EAudioParameterType::Integer: { Set(InParameter.IntParam); } break; case EAudioParameterType::IntegerArray: { Set(InParameter.ArrayIntParam); } break; case EAudioParameterType::None: { Set(FMetasoundFrontendLiteral::FDefault()); } break; case EAudioParameterType::NoneArray: { Set(FMetasoundFrontendLiteral::FDefaultArray{ InParameter.IntParam }); } break; case EAudioParameterType::Object: { Set(InParameter.ObjectParam); } break; case EAudioParameterType::ObjectArray: { Set(InParameter.ArrayObjectParam); } break; case EAudioParameterType::String: { Set(InParameter.StringParam); } break; case EAudioParameterType::StringArray: { Set(InParameter.ArrayStringParam); } break; default: { static_assert(static_cast(EAudioParameterType::COUNT) == 13, "Possible missing switch case coverage"); checkNoEntry(); } } } bool FMetasoundFrontendLiteral::IsArray() const { switch (Type) { case EMetasoundFrontendLiteralType::NoneArray: case EMetasoundFrontendLiteralType::BooleanArray: case EMetasoundFrontendLiteralType::IntegerArray: case EMetasoundFrontendLiteralType::FloatArray: case EMetasoundFrontendLiteralType::StringArray: case EMetasoundFrontendLiteralType::UObjectArray: return true; case EMetasoundFrontendLiteralType::None: case EMetasoundFrontendLiteralType::Boolean: case EMetasoundFrontendLiteralType::Integer: case EMetasoundFrontendLiteralType::Float: case EMetasoundFrontendLiteralType::String: case EMetasoundFrontendLiteralType::UObject: case EMetasoundFrontendLiteralType::Invalid: return false; default: static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == 12, "Possible missing EMetasoundFrontendLiteralType case coverage"); { checkNoEntry(); return false; } } } bool FMetasoundFrontendLiteral::IsEqual(const FMetasoundFrontendLiteral& InOther) const { if (InOther.GetType() != GetType()) { return false; } switch (Type) { case EMetasoundFrontendLiteralType::Boolean: case EMetasoundFrontendLiteralType::BooleanArray: return AsBoolean == InOther.AsBoolean; case EMetasoundFrontendLiteralType::Float: case EMetasoundFrontendLiteralType::FloatArray: return AsFloat == InOther.AsFloat; case EMetasoundFrontendLiteralType::Integer: case EMetasoundFrontendLiteralType::IntegerArray: return AsInteger == InOther.AsInteger; case EMetasoundFrontendLiteralType::None: case EMetasoundFrontendLiteralType::NoneArray: return AsNumDefault == InOther.AsNumDefault; case EMetasoundFrontendLiteralType::String: case EMetasoundFrontendLiteralType::StringArray: return AsString == InOther.AsString; case EMetasoundFrontendLiteralType::UObject: case EMetasoundFrontendLiteralType::UObjectArray: return AsUObject == InOther.AsUObject; case EMetasoundFrontendLiteralType::Invalid: default: return true; } } bool FMetasoundFrontendLiteral::IsValid() const { return (Type != EMetasoundFrontendLiteralType::Invalid); } void FMetasoundFrontendLiteral::Set(FDefault InValue) { Empty(); Type = EMetasoundFrontendLiteralType::None; AsNumDefault = 1; } void FMetasoundFrontendLiteral::Set(const FDefaultArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::NoneArray; AsNumDefault = FMath::Max(0, InValue.Num); } void FMetasoundFrontendLiteral::Set(bool InValue) { Empty(); Type = EMetasoundFrontendLiteralType::Boolean; AsBoolean.Add(InValue); } void FMetasoundFrontendLiteral::Set(const TArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::BooleanArray; AsBoolean = InValue; } void FMetasoundFrontendLiteral::Set(int32 InValue) { Empty(); Type = EMetasoundFrontendLiteralType::Integer; AsInteger.Add(InValue); } void FMetasoundFrontendLiteral::Set(const TArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::IntegerArray; AsInteger = InValue; } void FMetasoundFrontendLiteral::Set(float InValue) { Empty(); Type = EMetasoundFrontendLiteralType::Float; AsFloat.Add(InValue); } void FMetasoundFrontendLiteral::Set(const TArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::FloatArray; AsFloat = InValue; } void FMetasoundFrontendLiteral::Set(const FString& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::String; AsString.Add(InValue); } void FMetasoundFrontendLiteral::Set(const TArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::StringArray; AsString = InValue; } void FMetasoundFrontendLiteral::Set(UObject* InValue) { Empty(); Type = EMetasoundFrontendLiteralType::UObject; AsUObject.Add(InValue); } void FMetasoundFrontendLiteral::Set(const TArray& InValue) { Empty(); Type = EMetasoundFrontendLiteralType::UObjectArray; AsUObject = InValue; } void FMetasoundFrontendLiteral::SetFromLiteral(const Metasound::FLiteral& InLiteral) { using namespace Metasound; Clear(); switch (InLiteral.GetType()) { case ELiteralType::None: { Set(FMetasoundFrontendLiteral::FDefault{}); } break; case ELiteralType::Boolean: { Set(InLiteral.Value.Get()); } break; case ELiteralType::Float: { Set(InLiteral.Value.Get()); } break; case ELiteralType::Integer: { Set(InLiteral.Value.Get()); } break; case ELiteralType::String: { Set(InLiteral.Value.Get()); } break; case ELiteralType::UObjectProxy: { // Only error if attempting to retrieve valid UObject from ProxyDataPtr // as this function can safely is used to initialize from defaults (which // is valid as a null proxy can safely correspond to a null UObject ptr). if (InLiteral.Value.Get>().IsValid()) { UE_LOG(LogMetaSound, Error, TEXT("Cannot set UObjectProxy from Metasound::FLiteral")); } Set(static_cast(nullptr)); } break; case ELiteralType::NoneArray: { int32 Num = InLiteral.Value.Get>().Num(); Set(FMetasoundFrontendLiteral::FDefaultArray{ Num }); } break; case ELiteralType::BooleanArray: { Set(InLiteral.Value.Get>()); } break; case ELiteralType::IntegerArray: { Set(InLiteral.Value.Get>()); } break; case ELiteralType::FloatArray: { Set(InLiteral.Value.Get>()); } break; case ELiteralType::StringArray: { Set(InLiteral.Value.Get>()); } break; case ELiteralType::UObjectProxyArray: { // Only error if attempting to retrieve valid UObject from ProxyDataPtr // as this function can safely is used to initialize from defaults (which // is valid as a null proxy can safely correspond to a null UObject ptr). if (!InLiteral.Value.Get>>().IsEmpty()) { UE_LOG(LogMetaSound, Error, TEXT("Cannot set UObjectProxy from Metasound::FLiteral")); } Set(TArray()); } break; case ELiteralType::Invalid: default: { static_assert(static_cast(ELiteralType::Invalid) == 12, "Possible missing literal type switch coverage"); } } } void FMetasoundFrontendLiteral::SetType(EMetasoundFrontendLiteralType InType) { switch (Type) { case EMetasoundFrontendLiteralType::NoneArray: { Set(FDefaultArray()); } break; case EMetasoundFrontendLiteralType::BooleanArray: { Set(TArray()); } break; case EMetasoundFrontendLiteralType::IntegerArray: { Set(TArray()); } break; case EMetasoundFrontendLiteralType::FloatArray: { Set(TArray()); } break; case EMetasoundFrontendLiteralType::StringArray: { Set(TArray()); } break; case EMetasoundFrontendLiteralType::UObjectArray: { Set(TArray()); } break; case EMetasoundFrontendLiteralType::None: { Set(FDefault()); } break; case EMetasoundFrontendLiteralType::Boolean: { Set(false); } break; case EMetasoundFrontendLiteralType::Integer: { Set(0); } break; case EMetasoundFrontendLiteralType::Float: { Set(0.f); } break; case EMetasoundFrontendLiteralType::String: { Set(FString()); } break; case EMetasoundFrontendLiteralType::UObject: { Set(static_cast(nullptr)); } break; case EMetasoundFrontendLiteralType::Invalid: { Set(FDefault()); } break; default: static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == 12, "Possible missing EMetasoundFrontendLiteralType case coverage"); { checkNoEntry(); } break; } } bool FMetasoundFrontendLiteral::TryGet(UObject*& OutValue) const { if (Type == EMetasoundFrontendLiteralType::UObject) { if (ensure(!AsUObject.IsEmpty())) { OutValue = AsUObject[0]; return true; } } return false; } bool FMetasoundFrontendLiteral::TryGet(TArray& OutValue) const { if (Type == EMetasoundFrontendLiteralType::UObjectArray) { OutValue = AsUObject; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(bool& OutValue) const { if (Type == EMetasoundFrontendLiteralType::Boolean) { OutValue = AsBoolean[0]; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(TArray& OutValue) const { if (Type == EMetasoundFrontendLiteralType::BooleanArray) { OutValue = AsBoolean; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(int32& OutValue) const { if (Type == EMetasoundFrontendLiteralType::Integer) { OutValue = AsInteger[0]; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(TArray& OutValue) const { if (Type == EMetasoundFrontendLiteralType::IntegerArray) { OutValue = AsInteger; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(float& OutValue) const { if (Type == EMetasoundFrontendLiteralType::Float) { OutValue = AsFloat[0]; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(TArray& OutValue) const { if (Type == EMetasoundFrontendLiteralType::FloatArray) { OutValue = AsFloat; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(FString& OutValue) const { if (Type == EMetasoundFrontendLiteralType::String) { OutValue = AsString[0]; return true; } return false; } bool FMetasoundFrontendLiteral::TryGet(TArray& OutValue) const { if (Type == EMetasoundFrontendLiteralType::StringArray) { OutValue = AsString; return true; } return false; } Metasound::FLiteral FMetasoundFrontendLiteral::ToLiteral(const FName& InMetasoundDataTypeName, const Metasound::Frontend::IDataTypeRegistry* InDataTypeRegistry, const Metasound::Frontend::FProxyDataCache* InProxyDataCache) const { using namespace Metasound; using namespace Metasound::Frontend; if (EMetasoundFrontendLiteralType::UObject == Type) { if (ensure(AsUObject.Num() > 0)) { if (InProxyDataCache) { if (const TSharedPtr* ProxyData = InProxyDataCache->FindProxy(AsUObject[0])) { return FLiteral(*ProxyData); } else { return FLiteral(TSharedPtr()); } } else { if (!InDataTypeRegistry) { InDataTypeRegistry = &IDataTypeRegistry::Get(); } // UObject proxies must go through the registry. The registry contains the information // needed to convert UObjects to proxies. return InDataTypeRegistry->CreateLiteralFromUObject(InMetasoundDataTypeName, AsUObject[0]); } } return FLiteral(TSharedPtr()); } else if (EMetasoundFrontendLiteralType::UObjectArray == Type) { if (InProxyDataCache) { TArray> Proxies; for (const UObject* Object : AsUObject) { if (const TSharedPtr* ProxyData = InProxyDataCache->FindProxy(Object)) { Proxies.Add(*ProxyData); } else { Proxies.Add(TSharedPtr()); } } return FLiteral(Proxies); } else { if (!InDataTypeRegistry) { InDataTypeRegistry = &IDataTypeRegistry::Get(); } // UObject proxies must go through the registry. The registry contains the information // needed to convert UObjects to proxies. return InDataTypeRegistry->CreateLiteralFromUObjectArray(InMetasoundDataTypeName, AsUObject); } } else { // Use default conversions for core literal types. return ToLiteralNoProxy(); } } Metasound::FLiteral FMetasoundFrontendLiteral::ToLiteralNoProxy() const { using namespace Metasound; if ((EMetasoundFrontendLiteralType::UObject == Type) || (EMetasoundFrontendLiteralType::UObjectArray == Type)) { UE_LOG(LogMetaSound, Error, TEXT("Cannot convert FMetasoundFrontendLiteral to Metasound::Literal without data type name [Literal:%s]"), *ToString()); return FLiteral::CreateInvalid(); } switch (Type) { case EMetasoundFrontendLiteralType::None: return FLiteral(FLiteral::FNone{}); case EMetasoundFrontendLiteralType::Boolean: return FLiteral(AsBoolean[0]); case EMetasoundFrontendLiteralType::Integer: return FLiteral(AsInteger[0]); case EMetasoundFrontendLiteralType::Float: return FLiteral(AsFloat[0]); case EMetasoundFrontendLiteralType::String: return FLiteral(AsString[0]); case EMetasoundFrontendLiteralType::NoneArray: { TArray Nones; Nones.SetNum(AsNumDefault); return FLiteral(Nones); } case EMetasoundFrontendLiteralType::BooleanArray: return FLiteral(AsBoolean); case EMetasoundFrontendLiteralType::IntegerArray: return FLiteral(AsInteger); case EMetasoundFrontendLiteralType::FloatArray: return FLiteral(AsFloat); case EMetasoundFrontendLiteralType::StringArray: return FLiteral(AsString); case EMetasoundFrontendLiteralType::UObject: case EMetasoundFrontendLiteralType::UObjectArray: case EMetasoundFrontendLiteralType::Invalid: return FLiteral::CreateInvalid(); default: static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == 12, "Possible missing literal type switch coverage"); return FLiteral::CreateInvalid(); } } FString FMetasoundFrontendLiteral::ToString() const { using namespace Metasound; using namespace MetasoundFrontendLiteralPrivate; switch (Type) { case EMetasoundFrontendLiteralType::None: return TLiteralValueToString(FLiteral::FNone{}); case EMetasoundFrontendLiteralType::Boolean: return TLiteralValueToString(AsBoolean[0]); case EMetasoundFrontendLiteralType::Integer: return TLiteralValueToString(AsInteger[0]); case EMetasoundFrontendLiteralType::Float: return TLiteralValueToString(AsFloat[0]); case EMetasoundFrontendLiteralType::String: return TLiteralValueToString(AsString[0]); case EMetasoundFrontendLiteralType::UObject: return TLiteralValueToString(AsUObject[0]); case EMetasoundFrontendLiteralType::NoneArray: { TArray Nones; Nones.SetNum(AsNumDefault); return TLiteralValueToString(Nones); } case EMetasoundFrontendLiteralType::BooleanArray: return TLiteralValueToString(AsBoolean); case EMetasoundFrontendLiteralType::IntegerArray: return TLiteralValueToString(AsInteger); case EMetasoundFrontendLiteralType::FloatArray: return TLiteralValueToString(AsFloat); case EMetasoundFrontendLiteralType::StringArray: return TLiteralValueToString(AsString); case EMetasoundFrontendLiteralType::UObjectArray: return TLiteralValueToString(AsUObject); case EMetasoundFrontendLiteralType::Invalid: default: static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == 12, "Possible missing literal type switch coverage"); return FString(); } } int32 FMetasoundFrontendLiteral::GetArrayNum() const { switch (Type) { case EMetasoundFrontendLiteralType::None: case EMetasoundFrontendLiteralType::Boolean: case EMetasoundFrontendLiteralType::Integer: case EMetasoundFrontendLiteralType::Float: case EMetasoundFrontendLiteralType::String: case EMetasoundFrontendLiteralType::UObject: return INDEX_NONE; case EMetasoundFrontendLiteralType::NoneArray: return AsNumDefault; case EMetasoundFrontendLiteralType::BooleanArray: return AsBoolean.Num(); case EMetasoundFrontendLiteralType::IntegerArray: return AsInteger.Num(); case EMetasoundFrontendLiteralType::FloatArray: return AsFloat.Num(); case EMetasoundFrontendLiteralType::StringArray: return AsString.Num(); case EMetasoundFrontendLiteralType::UObjectArray: return AsUObject.Num(); case EMetasoundFrontendLiteralType::Invalid: default: static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == 12, "Possible missing literal type switch coverage"); return INDEX_NONE; } } EMetasoundFrontendLiteralType FMetasoundFrontendLiteral::GetType() const { return Type; } void FMetasoundFrontendLiteral::Clear() { Empty(); Type = EMetasoundFrontendLiteralType::None; AsNumDefault = 1; } FMetasoundFrontendLiteral FMetasoundFrontendLiteral::GetInvalid() { FMetasoundFrontendLiteral InvalidValue; InvalidValue.Type = EMetasoundFrontendLiteralType::Invalid; return InvalidValue; } void FMetasoundFrontendLiteral::Empty() { AsNumDefault = 0; AsBoolean.Empty(); AsInteger.Empty(); AsFloat.Empty(); AsString.Empty(); AsUObject.Empty(); } namespace Metasound { namespace Frontend { // Convenience function to convert Metasound::ELiteralType to EMetasoundFrontendLiteralType, the UEnum used for metasound documents. EMetasoundFrontendLiteralType GetMetasoundFrontendLiteralType(ELiteralType InLiteralType) { static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == static_cast(ELiteralType::Invalid), "Possible literal type enum mismatch"); return static_cast(InLiteralType); } // Convenience function to convert Metasound::ELiteralType to EMetasoundFrontendLiteralType, the UEnum used for metasound documents. ELiteralType GetMetasoundLiteralType(EMetasoundFrontendLiteralType InLiteralType) { static_assert(static_cast(EMetasoundFrontendLiteralType::Invalid) == static_cast(ELiteralType::Invalid), "Possible literal type enum mismatch"); return static_cast(InLiteralType); } } }