// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "DSP/BufferVectorOperations.h" #include "MetasoundAudioBuffer.h" #include "MetasoundBuilderInterface.h" #include "MetasoundDataReference.h" #include "MetasoundDataReferenceCollection.h" #include "MetasoundExecutableOperator.h" #include "MetasoundFacade.h" #include "MetasoundNode.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundNodeInterface.h" #include "MetasoundOperatorInterface.h" #include "MetasoundPrimitives.h" #include "MetasoundStandardNodesCategories.h" #include "MetasoundStandardNodesNames.h" #include "MetasoundTime.h" #include "MetasoundTrigger.h" #include "MetasoundVertex.h" #define LOCTEXT_NAMESPACE "MetasoundMathOpNode" #define DEFINE_METASOUND_MATHOP(OpName, DataTypeName, DataClass, Description, Keywords) \ class F##OpName##DataTypeName##Node \ : public ::Metasound::TMathOpNode, DataClass, DataClass> \ { \ public: \ static FNodeClassName GetClassName() { return { ::Metasound::StandardNodes::Namespace, TEXT(#OpName), TEXT(#DataTypeName)}; } \ static FText GetDisplayName() { return MathOpNames::Get##OpName##DisplayName(); } \ static FText GetDescription() { return Description; } \ static FName GetImageName() { return "MetasoundEditor.Graph.Node.Math." #OpName; } \ static TArray GetKeywords() { return Keywords; } \ F##OpName##DataTypeName##Node(const FNodeInitData& InInitData) : TMathOpNode(InInitData) { } \ }; #define DEFINE_METASOUND_OPERAND_TYPED_MATHOP(OpName, DataTypeName, DataClass, OperandTypeName, OperandDataClass, Description, Keywords) \ class F##OpName##DataTypeName##OperandTypeName##Node \ : public ::Metasound::TMathOpNode, DataClass, OperandDataClass> \ { \ public: \ static FNodeClassName GetClassName() { return {::Metasound::StandardNodes::Namespace, TEXT(#OpName), TEXT(#DataTypeName " by " #OperandTypeName)}; } \ static FText GetDisplayName() { return MathOpNames::Get##OpName##DisplayName(); } \ static FText GetDescription() { return Description; } \ static FName GetImageName() { return "MetasoundEditor.Graph.Node.Math." #OpName; } \ static TArray GetKeywords() { return Keywords; } \ F##OpName##DataTypeName##OperandTypeName##Node(const FNodeInitData& InInitData) : TMathOpNode(InInitData) { } \ }; namespace Metasound { namespace MathOpNames { static const FVertexName PrimaryOperandName = TEXT("PrimaryOperand"); static const FVertexName AdditionalOperandsName = TEXT("AdditionalOperands"); static const TArray AddKeywords = { LOCTEXT("AddMathKeyword", "+") }; static const TArray SubtractKeywords = { LOCTEXT("SubtractMathKeyword", "-") }; static const TArray MultiplyKeywords = { LOCTEXT("MultiplyMathKeyword", "*") }; static const TArray DivideKeywords = { LOCTEXT("DivideMathKeyword", "/") }; static const TArray PowerKeywords = { LOCTEXT("PowerMathKeyword", "^") }; static const TArray ModuloKeywords = { LOCTEXT("ModuloMathKeyword", "%") }; template const FText GetAddDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("AddNodeDisplayNamePattern", "Add ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetAddDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("AddNodeDisplayNamePattern", "Add ({0} to {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetSubtractDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("SubtractNodeDisplayNamePattern", "Subtract ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetSubtractDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("SubtractNodeOperandTypedDisplayNamePattern", "Subtract ({0} from {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetMultiplyDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("MultiplyNodeDisplayNamePattern", "Multiply ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetMultiplyDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("MultiplyNodeOperandTypedDisplayNamePattern", "Multiply ({0} by {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetDivideDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("DivideNodeDisplayNamePattern", "Divide ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetDivideDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("DivideNodeOperandTypedDisplayNamePattern", "Divide ({0} by {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetModuloDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("ModuloNodeDisplayNamePattern", "Modulo ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetModuloDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("ModuloNodeOperandTypedDisplayNamePattern", "Modulo ({0} by {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetPowerDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("PowerNodeDisplayNamePattern", "Power ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetPowerDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("PowerNodeOperandTypedDisplayNamePattern", "Power ({0} to the power of {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetLogarithmDisplayName() { static const FText DisplayName = FText::Format(LOCTEXT("LogNodeDisplayNamePattern", "Log ({0})"), GetMetasoundDataTypeDisplayText()); return DisplayName; } template const FText GetLogarithmDisplayName() { static const FText DisplayName = FText::Format( LOCTEXT("LogarithmNodeOperandTypedDisplayNamePattern", "Log ({0}-Base logarithm of {1})"), GetMetasoundDataTypeDisplayText(), GetMetasoundDataTypeDisplayText()); return DisplayName; } } template class TMathOpNode : public FNodeFacade { private: class TMathOperator : public TExecutableOperator { private: using TDataClassReadRef = TDataReadReference; using TOperandDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; TMathOpClass InstanceData; TDataClassReadRef PrimaryOperandRef; TArray AdditionalOperandRefs; TDataClassWriteRef ValueRef; public: static const FNodeClassMetadata& GetNodeInfo() { auto InitNodeInfo = []() -> FNodeClassMetadata { FNodeDisplayStyle DisplayStyle; DisplayStyle.ImageName = TDerivedClass::GetImageName(); DisplayStyle.bShowName = false; DisplayStyle.bShowInputNames = false; DisplayStyle.bShowOutputNames = false; FNodeClassMetadata Info; Info.ClassName = TDerivedClass::GetClassName(); Info.MajorVersion = 1; Info.MinorVersion = 0; Info.DisplayName = TDerivedClass::GetDisplayName(); Info.Description = TDerivedClass::GetDescription(); Info.Author = PluginAuthor; Info.PromptIfMissing = PluginNodeMissingPrompt; Info.DefaultInterface = TMathOpClass::GetVertexInterface(); Info.DisplayStyle = DisplayStyle; Info.CategoryHierarchy = { NodeCategories::Math }; Info.Keywords = TDerivedClass::GetKeywords(); return Info; }; static const FNodeClassMetadata Info = InitNodeInfo(); return Info; } void Execute() { TMathOpClass::Calculate(InstanceData, PrimaryOperandRef, AdditionalOperandRefs, ValueRef); } static TUniquePtr CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors) { const TMathOpNode& MathOpNode = static_cast(InParams.Node); const FInputVertexInterface& InputInterface = MathOpNode.GetVertexInterface().GetInputInterface(); FLiteral DefaultValue = FLiteral::CreateInvalid(); const FVertexName& PrimaryOperandName = MathOpNames::PrimaryOperandName; if (InputInterface.Contains(PrimaryOperandName)) { DefaultValue = InputInterface[PrimaryOperandName].GetDefaultLiteral(); } TDataClassReadRef PrimaryOperand = InParams.InputDataReferences.GetDataReadReferenceOrConstruct(MathOpNames::PrimaryOperandName, TMathOpClass::GetDefault(InParams.OperatorSettings, DefaultValue)); // TODO: Support dynamic number of inputs const FVertexName& OpName = MathOpNames::AdditionalOperandsName; if (InputInterface.Contains(OpName)) { DefaultValue = InputInterface[OpName].GetDefaultLiteral(); } TOperandDataClassReadRef Op1 = InParams.InputDataReferences.GetDataReadReferenceOrConstruct(OpName, TMathOpClass::GetDefaultOp(InParams.OperatorSettings, DefaultValue)); TArray AdditionalOperandRefs = { Op1 }; return MakeUnique(InParams.OperatorSettings, PrimaryOperand, AdditionalOperandRefs); } TMathOperator(const FOperatorSettings& InSettings, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands) : PrimaryOperandRef(InPrimaryOperand) , AdditionalOperandRefs(InAdditionalOperands) , ValueRef(TDataWriteReferenceFactory::CreateAny(InSettings)) { } virtual FDataReferenceCollection GetInputs() const override { FDataReferenceCollection InputDataReferences; InputDataReferences.AddDataReadReference(MathOpNames::PrimaryOperandName, PrimaryOperandRef); InputDataReferences.AddDataReadReference(MathOpNames::AdditionalOperandsName, AdditionalOperandRefs[0]); return InputDataReferences; } virtual FDataReferenceCollection GetOutputs() const override { FDataReferenceCollection OutputDataReferences; OutputDataReferences.AddDataReadReference(TEXT("Out"), TDataClassReadRef(ValueRef)); return OutputDataReferences; } }; public: TMathOpNode(const FNodeInitData& InInitData) : FNodeFacade(InInitData.InstanceName, InInitData.InstanceID, TFacadeOperatorClass()) { } }; // Standard Math Operations template class TMathOpAdd { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAddendInitialTooltip", "Initial addend."), static_cast(0)), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendsTooltip", "Additional addend(s)."), static_cast(0)) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Math operation result")) ) ); return Interface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(0); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(0); } static void Calculate(TMathOpAdd& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { *OutResult += *InAdditionalOperands[i]; } } }; template class TMathOpSubtract { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpMinuendTooltip", "Minuend."), static_cast(0)), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpSubtrahendsTooltip", "Subtrahend(s)."), static_cast(0)) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Subtraction result")) ) ); return Interface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(0); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(0); } static void Calculate(TMathOpSubtract& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { *OutResult -= *InAdditionalOperands[i]; } } }; template class TMathOpMultiply { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpInitMultiplicandTooltip", "Initial multiplicand."), static_cast(1)), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpMultiplicandsTooltip", "Additional multiplicand(s)."), static_cast(1)) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MultiplicationResultTooltip", "Multiplication result")) ) ); return Interface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static void Calculate(TMathOpMultiply& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { *OutResult *= *InAdditionalOperands[i]; } } }; template class TMathOpDivide { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpDividendTooltip", "Dividend."), static_cast(1)), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpDivisorsTooltip", "Divisor(s)."), static_cast(1)) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Math operation result")) ) ); return Interface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static void Calculate(TMathOpDivide& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const TDataClass& OperandValue = *InAdditionalOperands[i]; if (OperandValue == static_cast(0)) { // TODO: Error here return; } *OutResult /= OperandValue; } } }; template class TMathOpModulo { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpModuloDividendTooltip", "Dividend."), static_cast(1)), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpModuloDivisorsTooltip", "Divisor(s)."), static_cast(1)) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Resulting value")) ) ); return Interface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static void Calculate(TMathOpModulo& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; // TODO: chaining modulo operations doesn't make too much sense... how do we forbid additional operands in some math ops? for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const TDataClass& OperandValue = *InAdditionalOperands[i]; if (OperandValue == static_cast(0)) { // TODO: Error here return; } *OutResult %= OperandValue; } } }; template class TMathOpPower { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("Base", "The base of the power")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("Exponent", "The exponent to take the base to the power of")) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("Result", "Returns Base to the Exponent power")) ) ); return DefaultInterface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static void Calculate(TMathOpPower& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const TDataClass& OperandValue = *InAdditionalOperands[i]; *OutResult = FMath::Pow(*OutResult, OperandValue); } } }; template class TMathOpLogarithm { public: using TDataClassReadRef = TDataReadReference; using TDataClassWriteRef = TDataWriteReference; using TOperandDataClassReadRef = TDataReadReference; static const FVertexInterface& GetVertexInterface() { static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("Base", "The base of the logarithm")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("Value", "The value to find the logarithm of")) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("Result", "The logarithm of the inputted value")) ) ); return DefaultInterface; } static TDataClass GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static TOperandDataClass GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(1); } static void Calculate(TMathOpLogarithm& InInstanceData, const TDataClassReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, TDataClassWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; //TODO: Find out how to disallow it from additional inputs, it doesn't really make sense in the context of logarithms for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const TDataClass& OperandValue = *InAdditionalOperands[i]; *OutResult = FMath::LogX(FMath::Max(SMALL_NUMBER, *OutResult), FMath::Max(SMALL_NUMBER, OperandValue)); } } }; // Specialized Math Operations template <> class TMathOpAdd { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAddendTooltip", "First attend.")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Additional attends.")) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Math operation result")) ) ); return Interface; } static FAudioBuffer GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FAudioBuffer(InSettings.GetNumFramesPerBlock()); } static FAudioBuffer GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return GetDefault(InSettings, InVertexDefault); } static void Calculate(TMathOpAdd& InInstanceData, FAudioBufferReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FAudioBufferWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { OutResult->Zero(); return; } const int32 NumSamples = InAdditionalOperands[0]->Num(); if (!ensure(NumSamples == OutResult->Num())) { // TODO: Error OutResult->Zero(); return; } FMemory::Memcpy(OutResult->GetData(), InPrimaryOperand->GetData(), sizeof(float) * NumSamples); for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const FAudioBuffer& Operand = *InAdditionalOperands[i]; if (!ensure(NumSamples == Operand.Num())) { // TODO: Error OutResult->Zero(); return; } const float* OperandData = InAdditionalOperands[i]->GetData(); float* OutData = OutResult->GetData(); if (NumSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER) { for (int32 SampleIndex = 0; SampleIndex < NumSamples; ++SampleIndex) { OutData[SampleIndex] += OperandData[SampleIndex]; } } else { const float StartGain = 1.0f; const float EndGain = 1.0f; Audio::MixInBufferFast(OperandData, OutData, NumSamples, StartGain, EndGain); } } } }; template <> class TMathOpAdd { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAddendTooltip", "First attend."), 0.0f), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Additional attends."), 0.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Math operation result")) ) ); return Interface; } static FTime GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FTime(InVertexDefault.Value.Get()); } static FTime GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return GetDefault(InSettings, InVertexDefault); } static void Calculate(TMathOpAdd& InInstanceData, const FTimeReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FTimeWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { *OutResult += *InAdditionalOperands[i]; } } }; template <> class TMathOpAdd { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAddendAudioTooltip", "Audio Buffer to add offset(s) to.")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Float attends of which to offset buffer samples."), 0.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpAudioFloatAddOutTooltip", "Resulting buffer")) ) ); return Interface; } static FAudioBuffer GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FAudioBuffer(InSettings.GetNumFramesPerBlock()); } static float GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return InVertexDefault.Value.Get(); } static void Calculate(TMathOpAdd& InInstanceData, const FAudioBufferReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FAudioBufferWriteRef& OutResult) { FMemory::Memcpy(OutResult->GetData(), InPrimaryOperand->GetData(), sizeof(float) * OutResult->Num()); const int32 SIMDRemainder = OutResult->Num() % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER; const int32 SIMDCount = OutResult->Num() - SIMDRemainder; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { Audio::AddConstantToBufferInplace(OutResult->GetData(), SIMDCount, *InAdditionalOperands[i]); for (int32 j = SIMDCount; j < OutResult->Num(); ++j) { OutResult->GetData()[j] += *InAdditionalOperands[i]; } } } }; #if 0 // int64 has to be explicitly implemented as they are PODs that are not implemented as a literal base type. template <> class TMathOpAdd { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpInt64InitialAddendMinuendTooltip", "Initial Addend."), 0), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpInt64AddendsTooltip", "Additional Addend(s)."), 0) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Addition result")) ) ); return Interface; } static int32 GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static int32 GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static void Calculate(TMathOpAdd& InInstanceData, const FInt64ReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FInt64WriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int64 OperandValue = *InAdditionalOperands[i]; *OutResult += OperandValue; } } }; #endif template <> class TMathOpSubtract { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpBuffersMinuendTooltip", "Initial buffer to act as minuend.")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpSubtractBuffersSubtrahendsTooltip", "Additional buffers to act as subtrahend(s).")) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpSubtractBuffersOutTooltip", "Resulting buffer")) ) ); return Interface; } static FAudioBuffer GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FAudioBuffer(InSettings.GetNumFramesPerBlock()); } static FAudioBuffer GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return GetDefault(InSettings, InVertexDefault); } static void Calculate(TMathOpSubtract& InInstanceData, FAudioBufferReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FAudioBufferWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { OutResult->Zero(); return; } const int32 NumSamples = InAdditionalOperands[0]->Num(); if (!ensure(NumSamples == OutResult->Num())) { // TODO: Error OutResult->Zero(); return; } FMemory::Memcpy(OutResult->GetData(), InPrimaryOperand->GetData(), sizeof(float) * NumSamples); for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const FAudioBuffer& Operand = *InAdditionalOperands[i]; if (!ensure(NumSamples == Operand.Num())) { // TODO: Error OutResult->Zero(); return; } const float* OperandData = InAdditionalOperands[i]->GetData(); float* OutData = OutResult->GetData(); if (NumSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER) { for (int32 SampleIndex = 0; SampleIndex < NumSamples; ++SampleIndex) { OutData[i] -= OperandData[i]; } } else { Audio::BufferSubtractInPlace2Fast(OutData, OperandData, NumSamples); } } } }; template <> class TMathOpSubtract { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpTimeMinuendTooltip", "Time minuend."), 0.0f), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpTimeSubtrahendsTooltip", "Time subtrahends."), 0.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpTimeSubtractOutTooltip", "Resulting time value")) ) ); return Interface; } static FTime GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FTime(InVertexDefault.Value.Get()); } static FTime GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return GetDefault(InSettings, InVertexDefault); } static void Calculate(TMathOpSubtract& InInstanceData, const FTimeReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FTimeWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } *OutResult = *InPrimaryOperand; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { *OutResult -= *InAdditionalOperands[i]; } } }; #if 0 // int64 has to be explicitly implemented as they are PODs that are not implemented as a literal base type. template <> class TMathOpSubtract { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpMinuendTooltip", "Minuend."), 0), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpSubtrahendsTooltip", "Subtrahend(s)."), 0) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpOutTooltip", "Subtraction result")) ) ); return Interface; } static int32 GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static int32 GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static void Calculate(TMathOpSubtract& InInstanceData, const FInt64ReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FInt64WriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int64 OperandValue = *InAdditionalOperands[i]; *OutResult -= OperandValue; } } }; #endif template <> class TMathOpMultiply { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAudioInitMultiplicandTooltip", "Initial audio to multiply.")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpMultiplyAudioSubtrahendsTooltip", "Additional audio to multiply sample-by-sample.")) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpMultiplyAudioOutTooltip", "Resulting buffer")) ) ); return Interface; } static FAudioBuffer GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FAudioBuffer(InSettings.GetNumFramesPerBlock()); } static FAudioBuffer GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return GetDefault(InSettings, InVertexDefault); } static void Calculate(TMathOpMultiply& InInstanceData, const FAudioBufferReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FAudioBufferWriteRef& OutResult) { if (!ensure(!InAdditionalOperands.IsEmpty())) { OutResult->Zero(); return; } const int32 NumSamples = InPrimaryOperand->Num(); if (!ensure(NumSamples == OutResult->Num())) { // TODO: Error OutResult->Zero(); return; } FMemory::Memcpy(OutResult->GetData(), InPrimaryOperand->GetData(), sizeof(float) * NumSamples); for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const FAudioBuffer& Operand = *InAdditionalOperands[i]; if (!ensure(NumSamples == Operand.Num())) { // TODO: Error OutResult->Zero(); return; } const float* OperandData = InAdditionalOperands[i]->GetData(); float* OutData = OutResult->GetData(); if (NumSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER) { for (int32 SampleIndex = 0; SampleIndex < NumSamples; ++SampleIndex) { OutData[SampleIndex] += OperandData[SampleIndex]; } } else { Audio::MultiplyBuffersInPlace(OperandData, OutData, NumSamples); } } } }; template <> class TMathOpMultiply { bool bInit = false; float LastGain = 0.0f; public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpAudioMultiplyFloatTooltip", "Audio multiplicand.")), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Float multiplicand to apply sample-by-sample to audio. Interpolates over buffer size on value change."), 1.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpAudioFloatMultiplyOutTooltip", "Resulting buffer")) ) ); return Interface; } static FAudioBuffer GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FAudioBuffer(InSettings.GetNumFramesPerBlock()); } static float GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return InVertexDefault.Value.Get(); } static void Calculate(TMathOpMultiply& InInstanceData, const FAudioBufferReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FAudioBufferWriteRef& OutResult) { if (!InInstanceData.bInit) { InInstanceData.bInit = true; InInstanceData.LastGain = 1.0f; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { InInstanceData.LastGain *= *InAdditionalOperands[i]; } } FMemory::Memcpy(OutResult->GetData(), InPrimaryOperand->GetData(), sizeof(float) * InPrimaryOperand->Num()); float NewGain = 1.0f; for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int32 SIMDRemainder = OutResult->Num() % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER; const int32 SIMDCount = OutResult->Num() - SIMDRemainder; Audio::FadeBufferFast(OutResult->GetData(), SIMDCount, InInstanceData.LastGain, *InAdditionalOperands[i]); for (int32 j = SIMDCount; j < OutResult->Num(); ++j) { OutResult->GetData()[j] *= *InAdditionalOperands[j]; } NewGain *= *InAdditionalOperands[i]; } InInstanceData.LastGain = NewGain; } }; template <> class TMathOpMultiply { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpTimeMultiplyFloatTooltip", "Time multiplicand."), 1.0f), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Float multiplicand(s)."), 1.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpTimeMultiplyOutTooltip", "Resulting buffer")) ) ); return Interface; } static FTime GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FTime(InVertexDefault.Value.Get()); } static float GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return 1.0f; } static void Calculate(TMathOpMultiply& InInstanceData, const FTimeReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FTimeWriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const float OperandValue = *InAdditionalOperands[i]; *OutResult *= OperandValue; } } }; #if 0 // int64 has to be explicitly implemented as they are PODs that are not implemented as a literal base type. template <> class TMathOpMultiply { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpInitMultiplicandTooltip", "Initial multiplicand."), 1), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpMultiplicandsTooltip", "Additional multiplicand(s)."), 1) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MultiplicationResultTooltip", "Multiplication result")) ) ); return Interface; } static int32 GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static int32 GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static void Calculate(TMathOpMultiply& InInstanceData, const FInt64ReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FInt64WriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int64 OperandValue = *InAdditionalOperands[i]; *OutResult *= OperandValue; } } }; #endif template <> class TMathOpDivide { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpTimeMultiplyFloatTooltip", "Time multiplicand."), 1.0f), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpAddendAdditionalTooltip", "Float multiplicand(s)."), 1.0f) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpTimeMultiplyOutTooltip", "Resulting buffer")) ) ); return Interface; } static FTime GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return FTime(InVertexDefault.Value.Get()); } static float GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return 1.0f; } static void Calculate(TMathOpDivide& InInstanceData, const FTimeReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FTimeWriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const float OperandValue = *InAdditionalOperands[i]; if (OperandValue == 0.0f) { // TODO: Error here continue; } *OutResult /= OperandValue; } } }; #if 0 // int64 has to be explicitly implemented as they are PODs that are not implemented as a literal base type. template <> class TMathOpDivide { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpDivideDividendTooltip", "Dividend of operation."), 1), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpDivideDivisorTooltip", "Divisor of operation."), 1) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpDivideOutTooltip", "Resulting value")) ) ); return Interface; } static int64 GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { // TODO: we are truncating to int32, why do we need int64? return static_cast(InVertexDefault.Value.Get()); } static int64 GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { // TODO: we are truncating to int32, why do we need in64? return static_cast(InVertexDefault.Value.Get()); } static void Calculate(TMathOpDivide& InInstanceData, const FInt64ReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FInt64WriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int64 OperandValue = *InAdditionalOperands[i]; if (OperandValue == 0) { // TODO: Error here continue; } *OutResult /= OperandValue; } } }; // int64 has to be explicitly implemented as they are PODs that are not implemented as a literal base type. template <> class TMathOpModulo { public: static const FVertexInterface& GetVertexInterface() { static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertexModel(MathOpNames::PrimaryOperandName, LOCTEXT("MathOpModuloDividendTooltip", "Dividend"), 1), TInputDataVertexModel(MathOpNames::AdditionalOperandsName, LOCTEXT("MathOpModuluDivisorTooltip", "Divisor(s)"), 1) ), FOutputVertexInterface( TOutputDataVertexModel(TEXT("Out"), LOCTEXT("MathOpModuloOutTooltip", "Resulting value")) ) ); return Interface; } static int64 GetDefault(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static int64 GetDefaultOp(const FOperatorSettings& InSettings, const FLiteral& InVertexDefault) { return static_cast(InVertexDefault.Value.Get()); } static void Calculate(TMathOpModulo& InInstanceData, const FInt64ReadRef& InPrimaryOperand, const TArray& InAdditionalOperands, FInt64WriteRef& OutResult) { *OutResult = *InPrimaryOperand; if (!ensure(!InAdditionalOperands.IsEmpty())) { return; } for (int32 i = 0; i < InAdditionalOperands.Num(); ++i) { const int64 OperandValue = *InAdditionalOperands[i]; if (OperandValue == 0) { // TODO: Error here continue; } *OutResult %= OperandValue; } } }; #endif // Definitions DEFINE_METASOUND_MATHOP(Add, Float, float, LOCTEXT("Metasound_MathAddFloatNodeDescription", "Adds floats."), MathOpNames::AddKeywords) DEFINE_METASOUND_MATHOP(Add, Int32, int32, LOCTEXT("Metasound_MathAddInt32NodeDescription", "Adds int32s."), MathOpNames::AddKeywords) //DEFINE_METASOUND_MATHOP(Add, Int64, int64, LOCTEXT("Metasound_MathAddInt64NodeDescription", "Adds int64s."), MathOpNames::AddKeywords) DEFINE_METASOUND_MATHOP(Add, Audio, FAudioBuffer, LOCTEXT("Metasound_MathAddBufferNodeDescription", "Adds buffers together by sample."), MathOpNames::AddKeywords) DEFINE_METASOUND_MATHOP(Add, Time, FTime, LOCTEXT("Metasound_MathAddTimeNodeDescription", "Adds time values."), MathOpNames::AddKeywords) DEFINE_METASOUND_OPERAND_TYPED_MATHOP(Add, Audio, FAudioBuffer, Float, float, LOCTEXT("Metasound_MathAddAudioFloatNodeDescription", "Add floats to buffer sample-by-sample."), MathOpNames::AddKeywords) DEFINE_METASOUND_MATHOP(Subtract, Float, float, LOCTEXT("Metasound_MathSubractFloatNodeDescription", "Subtracts floats."), MathOpNames::SubtractKeywords) DEFINE_METASOUND_MATHOP(Subtract, Int32, int32, LOCTEXT("Metasound_MathSubractInt32NodeDescription", "Subtracts int32s."), MathOpNames::SubtractKeywords) //DEFINE_METASOUND_MATHOP(Subtract, Int64, int64, LOCTEXT("Metasound_MathSubractInt64NodeDescription", "Subtracts int64s."), MathOpNames::SubtractKeywords) DEFINE_METASOUND_MATHOP(Subtract, Audio, FAudioBuffer, LOCTEXT("Metasound_MathSubtractBufferNodeDescription", "Subtracts buffers sample-by-sample."), MathOpNames::SubtractKeywords) DEFINE_METASOUND_MATHOP(Subtract, Time, FTime, LOCTEXT("Metasound_MathSubractTimeNodeDescription", "Subtracts time values."), MathOpNames::SubtractKeywords) DEFINE_METASOUND_MATHOP(Multiply, Float, float, LOCTEXT("Metasound_MathMultiplyFloatNodeDescription", "Multiplies floats."), MathOpNames::MultiplyKeywords) DEFINE_METASOUND_MATHOP(Multiply, Int32, int32, LOCTEXT("Metasound_MathMultiplyInt32NodeDescription", "Multiplies int32s."), MathOpNames::MultiplyKeywords) //DEFINE_METASOUND_MATHOP(Multiply, Int64, int64, LOCTEXT("Metasound_MathMultiplyInt64NodeDescription", "Multiplies int64s."), MathOpNames::MultiplyKeywords) DEFINE_METASOUND_MATHOP(Multiply, Audio, FAudioBuffer, LOCTEXT("Metasound_MathMultiplyBufferNodeDescription", "Multiplies buffers together sample-by-sample."), MathOpNames::MultiplyKeywords) DEFINE_METASOUND_OPERAND_TYPED_MATHOP(Multiply, Audio, FAudioBuffer, Float, float, LOCTEXT("Metasound_MathMultiplyAudioByFloatDescription", "Multiplies buffer by float scalars."), MathOpNames::MultiplyKeywords) DEFINE_METASOUND_OPERAND_TYPED_MATHOP(Multiply, Time, FTime, Float, float, LOCTEXT("Metasound_MathMultiplyTimeNodeDescription", "Scales time by floats."), MathOpNames::MultiplyKeywords) DEFINE_METASOUND_MATHOP(Divide, Float, float, LOCTEXT("Metasound_MathDivideFloatNodeDescription", "Divide float by another float."), MathOpNames::DivideKeywords) DEFINE_METASOUND_MATHOP(Divide, Int32, int32, LOCTEXT("Metasound_MathDivideInt32NodeDescription", "Divide int32 by another int32."), MathOpNames::DivideKeywords) //DEFINE_METASOUND_MATHOP(Divide, Int64, int64, LOCTEXT("Metasound_MathDivideInt64NodeDescription", "Divide int64 by another int64."), MathOpNames::DivideKeywords) DEFINE_METASOUND_OPERAND_TYPED_MATHOP(Divide, Time, FTime, Float, float, LOCTEXT("Metasound_MathDivideTimeNodeDescription", "Divides time by floats."), MathOpNames::DivideKeywords) DEFINE_METASOUND_MATHOP(Modulo, Int32, int32, LOCTEXT("Metasound_MathModulusInt32NodeDescription", "Modulo int32 by another int32."), MathOpNames::ModuloKeywords) //DEFINE_METASOUND_MATHOP(Modulo, Int64, int64, LOCTEXT("Metasound_MathModulusInt64NodeDescription", "Modulo int64 by another int64."), MathOpNames::ModuloKeywords) DEFINE_METASOUND_MATHOP(Power, Float, float, LOCTEXT("Metasound_MathPowerFloatNodeDescription", "Raise float to the power of another float."), MathOpNames::PowerKeywords) DEFINE_METASOUND_MATHOP(Logarithm, Float, float, LOCTEXT("Metasound_MathLogarithmFloatNodeDescription", "Calculate float-base logarithm of another float."), TArray()) METASOUND_REGISTER_NODE(FAddFloatNode) METASOUND_REGISTER_NODE(FAddInt32Node) //METASOUND_REGISTER_NODE(FAddInt64Node) METASOUND_REGISTER_NODE(FAddTimeNode) METASOUND_REGISTER_NODE(FAddAudioNode) METASOUND_REGISTER_NODE(FAddAudioFloatNode) METASOUND_REGISTER_NODE(FSubtractFloatNode) METASOUND_REGISTER_NODE(FSubtractInt32Node) //METASOUND_REGISTER_NODE(FSubtractInt64Node) METASOUND_REGISTER_NODE(FSubtractTimeNode) METASOUND_REGISTER_NODE(FSubtractAudioNode) METASOUND_REGISTER_NODE(FMultiplyAudioNode) METASOUND_REGISTER_NODE(FMultiplyAudioFloatNode) METASOUND_REGISTER_NODE(FMultiplyFloatNode) METASOUND_REGISTER_NODE(FMultiplyInt32Node) //METASOUND_REGISTER_NODE(FMultiplyInt64Node) METASOUND_REGISTER_NODE(FMultiplyTimeFloatNode) METASOUND_REGISTER_NODE(FDivideFloatNode) METASOUND_REGISTER_NODE(FDivideInt32Node) //METASOUND_REGISTER_NODE(FDivideInt64Node) METASOUND_REGISTER_NODE(FDivideTimeFloatNode) METASOUND_REGISTER_NODE(FModuloInt32Node) //METASOUND_REGISTER_NODE(FModuloInt64Node) METASOUND_REGISTER_NODE(FPowerFloatNode) METASOUND_REGISTER_NODE(FLogarithmFloatNode) } #undef LOCTEXT_NAMESPACE // MetasoundMathOpNode