// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundCatMakeNode.h" #include "MetasoundChannelAgnosticType.h" #include "MetasoundExecutableOperator.h" #include "MetasoundFacade.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundVertex.h" #include "TypeFamily/ChannelTypeFamily.h" #define LOCTEXT_NAMESPACE "MetasoundStandardNodes_CatMakeNode" namespace Metasound { class FChannelAgnosticType; struct FBuildOperatorParams; class FNodeFacade; namespace CatMakeNodePrivate { METASOUND_PARAM(InputFromCat, "Input", "CAT to Cast"); METASOUND_PARAM(OutputToCat, "Output", "CAT Result"); const FLazyName InputBaseName{"In"}; FName MakeInputVertexName(const int32 InIndex) { FName Name = InputBaseName; Name.SetNumber(InIndex); return Name; } FInputDataVertex MakeInputDataVertex(const int32 InIndex, const FName InName, const FString& InFriendlyName) { const FName InputName = MakeInputVertexName(InIndex); const FText InputDisplayName = METASOUND_LOCTEXT_FORMAT("In_DisplayName", "{0}", FText::FromString(InName.ToString())); return TInputDataVertex{InputName, FDataVertexMetadata{FText::FromString(*InFriendlyName), InputDisplayName}}; } FVertexInterface MakeClassInterface(const FName Format) { const Audio::FChannelTypeFamily* Found = Audio::GetChannelRegistry().FindConcreteChannel(Format); if (!Found) { return {}; } const int32 NumChannels = Found->NumChannels(); FInputVertexInterface InputInterface; for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ++ChannelIndex ) { if (TOptional Name = Found->GetChannelName(ChannelIndex); Name.IsSet()) { InputInterface.Add(MakeInputDataVertex(ChannelIndex, Name->Name, Name->FriendlyName)); } else { InputInterface.Add(MakeInputDataVertex(ChannelIndex, TEXT("Input"), FString::FromInt(ChannelIndex) )); } } FOutputVertexInterface OutputInterface; OutputInterface.Add(TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputToCat))); return{ MoveTemp(InputInterface), MoveTemp(OutputInterface) }; } class FCatMakeOperatorData final : public TOperatorData { public: // The OperatorDataTypeName is used when downcasting an IOperatorData to ensure // that the downcast is valid. static const FLazyName OperatorDataTypeName; explicit FCatMakeOperatorData(const FName& InToTypeName) : ToTypeName(InToTypeName) {} const FName& GetToType() const { return ToTypeName; } private: FName ToTypeName; }; // Linkage. const FLazyName FCatMakeOperatorData::OperatorDataTypeName = TEXT("FCatMakeOperatorData"); } class FCatMakeOperator final : public TExecutableOperator { public: FCatMakeOperator(const FBuildOperatorParams& InParams, TArray>&& InInputs, FChannelAgnosticTypeWriteRef&& InOutputCat) : InputAudioVertices(MoveTemp(InInputs)) , OutputCat(MoveTemp(InOutputCat)) , Settings(InParams.OperatorSettings) { } virtual ~FCatMakeOperator() override = default; static const FVertexInterface& GetDefaultInterface() { using namespace CatMakeNodePrivate ; auto CreateDefaultInterface = []()-> FVertexInterface { // outputs FOutputVertexInterface OutputInterface; OutputInterface.Add(TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputToCat))); return FVertexInterface({}, OutputInterface); }; // end lambda: CreateDefaultInterface() static const FVertexInterface DefaultInterface = CreateDefaultInterface(); return DefaultInterface; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace CatMakeNodePrivate; const FCatMakeOperatorData* CatMakeData = CastOperatorData(InParams.Node.GetOperatorData().Get()); if (!CatMakeData) { return TUniquePtr(); } const FName OutputFormat = CatMakeData->GetToType(); const FInputVertexInterfaceData& InputData = InParams.InputData; const Audio::FChannelTypeFamily* ConcreteToType = Audio::GetChannelRegistry().FindConcreteChannel(OutputFormat); // Set the number of inputs pins to match the output format. TArray> InputAudioVertices; for (int32 i = 0; i < ConcreteToType->NumChannels(); ++i) { InputAudioVertices.Emplace(InputData.GetOrCreateDefaultDataReadReference(MakeInputVertexName(i), InParams.OperatorSettings)); } // Make output CAT to match our configs settings. (use concrete form). FChannelAgnosticTypeWriteRef OutputPin = FChannelAgnosticTypeWriteRef::CreateNew(InParams.OperatorSettings, ConcreteToType->GetName()); return MakeUnique( InParams, MoveTemp(InputAudioVertices), MoveTemp(OutputPin) ); } virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override { using namespace CatMakeNodePrivate; for (int32 i = 0; i < InputAudioVertices.Num(); i++) { InOutVertexData.BindReadVertex(MakeInputVertexName(i), InputAudioVertices[i]); } } virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override { using namespace CatMakeNodePrivate; InOutVertexData.BindWriteVertex(METASOUND_GET_PARAM_NAME(OutputToCat),OutputCat); } void Reset(const FResetParams& InParams) { Execute(); } void Execute() { // Copy each input channel into the CAT. for (int32 Index = 0; Index < InputAudioVertices.Num(); Index++) { const FAudioBuffer& Src = *InputAudioVertices[Index]; TArrayView Dst = OutputCat->GetChannel(Index); checkSlow(Src.Num() == Dst.Num()); FMemory::Memcpy(Dst.GetData(), Src.GetData(), Dst.Num() * sizeof(float)); } } static FNodeClassMetadata GetNodeInfo() { using namespace CatMakeNodePrivate; return FNodeClassMetadata { FNodeClassName{ "Experimental", "CatMakeOperator", "" }, 1, // Major version 0, // Minor version METASOUND_LOCTEXT("CatMakeNodeName", "CAT Make Node"), METASOUND_LOCTEXT("CatMakeNodeNameDescription", "A Node that builds CATs"), TEXT("UE"), // Author METASOUND_LOCTEXT("ExampleConfigurablePromptIfMissing", "Enable the MetaSoundExperimental Plugin"), // Prompt if missing GetDefaultInterface(), {} }; } private: TArray> InputAudioVertices; FChannelAgnosticTypeWriteRef OutputCat; FOperatorSettings Settings; }; // class FCatCastingOperator using FCatMakeNode = TNodeFacade; // register node config. METASOUND_REGISTER_NODE_AND_CONFIGURATION(FCatMakeNode, FMetaSoundCatMakeNodeConfiguration); } // namespace Metasound TInstancedStruct FMetaSoundCatMakeNodeConfiguration::OverrideDefaultInterface( const FMetasoundFrontendClass& InNodeClass) const { using namespace Metasound::CatMakeNodePrivate; return TInstancedStruct::Make(FMetasoundFrontendClassInterface::GenerateClassInterface(MakeClassInterface(Format))); } TSharedPtr FMetaSoundCatMakeNodeConfiguration::GetOperatorData() const { return MakeShared(Format); } #undef LOCTEXT_NAMESPACE //