Tutorial: Creating MetaSound Nodes in C++ Quickstart

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

4 Likes

I just get the error:
Cannot open include file: ‘MetasoundExecutableOperator.h’: No such file or directory

2 Likes

Found this template on the Facebook audio group, maybe it helps paint the full picture:

4 Likes

You probably need to add the relevant Metasound module to your project’s .build.cs file. Something like:

PrivateDependencyModuleNames.AddRange(new string[]
{
	"MetasoundEngine",
	"MetasoundFrontend"
});
3 Likes

@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…

2 Likes

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…

1 Like

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.

2 Likes

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!

1 Like

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…

1 Like