2023-10-25 17:25:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Interfaces/MetasoundFrontendSourceInterface.h"
# include "MetasoundDataFactory.h"
# include "MetasoundEngineNodesNames.h"
# include "MetasoundEnumRegistrationMacro.h"
# include "MetasoundExecutableOperator.h"
# include "MetasoundFacade.h"
# include "MetasoundNodeRegistrationMacro.h"
# include "MetasoundPrimitives.h"
# include "MetasoundParamHelper.h"
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundWaveTable.h"
# include "WaveTable.h"
# include "WaveTableSampler.h"
# define LOCTEXT_NAMESPACE "MetasoundStandardNodes"
namespace Metasound
{
namespace WaveTableBankEvaluateNode
{
2023-10-26 18:50:32 -04:00
METASOUND_PARAM ( WaveTableBankEval_BankParam , " WaveTableBank " , " The WaveTableBank to evaluate " ) ;
METASOUND_PARAM ( WaveTableBankEval_InputParam , " Input " , " The X input with which to evaluate the wavetable ([-1, 1] or [0, 1] depending on Bank's 'bipolar' setting) " ) ;
METASOUND_PARAM ( WaveTableBankEval_IndexParam , " Index " , " The table index to interpolate and evaluate the result of (wraps over number of entries) " ) ;
METASOUND_PARAM ( WaveTableBankEval_OutParam , " Output " , " The linearly mixed value of the provided WaveTableBank's applicable entries " ) ;
2023-10-25 17:25:17 -04:00
} // WaveTableBankEvaluateNode
class FMetasoundWaveTableBankEvaluateNodeOperator : public TExecutableOperator < FMetasoundWaveTableBankEvaluateNodeOperator >
{
public :
static const FVertexInterface & GetDefaultInterface ( )
{
using namespace WaveTable ;
using namespace WaveTableBankEvaluateNode ;
static const FVertexInterface DefaultInterface (
FInputVertexInterface (
2023-10-26 18:50:32 -04:00
TInputDataVertex < FWaveTableBankAsset > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( WaveTableBankEval_BankParam ) ) ,
TInputDataVertex < float > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( WaveTableBankEval_InputParam ) , 0.0f ) ,
TInputDataVertex < float > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( WaveTableBankEval_IndexParam ) , 0.0f ) ,
2023-10-25 17:25:17 -04:00
TInputDataVertex < FEnumWaveTableInterpolationMode > ( " Interpolation " , FDataVertexMetadata
{
LOCTEXT ( " MetasoundWaveTableBankEvaluateNode_InterpDescription " , " How interpolation occurs between WaveTable values. " ) ,
LOCTEXT ( " MetasoundWaveTableBankEvaluateNode_Interp " , " Interpolation " ) ,
true /* bIsAdvancedDisplay */
} , static_cast < int32 > ( FWaveTableSampler : : EInterpolationMode : : Linear ) )
) ,
FOutputVertexInterface (
2023-10-26 18:50:32 -04:00
TOutputDataVertex < float > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( WaveTableBankEval_OutParam ) )
2023-10-25 17:25:17 -04:00
)
) ;
return DefaultInterface ;
}
static const FNodeClassMetadata & GetNodeInfo ( )
{
auto CreateNodeClassMetadata = [ ] ( ) - > FNodeClassMetadata
{
FNodeClassMetadata Metadata
{
{ EngineNodes : : Namespace , " WaveTableBankEvaluate " , " " } ,
1 , // Major Version
0 , // Minor Version
LOCTEXT ( " MetasoundWaveTableBankEvaluateNode_Name " , " Evaluate WaveTableBank " ) ,
LOCTEXT ( " MetasoundWaveTableBankEvaluateNode_Description " ,
" Evaluates a WaveTableBank's given entires for a given input value, linearly interpolating inline between using the provided index. More performant "
" than using 'WaveTableGet' and using the 'WaveTableEvaluate' nodes as no resulting WaveTable is generated any time the input float index is changed. " ) ,
PluginAuthor ,
PluginNodeMissingPrompt ,
GetDefaultInterface ( ) ,
{ NodeCategories : : WaveTables } ,
2023-10-26 14:42:05 -04:00
{ NodeCategories : : Envelopes , METASOUND_LOCTEXT ( " WaveTableBankEvaluateCurveKeyword " , " Curve " ) } ,
2023-10-25 17:25:17 -04:00
{ }
} ;
return Metadata ;
} ;
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata ( ) ;
return Metadata ;
}
static TUniquePtr < IOperator > CreateOperator ( const FBuildOperatorParams & InParams , FBuildResults & OutResults )
{
using namespace WaveTable ;
const FInputVertexInterfaceData & InputData = InParams . InputData ;
FWaveTableBankAssetReadRef InWaveTableReadRef = InputData . GetOrConstructDataReadReference < FWaveTableBankAsset > ( " WaveTableBank " ) ;
FFloatReadRef InInputReadRef = InputData . GetOrCreateDefaultDataReadReference < float > ( " Input " , InParams . OperatorSettings ) ;
FFloatReadRef InIndexReadRef = InputData . GetOrCreateDefaultDataReadReference < float > ( " Index " , InParams . OperatorSettings ) ;
FEnumWaveTableInterpModeReadRef InInterpReadRef = InputData . GetOrCreateDefaultDataReadReference < FEnumWaveTableInterpolationMode > ( " Interpolation " , InParams . OperatorSettings ) ;
return MakeUnique < FMetasoundWaveTableBankEvaluateNodeOperator > ( InParams , InWaveTableReadRef , InInputReadRef , InIndexReadRef , InInterpReadRef ) ;
}
FMetasoundWaveTableBankEvaluateNodeOperator (
const FBuildOperatorParams & InParams ,
const FWaveTableBankAssetReadRef & InWaveTableBankReadRef ,
const FFloatReadRef & InInputReadRef ,
const FFloatReadRef & InIndexReadRef ,
const FEnumWaveTableInterpModeReadRef & InInterpModeReadRef )
: WaveTableBankReadRef ( InWaveTableBankReadRef )
, InputReadRef ( InInputReadRef )
, IndexReadRef ( InIndexReadRef )
, InterpModeReadRef ( InInterpModeReadRef )
, OutWriteRef ( TDataWriteReferenceFactory < float > : : CreateAny ( InParams . OperatorSettings ) )
{
Reset ( InParams ) ;
}
virtual ~ FMetasoundWaveTableBankEvaluateNodeOperator ( ) = default ;
virtual void BindInputs ( FInputVertexInterfaceData & InOutVertexData ) override
{
using namespace WaveTableBankEvaluateNode ;
2023-10-26 18:50:32 -04:00
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( WaveTableBankEval_BankParam ) , WaveTableBankReadRef ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( WaveTableBankEval_InputParam ) , InputReadRef ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( WaveTableBankEval_IndexParam ) , IndexReadRef ) ;
2023-10-25 17:25:17 -04:00
InOutVertexData . BindReadVertex ( " Interpolation " , InterpModeReadRef ) ;
}
virtual void BindOutputs ( FOutputVertexInterfaceData & InOutVertexData ) override
{
using namespace WaveTableBankEvaluateNode ;
2023-10-26 18:50:32 -04:00
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( WaveTableBankEval_OutParam ) , OutWriteRef ) ;
2023-10-25 17:25:17 -04:00
}
virtual FDataReferenceCollection GetInputs ( ) const override
{
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
FDataReferenceCollection InputDataReferences ;
return InputDataReferences ;
}
virtual FDataReferenceCollection GetOutputs ( ) const override
{
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
FDataReferenceCollection OutputDataReferences ;
return OutputDataReferences ;
}
void Execute ( )
{
using namespace WaveTable ;
const FWaveTableBankAsset & WaveTableBankAsset = * WaveTableBankReadRef ;
FWaveTableBankAssetProxyPtr Proxy = WaveTableBankAsset . GetProxy ( ) ;
2023-10-26 14:42:05 -04:00
float NewIndex = 0.f ;
2023-10-26 18:50:32 -04:00
2023-11-03 16:22:44 -04:00
if ( ! WaveTableBankAsset . IsValid ( ) )
{
* OutWriteRef = 0.f ; // same as Reset() state
return ;
}
2023-10-26 18:50:32 -04:00
const float Min = WaveTableBankAsset - > IsBipolar ( ) ? - 1.f : 0.f ;
const float Input = FMath : : Clamp ( * InputReadRef , Min , 1.f ) ;
2023-10-26 14:42:05 -04:00
if ( ! ResolveNextComputeIndex ( Proxy , Input , NewIndex ) )
2023-10-25 17:25:17 -04:00
{
return ;
}
const TArray < FWaveTableData > & WaveTables = WaveTableBankAsset - > GetWaveTableData ( ) ;
const int32 IndexFloor = FMath : : FloorToInt32 ( NewIndex ) % WaveTables . Num ( ) ;
const int32 IndexCeil = FMath : : CeilToInt32 ( NewIndex ) % WaveTables . Num ( ) ;
const FWaveTableData & IndexFloorTable = WaveTables [ IndexFloor ] ;
float IndexFloorValue = 0.0f ;
constexpr FWaveTableSampler : : ESingleSampleMode SampleMode = FWaveTableSampler : : ESingleSampleMode : : Hold ;
Sampler . Reset ( ) ;
Sampler . SetPhase ( Input ) ;
Sampler . Process ( IndexFloorTable , IndexFloorValue , SampleMode ) ;
if ( IndexFloor ! = IndexCeil )
{
const FWaveTableData & IndexCeilTable = WaveTables [ IndexCeil ] ;
2023-10-26 14:42:05 -04:00
float IndexCeilValue = 0.f ;
2023-10-25 17:25:17 -04:00
Sampler . Reset ( ) ;
Sampler . SetPhase ( Input ) ;
Sampler . Process ( IndexCeilTable , IndexCeilValue , SampleMode ) ;
const float Fractional = FMath : : Frac ( NewIndex ) ;
2023-10-26 14:42:05 -04:00
CachedState . Value = ( IndexFloorValue * ( 1.f - Fractional ) ) + ( IndexCeilValue * Fractional ) ;
2023-10-25 17:25:17 -04:00
}
else
{
2023-10-26 14:42:05 -04:00
CachedState . Value = IndexFloorValue ;
2023-10-25 17:25:17 -04:00
}
2023-10-26 14:42:05 -04:00
* OutWriteRef = CachedState . Value ;
2023-10-25 17:25:17 -04:00
}
void Reset ( const IOperator : : FResetParams & InParams )
{
using namespace WaveTable ;
2023-10-26 14:42:05 -04:00
2023-10-25 17:25:17 -04:00
FWaveTableSampler : : FSettings Settings ;
Settings . Freq = 0.0f ; // Sampler phase is manually progressed via this node
Sampler = FWaveTableSampler ( MoveTemp ( Settings ) ) ;
2023-10-26 14:42:05 -04:00
CachedState = { } ;
2023-10-25 17:25:17 -04:00
* OutWriteRef = 0.f ;
}
private :
// Returns true if new computation is required, setting OutIndex to index to compute.
// Returns false if new computation isn't required, resetting output & cached data accordingly.
2023-10-26 14:42:05 -04:00
bool ResolveNextComputeIndex ( const FWaveTableBankAssetProxyPtr & Proxy , float Input , float & OutIndex )
2023-10-25 17:25:17 -04:00
{
using namespace WaveTable ;
if ( ! Proxy . IsValid ( ) )
{
2023-10-26 14:42:05 -04:00
CachedState = { } ;
* OutWriteRef = 0.f ;
2023-10-25 17:25:17 -04:00
return false ;
}
const float Index = * IndexReadRef ;
const FWaveTableSampler : : EInterpolationMode NewInterpolationMode = * InterpModeReadRef ;
2023-10-26 14:42:05 -04:00
if ( CachedState . InterpolationMode = = NewInterpolationMode )
2023-10-25 17:25:17 -04:00
{
2023-10-26 14:42:05 -04:00
if ( FMath : : IsNearlyEqual ( Index , CachedState . Index ) )
2023-10-25 17:25:17 -04:00
{
2023-10-26 14:42:05 -04:00
if ( FMath : : IsNearlyEqual ( Input , CachedState . Input ) )
{
* OutWriteRef = CachedState . Value ;
return false ;
}
2023-10-25 17:25:17 -04:00
}
}
Sampler . SetInterpolationMode ( NewInterpolationMode ) ;
OutIndex = FMath : : Abs ( Index ) ; // Avoids fractional, interpolative flip at zero crossing
2023-10-26 14:42:05 -04:00
CachedState . InterpolationMode = NewInterpolationMode ;
CachedState . Input = Input ;
CachedState . Index = OutIndex ;
2023-10-25 17:25:17 -04:00
return true ;
}
FWaveTableBankAssetReadRef WaveTableBankReadRef ;
FFloatReadRef InputReadRef ;
FFloatReadRef IndexReadRef ;
FEnumWaveTableInterpModeReadRef InterpModeReadRef ;
WaveTable : : FWaveTableSampler Sampler ;
FFloatWriteRef OutWriteRef ;
2023-10-26 14:42:05 -04:00
struct FCachedState
{
WaveTable : : FWaveTableSampler : : EInterpolationMode InterpolationMode = WaveTable : : FWaveTableSampler : : EInterpolationMode : : COUNT ;
float Index = TNumericLimits < float > : : Max ( ) ;
float Input = TNumericLimits < float > : : Max ( ) ;
float Value = TNumericLimits < float > : : Max ( ) ;
} CachedState ;
2023-10-25 17:25:17 -04:00
} ;
class FMetasoundWaveTableBankEvaluateNode : public FNodeFacade
{
public :
FMetasoundWaveTableBankEvaluateNode ( const FNodeInitData & InInitData )
: FNodeFacade ( InInitData . InstanceName , InInitData . InstanceID , TFacadeOperatorClass < FMetasoundWaveTableBankEvaluateNodeOperator > ( ) )
{
}
virtual ~ FMetasoundWaveTableBankEvaluateNode ( ) = default ;
} ;
METASOUND_REGISTER_NODE ( FMetasoundWaveTableBankEvaluateNode )
} // namespace Metasound
# undef LOCTEXT_NAMESPACE // MetasoundStandardNodes