// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundAudioBuffer.h" #include "CoreMinimal.h" #include "MetasoundFacade.h" #include "MetasoundExecutableOperator.h" #include "MetasoundPrimitives.h" #include "MetasoundStandardNodesCategories.h" #include "MetasoundStandardNodesNames.h" #include "MetasoundTrigger.h" #include "Internationalization/Text.h" #include "MetasoundParamHelper.h" #define LOCTEXT_NAMESPACE "MetasoundStandardNodes_MapRangeNode" namespace Metasound { namespace MapRangeVertexNames { METASOUND_PARAM(InputValueName, "In", "Input value to map."); METASOUND_PARAM(InputInRangeAName, "In Range A", "The min input value range."); METASOUND_PARAM(InputInRangeBName, "In Range B", "The max input value range."); METASOUND_PARAM(InputOutRangeAName, "Out Range A", "The min output value range."); METASOUND_PARAM(InputOutRangeBName, "Out Range B", "The max output value range."); METASOUND_PARAM(InputClampedName, "Clamped", "Whether or not to clamp the input to the specified input range."); METASOUND_PARAM(OutputValueName, "Out Value", "Mapped output value."); } namespace MetasoundMapRangeNodePrivate { using namespace MapRangeVertexNames; class FIntRange { public: static FName GetDataTypeName() { return TEXT("Int32"); } static FText GetNodeName() { return METASOUND_LOCTEXT("MapRange_Int32Name", "Map Range (Int32)"); } static FVertexInterface GetVertexInterface() { static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputValueName)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeAName), 0), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeBName), 100), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeAName), 0), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeBName), 100), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputClampedName), true) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValueName)) ) ); return DefaultInterface; } FIntRange(const FOperatorSettings& OperatorSettings, const FInputVertexInterfaceData& InInputData) : Value(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputValueName), OperatorSettings)) , InRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeAName), OperatorSettings)) , InRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeBName), OperatorSettings)) , OutRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OperatorSettings)) , OutRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OperatorSettings)) , bClamped(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputClampedName), OperatorSettings)) , OutputValue(TDataWriteReferenceFactory::CreateAny(OperatorSettings)) { } void BindInputs(FInputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputValueName), Value); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeAName), InRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeBName), InRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OutRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OutRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputClampedName), bClamped); } void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputValueName), OutputValue); } void DoMapping() { if (*bClamped) { *OutputValue = (int32)FMath::GetMappedRangeValueClamped(FVector2f{ (float)*InRangeA, (float)*InRangeB }, FVector2f{ (float)*OutRangeA, (float)*OutRangeB }, (float)*Value); } else { *OutputValue = (int32)FMath::GetMappedRangeValueUnclamped(FVector2f{ (float)*InRangeA, (float)*InRangeB }, FVector2f{ (float)*OutRangeA, (float)*OutRangeB }, (float)*Value); } } private: TDataReadReference Value; TDataReadReference InRangeA; TDataReadReference InRangeB; TDataReadReference OutRangeA; TDataReadReference OutRangeB; FBoolReadRef bClamped; TDataWriteReference OutputValue; }; class FFloatRange { public: static FName GetDataTypeName() { return TEXT("Float"); } static FText GetNodeName() { return METASOUND_LOCTEXT("MapRange_FloatName", "Map Range (Float)"); } static FVertexInterface GetVertexInterface() { static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputValueName)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeAName), 0.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeBName), 1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeAName), 0.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeBName), 1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputClampedName), true) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValueName)) ) ); return DefaultInterface; } FFloatRange(const FOperatorSettings& OperatorSettings, const FInputVertexInterfaceData& InInputData) : Value(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputValueName), OperatorSettings)) , InRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeAName), OperatorSettings)) , InRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeBName), OperatorSettings)) , OutRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OperatorSettings)) , OutRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OperatorSettings)) , bClamped(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputClampedName), OperatorSettings)) , OutputValue(TDataWriteReferenceFactory::CreateAny(OperatorSettings)) { } void BindInputs(FInputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputValueName),Value); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeAName), InRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeBName), InRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OutRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OutRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputClampedName), bClamped); } void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputValueName), OutputValue); } void DoMapping() { if (*bClamped) { *OutputValue = FMath::GetMappedRangeValueClamped(FVector2f{ *InRangeA, *InRangeB }, FVector2f{ *OutRangeA, *OutRangeB }, *Value); } else { *OutputValue = FMath::GetMappedRangeValueUnclamped(FVector2f{ *InRangeA, *InRangeB }, FVector2f{ *OutRangeA, *OutRangeB }, *Value); } } private: TDataReadReference Value; TDataReadReference InRangeA; TDataReadReference InRangeB; TDataReadReference OutRangeA; TDataReadReference OutRangeB; FBoolReadRef bClamped; TDataWriteReference OutputValue; }; class FAudioRange { public: static FName GetDataTypeName() { return TEXT("Audio"); } static FText GetNodeName() { return METASOUND_LOCTEXT("MapRange_AudioName", "Map Range (Audio)"); } static FVertexInterface GetVertexInterface() { static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputValueName)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeAName), -1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputInRangeBName), 1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeAName), -1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputOutRangeBName), 1.0f), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputClampedName), true) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValueName)) ) ); return DefaultInterface; } FAudioRange(const FOperatorSettings& OperatorSettings, const FInputVertexInterfaceData& InInputData) : Value(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputValueName), OperatorSettings)) , InRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeAName), OperatorSettings)) , InRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputInRangeBName), OperatorSettings)) , OutRangeA(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OperatorSettings)) , OutRangeB(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OperatorSettings)) , bClamped(InInputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputClampedName), OperatorSettings)) , OutputValue(TDataWriteReferenceFactory::CreateAny(OperatorSettings)) { } void BindInputs(FInputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputValueName), Value); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeAName), InRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputInRangeBName), InRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeAName), OutRangeA); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputOutRangeBName), OutRangeB); InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputClampedName), bClamped); } void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) { InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputValueName), OutputValue); } void DoMapping() { FAudioBuffer& OutBuffer = *OutputValue; float* OutBufferPtr = OutBuffer.GetData(); const FAudioBuffer& InBuffer = *Value; const float* InBufferPtr = InBuffer.GetData(); int32 NumSamples = InBuffer.Num(); FVector2f InputRange = { *InRangeA, *InRangeB }; FVector2f OutputRange = { *OutRangeA, *OutRangeB }; // TODO: SIMD this for (int32 i = 0; i < NumSamples; ++i) { OutBufferPtr[i] = FMath::GetMappedRangeValueClamped(InputRange, OutputRange, InBufferPtr[i]); } } private: TDataReadReference Value; TDataReadReference InRangeA; TDataReadReference InRangeB; TDataReadReference OutRangeA; TDataReadReference OutRangeB; FBoolReadRef bClamped; TDataWriteReference OutputValue; }; } template class TMapRangeOperator : public TExecutableOperator> { public: static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { FName OperatorName = TEXT("MapRange"); const FText NodeDescription = METASOUND_LOCTEXT("MapRangeDescription", "Maps an input value in the given input range to the given output range."); FVertexInterface NodeInterface = FMappingClass::GetVertexInterface(); FNodeClassMetadata Metadata { FNodeClassName { "MapRange", OperatorName, FMappingClass::GetDataTypeName() }, 1, // Major Version 0, // Minor Version FMappingClass::GetNodeName(), NodeDescription, PluginAuthor, PluginNodeMissingPrompt, NodeInterface, { NodeCategories::Math }, { }, FNodeDisplayStyle() }; return Metadata; }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { FMappingClass MappingObject(InParams.OperatorSettings, InParams.InputData); return MakeUnique>(MappingObject); } TMapRangeOperator(const FMappingClass& InMappingObject) : MappingObject(InMappingObject) { MappingObject.DoMapping(); } virtual ~TMapRangeOperator() = default; virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override { MappingObject.BindInputs(InOutVertexData); } virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override { MappingObject.BindOutputs(InOutVertexData); } void Reset(const IOperator::FResetParams& InParams) { MappingObject.DoMapping(); } void Execute() { MappingObject.DoMapping(); } private: FMappingClass MappingObject; }; template using TMapRangeNode = TNodeFacade>; using FMapRangeNodeInt32 = TMapRangeNode; METASOUND_REGISTER_NODE(FMapRangeNodeInt32) using FMapRangeNodeFloat = TMapRangeNode; METASOUND_REGISTER_NODE(FMapRangeNodeFloat) using FMapRangeNodeAudio = TMapRangeNode; METASOUND_REGISTER_NODE(FMapRangeNodeAudio) } #undef LOCTEXT_NAMESPACE