This is a quickstart guide for making new types of MetaSound Nodes through C++ code.
https://dev.epicgames.com/community/learning/tutorials/ry7p/unreal-engine-creating-metasound-nodes-in-c-quickstart
I just get the error:
Cannot open include file: ‘MetasoundExecutableOperator.h’: No such file or directory
Found this template on the Facebook audio group, maybe it helps paint the full picture:
You probably need to add the relevant Metasound module to your project’s .build.cs
file. Something like:
PrivateDependencyModuleNames.AddRange(new string[]
{
"MetasoundEngine",
"MetasoundFrontend"
});
@lantz.anna Any possibility of updating this tutorial for Unreal 5.1? I’m walking through it block-by-block and the very first block fails to work as expected because some of the types have been moved into the Metasound::
namespace.
There may be more incompatibilities… I’ve not gone through the full tutorial yet…
Hi I cant seem to create a new meta sound node to work
im in 5.3.1 and i keep having troubles with namespaces
Creating library C:\Users\tgall\Documents\Unreal Projects\AudioMixPlug\Plugins\MyCustomMetaSoundPlugin\Binaries\Win64\UnrealEditor-MyCustomMetaSoundPlugin.patch_14.lib and object C:\Users\tgall\Documents\Unreal Projects\AudioMixPlug\Plugins\MyCustomMetaSoundPlugin\Binaries\Win64\UnrealEditor-MyCustomMetaSoundPlugin.patch_14.exp
Module.MyCustomMetaSoundPlugin.cpp.obj : error LNK2001: unresolved external symbol “class FName const Metasound::StandardNodes::Namespace” (?Namespace@StandardNodes@Metasound@@3VFName@@B)
Module.MyCustomMetaSoundPlugin.cpp.obj : error LNK2001: unresolved external symbol “class FName const Metasound::StandardNodes::AudioVariant” (?AudioVariant@StandardNodes@Metasound@@3VFName@@B)
C:\Users\tgall\Documents\Unreal Projects\AudioMixPlug\Plugins\MyCustomMetaSoundPlugin\Binaries\Win64\UnrealEditor-MyCustomMetaSoundPlugin.patch_14.exe : fatal error LNK1120: 2 unresolved
Everythgin seems to compile until I use the line METASOUND_REGISTER_NODE(FTutorialNode);
my build.cs file contains
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"MetasoundFrontend",
"MetasoundGraphCore",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"MetasoundEngine",
"MetasoundEditor",
"MetasoundFrontend",
"MetasoundGraphCore",
"AudioMixer",
"SignalProcessing",
// ... add private dependencies that you statically link with here ...
}
My Plugin file to hold the meta sounds
{
“FileVersion”: 3,
“Version”: 1,
“VersionName”: “1.0”,
“FriendlyName”: “MyCustomMetaSoundPlugin”,
“Description”: “”,
“Category”: “Other”,
“CreatedBy”: “”,
“CreatedByURL”: “”,
“DocsURL”: “”,
“MarketplaceURL”: “”,
“SupportURL”: “”,
“CanContainContent”: true,
“IsBetaVersion”: false,
“IsExperimentalVersion”: false,
“Installed”: false,
“Modules”: [
{
“Name”: “MyCustomMetaSoundPlugin”,
“Type”: “Runtime”,
“LoadingPhase”: “Default”
}
],
"Plugins": [
{
"Name": "Metasound",
"Enabled": true
}
]
}
cant seem to get my head around this atall…
am i missing somehting extremly obvious
my cpp
include “MetasoundExecutableOperator.h” // TExecutableOperator class
include “MetasoundPrimitives.h” // ReadRef and WriteRef descriptions for bool, int32, float, and string
include “MetasoundNodeRegistrationMacro.h” // METASOUND_LOCTEXT and METASOUND_REGISTER_NODE macros
include “MetasoundStandardNodesNames.h” // StandardNodes namespace
include “MetasoundFacade.h” // FNodeFacade class, eliminates the need for a fair amount of boilerplate code
include “MetasoundParamHelper.h” // METASOUND_PARAM and METASOUND_GET_PARAM family of macros
// Required for ensuring the node is supported by all languages in engine. Must be unique per MetaSound.
#define LOCTEXT_NAMESPACE “MetasoundStandardNodes_MetaSoundTutorialNode”
namespace Metasound
{
// Vertex Names - define your node’s inputs and outputs here
namespace TutorialNodeNames
{
METASOUND_PARAM(InputAValue, “A”, “Input value A.”);
METASOUND_PARAM(InputBValue, “B”, “Input value B.”);
METASOUND_PARAM(OutputValue, "Sum of A and B", "The sum of A and B.");
}
// Operator Class - defines the way your node is described, created and executed
class FTutorialOperator : public TExecutableOperator<FTutorialOperator>
{
public:
// Constructor
FTutorialOperator(
const FFloatReadRef& InAValue,
const FFloatReadRef& InBValue)
: InputA(InAValue)
, InputB(InBValue)
, TutorialNodeOutput(FFloatWriteRef::CreateNew(*InputA + *InputB))
{
}
// Helper function for constructing vertex interface
static const FVertexInterface& DeclareVertexInterface()
{
using namespace TutorialNodeNames;
static const FVertexInterface Interface(
FInputVertexInterface(
TInputDataVertexModel<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputAValue)),
TInputDataVertexModel<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputBValue))
),
FOutputVertexInterface(
TOutputDataVertexModel<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValue))
)
);
return Interface;
}
// Retrieves necessary metadata about your node
static const FNodeClassMetadata& GetNodeInfo()
{
auto CreateNodeClassMetadata = []() -> FNodeClassMetadata
{
FVertexInterface NodeInterface = DeclareVertexInterface();
FNodeClassMetadata Metadata
{
FNodeClassName { StandardNodes::Namespace, "Tutorial Node", StandardNodes::AudioVariant },
1, // Major Version
0, // Minor Version
METASOUND_LOCTEXT("TutorialNodeDisplayName", "Tutorial Node"),
METASOUND_LOCTEXT("TutorialNodeDesc", "A simple node to demonstrate how to create new MetaSound nodes in C++. Adds two floats together"),
PluginAuthor,
PluginNodeMissingPrompt,
NodeInterface,
{ }, // Category Hierarchy
{ }, // Keywords for searching
FNodeDisplayStyle{}
};
return Metadata;
};
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata();
return Metadata;
}
// Allows MetaSound graph to interact with your node's inputs
virtual FDataReferenceCollection GetInputs() const override
{
using namespace TutorialNodeNames;
FDataReferenceCollection InputDataReferences;
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InputAValue), InputA);
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InputBValue), InputB);
return InputDataReferences;
}
// Allows MetaSound graph to interact with your node's outputs
virtual FDataReferenceCollection GetOutputs() const override
{
using namespace TutorialNodeNames;
FDataReferenceCollection OutputDataReferences;
OutputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(OutputValue), TutorialNodeOutput);
return OutputDataReferences;
}
// Used to instantiate a new runtime instance of your node
static TUniquePtr<IOperator> CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors)
{
using namespace TutorialNodeNames;
const Metasound::FDataReferenceCollection& InputCollection = InParams.InputDataReferences;
const Metasound::FInputVertexInterface& InputInterface = DeclareVertexInterface().GetInputInterface();
TDataReadReference<float> InputA = InputCollection.GetDataReadReferenceOrConstructWithVertexDefault<float>(InputInterface, METASOUND_GET_PARAM_NAME(InputAValue), InParams.OperatorSettings);
TDataReadReference<float> InputB = InputCollection.GetDataReadReferenceOrConstructWithVertexDefault<float>(InputInterface, METASOUND_GET_PARAM_NAME(InputBValue), InParams.OperatorSettings);
return MakeUnique<FTutorialOperator>(InputA, InputB);
}
// Primary node functionality
void Execute()
{
*TutorialNodeOutput = *InputA + *InputB;
}
private:
// Inputs
FFloatReadRef InputA;
FFloatReadRef InputB;
// Outputs
FFloatWriteRef TutorialNodeOutput;
};
// Node Class - Inheriting from FNodeFacade is recommended for nodes that have a static FVertexInterface
class FTutorialNode : public FNodeFacade
{
public:
FTutorialNode(const FNodeInitData& InitData)
: FNodeFacade(InitData.InstanceName, InitData.InstanceID, TFacadeOperatorClass<FTutorialOperator>())
{
}
};
// Register node
METASOUND_REGISTER_NODE(FTutorialNode);
}
#undef LOCTEXT_NAMESPACE
Ive also been through Aarons tutorial for the pitch shifter , Same Issues…
Hi my friend , I want create my custom node and use custom data type instead float , but this type can`t reflect correspond widget . Should I make-up anywhere?
Curve is my variable of custom type like WaveTableBank.
Hi, I am a game developer. Now I also want to create custom meta sound nodes to get curves, can you refer to how your code is composed? Thx!
Hi, if I follow the Building Off Of Existing Templates code as you wrote at the end and then put in the custom plugin, how should I activate it? I currently created a plugin and then put Building Off Of Existing Templates code into it, but it doesn’t take effect in the engine…
For anyone coming here in 5.5: looks like there’s been some API changes. There are now deprecation warnings for FCreateOperatorParams and TInputDataVertexModel, and it looks like a call to the function FMetasoundFrontendRegistryContainer::Get()->RegisterPendingNodes(); is needed in the StartupModule() function of whichever module you’re putting the node in for it to get registered properly (if you’re not getting any errors or warnings in the log, but the node isn’t showing up as an option in the graph, this is probably the issue. It took me a while to figure out )
The following altered node template is working for me on 5.5:
#include "TestMetaSoundNode.h"
#include "MetasoundExecutableOperator.h" // TExecutableOperator class
#include "MetasoundPrimitives.h" // ReadRef and WriteRef descriptions for bool, int32, float, and string
#include "MetasoundNodeRegistrationMacro.h" // METASOUND_LOCTEXT and METASOUND_REGISTER_NODE macros
#include "MetasoundStandardNodesNames.h" // StandardNodes namespace
#include "MetasoundFacade.h" // FNodeFacade class, eliminates the need for a fair amount of boilerplate code
#include "MetasoundParamHelper.h" // METASOUND_PARAM and METASOUND_GET_PARAM family of macros
// Required for ensuring the node is supported by all languages in engine. Must be unique per MetaSound.
#define LOCTEXT_NAMESPACE "MetasoundStandardNodes_MetaSoundTutorialNode"
namespace Metasound
{
// Vertex Names - define your node's inputs and outputs here
namespace TutorialNodeNames
{
METASOUND_PARAM(InputAValue, "A", "Input value A.");
METASOUND_PARAM(InputBValue, "B", "Input value B.");
METASOUND_PARAM(OutputValue, "Sum of A and B", "The sum of A and B.");
}
class BOOLEANOPERATORMETASOUNDS_API FTestClass
{
FTestClass() {};
~FTestClass() {};
};
// Operator Class - defines the way your node is described, created and executed
class BOOLEANOPERATORMETASOUNDS_API FTutorialOperator : public TExecutableOperator<FTutorialOperator>
{
public:
// Constructor
FTutorialOperator(
const FFloatReadRef& InAValue,
const FFloatReadRef& InBValue)
: InputA(InAValue)
, InputB(InBValue)
, TutorialNodeOutput(FFloatWriteRef::CreateNew(*InputA + *InputB))
{
}
// Helper function for constructing vertex interface
static const FVertexInterface& DeclareVertexInterface()
{
using namespace TutorialNodeNames;
static const FVertexInterface Interface(
FInputVertexInterface(
TInputDataVertex<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputAValue)),
TInputDataVertex<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputBValue))
),
FOutputVertexInterface(
TOutputDataVertex<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValue))
)
);
return Interface;
}
// Retrieves necessary metadata about your node
static const FNodeClassMetadata& GetNodeInfo()
{
auto CreateNodeClassMetadata = []() -> FNodeClassMetadata
{
FVertexInterface NodeInterface = DeclareVertexInterface();
FNodeClassMetadata Metadata
{
FNodeClassName { StandardNodes::Namespace, "Tutorial Node", StandardNodes::AudioVariant },
1, // Major Version
0, // Minor Version
METASOUND_LOCTEXT("TutorialNodeDisplayName", "Tutorial Node"),
METASOUND_LOCTEXT("TutorialNodeDesc", "A simple node to demonstrate how to create new MetaSound nodes in C++. Adds two floats together"),
PluginAuthor,
PluginNodeMissingPrompt,
NodeInterface,
{ }, // Category Hierarchy
{ METASOUND_LOCTEXT("TutorialNodeKeyword", "Tutorial")}, // Keywords for searching
FNodeDisplayStyle{}
};
return Metadata;
};
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata();
return Metadata;
}
// Allows MetaSound graph to interact with your node's inputs
virtual FDataReferenceCollection GetInputs() const override
{
using namespace TutorialNodeNames;
FDataReferenceCollection InputDataReferences;
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InputAValue), InputA);
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InputBValue), InputB);
return InputDataReferences;
}
// Allows MetaSound graph to interact with your node's outputs
virtual FDataReferenceCollection GetOutputs() const override
{
using namespace TutorialNodeNames;
FDataReferenceCollection OutputDataReferences;
OutputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(OutputValue), TutorialNodeOutput);
return OutputDataReferences;
}
// Used to instantiate a new runtime instance of your node - DEPRECATED
/*
static TUniquePtr<IOperator> CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors)
{
using namespace TutorialNodeNames;
const Metasound::FDataReferenceCollection& InputCollection = InParams.InputDataReferences;
const Metasound::FInputVertexInterface& InputInterface = DeclareVertexInterface().GetInputInterface();
TDataReadReference<float> InputA = InputCollection.GetDataReadReferenceOrConstructWithVertexDefault<float>(InputInterface, METASOUND_GET_PARAM_NAME(InputAValue), InParams.OperatorSettings);
TDataReadReference<float> InputB = InputCollection.GetDataReadReferenceOrConstructWithVertexDefault<float>(InputInterface, METASOUND_GET_PARAM_NAME(InputBValue), InParams.OperatorSettings);
return MakeUnique<FTutorialOperator>(InputA, InputB);
}*/
//New Operator API
static TUniquePtr<IOperator> CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutErrors)
{
using namespace TutorialNodeNames;
const FInputVertexInterfaceData& InputData = InParams.InputData;
FFloatReadRef InputA = InputData.GetOrCreateDefaultDataReadReference<float>(METASOUND_GET_PARAM_NAME(InputAValue), InParams.OperatorSettings);
FFloatReadRef InputB = InputData.GetOrCreateDefaultDataReadReference<float>(METASOUND_GET_PARAM_NAME(InputBValue), InParams.OperatorSettings);
return MakeUnique<FTutorialOperator>(InputA, InputB);
}
// Primary node functionality
void Execute()
{
*TutorialNodeOutput = *InputA + *InputB;
}
private:
// Inputs
FFloatReadRef InputA;
FFloatReadRef InputB;
// Outputs
FFloatWriteRef TutorialNodeOutput;
};
// Node Class - Inheriting from FNodeFacade is recommended for nodes that have a static FVertexInterface
class BOOLEANOPERATORMETASOUNDS_API FTutorialNode : public FNodeFacade
{
public:
FTutorialNode(const FNodeInitData& InitData)
: FNodeFacade(InitData.InstanceName, InitData.InstanceID, TFacadeOperatorClass<FTutorialOperator>())
{
}
};
// Register node
METASOUND_REGISTER_NODE(FTutorialNode);
}
#undef LOCTEXT_NAMESPACE
But it also required putting the following in my plugin’s StartupModule function:
#include "BooleanOperatorMetaSounds.h"
#include "MetasoundDataTypeRegistrationMacro.h"
#define LOCTEXT_NAMESPACE "FBooleanOperatorMetaSoundsModule"
void FBooleanOperatorMetaSoundsModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
using namespace Metasound;
FMetasoundFrontendRegistryContainer::Get()->RegisterPendingNodes();
}