2022-07-06 15:11:11 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2022-08-19 12:14:31 -04:00
# include "Interfaces/MetasoundFrontendSourceInterface.h"
2022-07-06 15:11:11 -04:00
# include "MetasoundDataFactory.h"
# include "MetasoundEngineNodesNames.h"
# include "MetasoundExecutableOperator.h"
# include "MetasoundFacade.h"
# include "MetasoundNodeRegistrationMacro.h"
# include "MetasoundPrimitives.h"
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundStandardNodesNames.h"
# include "MetasoundTrace.h"
# include "MetasoundTrigger.h"
# include "MetasoundWaveTable.h"
2022-08-19 12:14:31 -04:00
# include "WaveTableSampler.h"
2022-07-06 15:11:11 -04:00
# define LOCTEXT_NAMESPACE "MetasoundStandardNodes"
namespace Metasound
{
class FMetasoundWaveTableOscillatorNodeOperator : public TExecutableOperator < FMetasoundWaveTableOscillatorNodeOperator >
{
public :
static const FVertexInterface & GetDefaultInterface ( )
{
using namespace WaveTable ;
static const FVertexInterface DefaultInterface (
FInputVertexInterface (
2022-09-30 11:18:31 -04:00
TInputDataVertex < FTrigger > ( " Play " , FDataVertexMetadata { LOCTEXT ( " MetasoundWaveTableOscillatorNode_InputPlayDesc " , " Plays the oscillator (block rate) " ) } ) ,
TInputDataVertex < FTrigger > ( " Stop " , FDataVertexMetadata { LOCTEXT ( " MetasoundWaveTableOscillatorNode_InputStopDesc " , " Stops the oscillator (block rate) " ) } ) ,
TInputDataVertex < FWaveTable > ( " WaveTable " , FDataVertexMetadata { LOCTEXT ( " MetasoundWaveTableOscillatorNode_InputWaveTableDesc " , " WaveTable " ) } ) ,
TInputDataVertex < FTrigger > ( " Sync " , FDataVertexMetadata
{
LOCTEXT ( " MetasoundWaveTableOscillatorNode_InputSyncDesc " , " Restarts playing the WaveTable on the trigger boundary (sample rate) " ) ,
LOCTEXT ( " MetasoundWaveTableOscillatorNode_InputSyncName " , " Sync " ) ,
true /* bIsAdvancedDisplay */
} ) ,
TInputDataVertex < float > ( " Freq " , FDataVertexMetadata { LOCTEXT ( " MetasoundWaveTableOscillatorNode_FreqDesc " , " Frequency (number of times to sample one period of wavetable per second) [-20000Hz, 20000Hz] " ) } , 440.0f ) ,
2022-07-06 15:11:11 -04:00
TInputDataVertex < FAudioBuffer > ( " PhaseMod " , FDataVertexMetadata
{
2022-08-16 19:21:52 -04:00
LOCTEXT ( " MetasoundWaveTableOscillatorNode_PhaseModDescription " , " Modulation audio source for modulating oscillation phase of provided table. A value of 0 is no phase modulation and 1 a full table length (360 degrees) of phase shift. " ) ,
2022-07-06 15:11:11 -04:00
LOCTEXT ( " MetasoundWaveTableOscillatorNode_PhaseMod " , " Phase Modulator " ) ,
true /* bIsAdvancedDisplay */
} )
) ,
FOutputVertexInterface (
TOutputDataVertex < FAudioBuffer > ( " Out " , FDataVertexMetadata { LOCTEXT ( " MetasoundWaveTableOscillatorNode_Output " , " Out " ) } )
)
) ;
return DefaultInterface ;
}
static const FNodeClassMetadata & GetNodeInfo ( )
{
auto CreateNodeClassMetadata = [ ] ( ) - > FNodeClassMetadata
{
FNodeClassMetadata Metadata
{
{ EngineNodes : : Namespace , " WaveTableOscillator " , " " } ,
1 , // Major Version
0 , // Minor Version
LOCTEXT ( " MetasoundWaveTableOscillatorNode_Name " , " WaveTable Oscillator " ) ,
LOCTEXT ( " MetasoundWaveTableOscillatorNode_Description " , " Reads through the given WaveTable at the provided frequency. " ) ,
PluginAuthor ,
PluginNodeMissingPrompt ,
GetDefaultInterface ( ) ,
2023-08-02 14:44:13 -04:00
{ NodeCategories : : WaveTables } ,
2022-08-17 16:42:56 -04:00
{ METASOUND_LOCTEXT ( " WaveTableOscillatorSynthesisKeyword " , " Synthesis " ) } ,
2022-07-06 15:11:11 -04:00
{ }
} ;
return Metadata ;
} ;
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata ( ) ;
return Metadata ;
}
2023-10-13 20:28:22 -04:00
static TUniquePtr < IOperator > CreateOperator ( const FBuildOperatorParams & InParams , FBuildResults & OutResults )
2022-07-06 15:11:11 -04:00
{
using namespace WaveTable ;
2023-10-13 20:28:22 -04:00
const FInputVertexInterfaceData & InputData = InParams . InputData ;
2022-07-06 15:11:11 -04:00
2023-10-13 20:28:22 -04:00
FWaveTableReadRef InWaveTableReadRef = InputData . GetOrConstructDataReadReference < FWaveTable > ( " WaveTable " ) ;
FTriggerReadRef InPlayReadRef = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( " Play " , InParams . OperatorSettings ) ;
FTriggerReadRef InStopReadRef = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( " Stop " , InParams . OperatorSettings ) ;
FTriggerReadRef InSyncReadRef = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( " Sync " , InParams . OperatorSettings ) ;
FFloatReadRef InFreqReadRef = InputData . GetOrCreateDefaultDataReadReference < float > ( " Freq " , InParams . OperatorSettings ) ;
2022-07-06 15:11:11 -04:00
TOptional < FAudioBufferReadRef > InPhaseModReadRef ;
2023-10-13 20:28:22 -04:00
if ( const FAnyDataReference * DataRef = InputData . FindDataReference ( " PhaseMod " ) )
2022-07-06 15:11:11 -04:00
{
2023-10-13 20:28:22 -04:00
InPhaseModReadRef = DataRef - > GetDataReadReference < FAudioBuffer > ( ) ;
2022-07-06 15:11:11 -04:00
}
return MakeUnique < FMetasoundWaveTableOscillatorNodeOperator > ( InParams , InWaveTableReadRef , InPlayReadRef , InStopReadRef , InSyncReadRef , InFreqReadRef , MoveTemp ( InPhaseModReadRef ) ) ;
}
FMetasoundWaveTableOscillatorNodeOperator (
2023-10-13 20:28:22 -04:00
const FBuildOperatorParams & InParams ,
2022-07-06 15:11:11 -04:00
const FWaveTableReadRef & InWaveTableReadRef ,
const FTriggerReadRef & InPlayReadRef ,
const FTriggerReadRef & InStopReadRef ,
const FTriggerReadRef & InSyncReadRef ,
const FFloatReadRef & InFreqReadRef ,
TOptional < FAudioBufferReadRef > & & InPhaseModReadRef
)
: WaveTableReadRef ( InWaveTableReadRef )
, PlayReadRef ( InPlayReadRef )
, StopReadRef ( InStopReadRef )
, SyncReadRef ( InSyncReadRef )
, FreqReadRef ( InFreqReadRef )
, PhaseModReadRef ( MoveTemp ( InPhaseModReadRef ) )
, OutBufferWriteRef ( TDataWriteReferenceFactory < FAudioBuffer > : : CreateAny ( InParams . OperatorSettings ) )
{
const float BlockRate = InParams . OperatorSettings . GetActualBlockRate ( ) ;
if ( BlockRate > 0.0f )
{
BlockPeriod = 1.0f / BlockRate ;
}
}
virtual ~ FMetasoundWaveTableOscillatorNodeOperator ( ) = default ;
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
virtual void BindInputs ( FInputVertexInterfaceData & InOutVertexData ) override
2022-07-06 15:11:11 -04:00
{
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
InOutVertexData . BindReadVertex ( " WaveTable " , WaveTableReadRef ) ;
InOutVertexData . BindReadVertex ( " Play " , PlayReadRef ) ;
InOutVertexData . BindReadVertex ( " Stop " , StopReadRef ) ;
InOutVertexData . BindReadVertex ( " Sync " , SyncReadRef ) ;
InOutVertexData . BindReadVertex ( " Freq " , FreqReadRef ) ;
2022-07-06 15:11:11 -04:00
if ( PhaseModReadRef . IsSet ( ) )
{
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
InOutVertexData . BindReadVertex ( " PhaseMod " , * PhaseModReadRef ) ;
2022-07-06 15:11:11 -04:00
}
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
}
2022-07-06 15:11:11 -04:00
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
virtual void BindOutputs ( FOutputVertexInterfaceData & InOutVertexData ) override
{
InOutVertexData . BindReadVertex ( " Out " , TDataReadReference < FAudioBuffer > ( OutBufferWriteRef ) ) ;
}
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 ( ) ;
return { } ;
2022-07-06 15:11:11 -04:00
}
virtual FDataReferenceCollection GetOutputs ( ) const override
{
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
// 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 ( ) ;
return { } ;
2022-07-06 15:11:11 -04:00
}
void Execute ( )
{
using namespace WaveTable ;
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetasoundWaveTableOscillatorNodeOperator : : Execute ) ;
FAudioBuffer & OutBuffer = * OutBufferWriteRef ;
OutBuffer . Zero ( ) ;
auto GetLastIndex = [ ] ( const FTriggerReadRef & Trigger )
{
int32 LastIndex = - 1 ;
Trigger - > ExecuteBlock ( [ ] ( int32 , int32 ) { } , [ & LastIndex ] ( int32 StartFrame , int32 EndFrame )
{
LastIndex = FMath : : Max ( LastIndex , StartFrame ) ;
} ) ;
return LastIndex ;
} ;
const int32 LastPlayIndex = GetLastIndex ( PlayReadRef ) ;
const int32 LastStopIndex = GetLastIndex ( StopReadRef ) ;
if ( LastPlayIndex > = 0 | | LastStopIndex > = 0 )
{
bPlaying = LastPlayIndex > LastStopIndex ;
}
if ( bPlaying )
{
const FTrigger & SyncTrigger = * SyncReadRef ;
TArrayView < float > SyncBufferView ;
if ( SyncTrigger . IsTriggered ( ) )
{
SyncBuffer . SetNum ( OutBuffer . Num ( ) ) ;
FMemory : : Memset ( SyncBuffer . GetData ( ) , 0 , sizeof ( float ) * SyncBuffer . Num ( ) ) ;
SyncTrigger . ExecuteBlock (
[ ] ( int32 StartFrame , int32 EndFrame ) { } ,
[ this ] ( int32 StartFrame , int32 EndFrame )
{
SyncBuffer [ StartFrame ] = 1.0f ;
}
) ;
SyncBufferView = SyncBuffer ;
}
TArrayView < const float > PhaseMod ;
if ( PhaseModReadRef . IsSet ( ) )
{
const FAudioBuffer & Buffer = * ( * PhaseModReadRef ) ;
PhaseMod = { Buffer . GetData ( ) , Buffer . Num ( ) } ;
}
// Limit wrap operations running off toward infinity while allowing sampler to play in reverse
Sampler . SetFreq ( FMath : : Clamp ( * FreqReadRef , - 20000.f , 20000.f ) * BlockPeriod ) ;
const FWaveTable & InputTable = * WaveTableReadRef ;
Sampler . Process ( InputTable . GetView ( ) , { } , PhaseMod , SyncBufferView , OutBuffer ) ;
}
}
2023-03-02 14:40:35 -05:00
void Reset ( const IOperator : : FResetParams & InParams )
{
const float BlockRate = InParams . OperatorSettings . GetActualBlockRate ( ) ;
if ( BlockRate > 0.0f )
{
BlockPeriod = 1.0f / BlockRate ;
}
bPlaying = false ;
if ( SyncBuffer . Num ( ) > 0 )
{
FMemory : : Memset ( SyncBuffer . GetData ( ) , 0 , sizeof ( float ) * SyncBuffer . Num ( ) ) ;
}
Sampler = WaveTable : : FWaveTableSampler { } ;
OutBufferWriteRef - > Zero ( ) ;
}
2022-07-06 15:11:11 -04:00
private :
float BlockPeriod = 0.0f ;
bool bPlaying = false ;
FWaveTableReadRef WaveTableReadRef ;
FTriggerReadRef PlayReadRef ;
FTriggerReadRef StopReadRef ;
FTriggerReadRef SyncReadRef ;
FFloatReadRef FreqReadRef ;
Audio : : FAlignedFloatBuffer SyncBuffer ;
TOptional < FAudioBufferReadRef > PhaseModReadRef ;
WaveTable : : FWaveTableSampler Sampler ;
TDataWriteReference < FAudioBuffer > OutBufferWriteRef ;
} ;
class FMetasoundWaveTableOscillatorNode : public FNodeFacade
{
public :
FMetasoundWaveTableOscillatorNode ( const FNodeInitData & InInitData )
: FNodeFacade ( InInitData . InstanceName , InInitData . InstanceID , TFacadeOperatorClass < FMetasoundWaveTableOscillatorNodeOperator > ( ) )
{
}
virtual ~ FMetasoundWaveTableOscillatorNode ( ) = default ;
} ;
METASOUND_REGISTER_NODE ( FMetasoundWaveTableOscillatorNode )
} // namespace Metasound
# undef LOCTEXT_NAMESPACE // MetasoundStandardNodes