2021-02-23 20:16:28 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Internationalization/Text.h"
# include "MetasoundExecutableOperator.h"
# include "MetasoundNodeRegistrationMacro.h"
# include "MetasoundPrimitives.h"
# include "MetasoundStandardNodesNames.h"
# include "MetasoundTrigger.h"
# include "MetasoundTime.h"
# include "MetasoundAudioBuffer.h"
# include "DSP/Delay.h"
2021-04-03 18:40:14 -04:00
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundFacade.h"
2021-02-23 20:16:28 -04:00
2022-01-18 17:44:56 -05:00
# define LOCTEXT_NAMESPACE "MetasoundStandardNodes"
2021-02-23 20:16:28 -04:00
namespace Metasound
{
namespace Delay
{
2022-01-18 17:44:56 -05:00
static const FName InParamNameAudioInput = " In " ;
static const FName InParamNameDelayTime = " Delay Time " ;
static const FName InParamNameDryLevel = " Dry Level " ;
static const FName InParamNameWetLevel = " Wet Level " ;
static const FName InParamNameFeedbackAmount = " Feedback " ;
static const FName OutParamNameAudio = " Out " ;
2021-02-23 20:16:28 -04:00
// TODO: make this a static vertex
static float MaxDelaySeconds = 5.0f ;
}
class FDelayOperator : public TExecutableOperator < FDelayOperator >
{
public :
static const FNodeClassMetadata & GetNodeInfo ( ) ;
static const FVertexInterface & GetVertexInterface ( ) ;
static TUniquePtr < IOperator > CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors ) ;
FDelayOperator ( const FOperatorSettings & InSettings ,
const FAudioBufferReadRef & InAudioInput ,
const FTimeReadRef & InDelayTime ,
const FFloatReadRef & InDryLevel ,
const FFloatReadRef & InWetLevel ,
const FFloatReadRef & InFeedback ) ;
virtual FDataReferenceCollection GetInputs ( ) const override ;
virtual FDataReferenceCollection GetOutputs ( ) const override ;
void Execute ( ) ;
private :
float GetInputDelayTimeMsec ( ) const ;
// The input audio buffer
FAudioBufferReadRef AudioInput ;
// The amount of delay time
FTimeReadRef DelayTime ;
2021-12-13 13:14:24 -05:00
// The dry level
2021-02-23 20:16:28 -04:00
FFloatReadRef DryLevel ;
2021-12-13 13:14:24 -05:00
// The wet level
2021-02-23 20:16:28 -04:00
FFloatReadRef WetLevel ;
// The feedback amount
FFloatReadRef Feedback ;
// The audio output
FAudioBufferWriteRef AudioOutput ;
// The internal delay buffer
Audio : : FDelay DelayBuffer ;
2021-12-13 13:14:24 -05:00
// The previous delay time
2021-02-23 20:16:28 -04:00
float PrevDelayTimeMsec ;
// Feedback sample
float FeedbackSample ;
} ;
2021-12-13 13:14:24 -05:00
FDelayOperator : : FDelayOperator ( const FOperatorSettings & InSettings ,
const FAudioBufferReadRef & InAudioInput ,
2021-02-23 20:16:28 -04:00
const FTimeReadRef & InDelayTime ,
2021-12-13 13:14:24 -05:00
const FFloatReadRef & InDryLevel ,
const FFloatReadRef & InWetLevel ,
2021-02-23 20:16:28 -04:00
const FFloatReadRef & InFeedback )
: AudioInput ( InAudioInput )
, DelayTime ( InDelayTime )
, DryLevel ( InDryLevel )
, WetLevel ( InWetLevel )
, Feedback ( InFeedback )
, AudioOutput ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
, PrevDelayTimeMsec ( GetInputDelayTimeMsec ( ) )
, FeedbackSample ( 0.0f )
{
DelayBuffer . Init ( InSettings . GetSampleRate ( ) , Delay : : MaxDelaySeconds ) ;
DelayBuffer . SetDelayMsec ( PrevDelayTimeMsec ) ;
}
FDataReferenceCollection FDelayOperator : : GetInputs ( ) const
{
FDataReferenceCollection InputDataReferences ;
InputDataReferences . AddDataReadReference ( Delay : : InParamNameAudioInput , FAudioBufferReadRef ( AudioInput ) ) ;
InputDataReferences . AddDataReadReference ( Delay : : InParamNameDelayTime , FTimeReadRef ( DelayTime ) ) ;
InputDataReferences . AddDataReadReference ( Delay : : InParamNameDryLevel , FFloatReadRef ( DryLevel ) ) ;
InputDataReferences . AddDataReadReference ( Delay : : InParamNameWetLevel , FFloatReadRef ( WetLevel ) ) ;
InputDataReferences . AddDataReadReference ( Delay : : InParamNameFeedbackAmount , FFloatReadRef ( Feedback ) ) ;
return InputDataReferences ;
}
FDataReferenceCollection FDelayOperator : : GetOutputs ( ) const
{
FDataReferenceCollection OutputDataReferences ;
OutputDataReferences . AddDataReadReference ( Delay : : OutParamNameAudio , FAudioBufferReadRef ( AudioOutput ) ) ;
return OutputDataReferences ;
}
float FDelayOperator : : GetInputDelayTimeMsec ( ) const
{
// Clamp the delay time to the max delay allowed
return 1000.0f * FMath : : Clamp ( ( float ) DelayTime - > GetSeconds ( ) , 0.0f , Delay : : MaxDelaySeconds ) ;
}
void FDelayOperator : : Execute ( )
{
// Get clamped delay time
float CurrentInputDelayTime = GetInputDelayTimeMsec ( ) ;
// Check to see if our delay amount has changed
if ( ! FMath : : IsNearlyEqual ( PrevDelayTimeMsec , CurrentInputDelayTime ) )
{
PrevDelayTimeMsec = CurrentInputDelayTime ;
DelayBuffer . SetEasedDelayMsec ( PrevDelayTimeMsec ) ;
}
const float * InputAudio = AudioInput - > GetData ( ) ;
float * OutputAudio = AudioOutput - > GetData ( ) ;
int32 NumFrames = AudioInput - > Num ( ) ;
// Clamp the feedback amount to make sure it's bounded. Clamp to a number slightly less than 1.0
float FeedbackAmount = FMath : : Clamp ( * Feedback , 0.0f , 1.0f - SMALL_NUMBER ) ;
float CurrentDryLevel = FMath : : Clamp ( * DryLevel , 0.0f , 1.0f ) ;
float CurrentWetLevel = FMath : : Clamp ( * WetLevel , 0.0f , 1.0f ) ;
if ( FMath : : IsNearlyZero ( FeedbackAmount ) )
{
FeedbackSample = 0.0f ;
for ( int32 FrameIndex = 0 ; FrameIndex < NumFrames ; + + FrameIndex )
{
2021-02-25 21:46:14 -04:00
OutputAudio [ FrameIndex ] = CurrentWetLevel * DelayBuffer . ProcessAudioSample ( InputAudio [ FrameIndex ] ) + CurrentDryLevel * InputAudio [ FrameIndex ] ;
2021-02-23 20:16:28 -04:00
}
}
else
{
// There is some amount of feedback so we do the feedback mixing
for ( int32 FrameIndex = 0 ; FrameIndex < NumFrames ; + + FrameIndex )
{
2021-02-25 21:46:14 -04:00
OutputAudio [ FrameIndex ] = CurrentWetLevel * DelayBuffer . ProcessAudioSample ( InputAudio [ FrameIndex ] + FeedbackSample * FeedbackAmount ) + CurrentDryLevel * InputAudio [ FrameIndex ] ;
2021-02-23 20:16:28 -04:00
FeedbackSample = OutputAudio [ FrameIndex ] ;
}
}
}
const FVertexInterface & FDelayOperator : : GetVertexInterface ( )
{
static const FVertexInterface Interface (
FInputVertexInterface (
2022-02-10 18:36:47 -05:00
TInputDataVertexModel < FAudioBuffer > ( Delay : : InParamNameAudioInput , METASOUND_LOCTEXT ( " DelayNode_AudioInputTooltip " , " Audio input. " ) ) ,
TInputDataVertexModel < FTime > ( Delay : : InParamNameDelayTime , METASOUND_LOCTEXT ( " DelayNode_DelayTimeTooltip " , " The amount of time to delay the audio, in seconds. " ) , 1.0f ) ,
TInputDataVertexModel < float > ( Delay : : InParamNameDryLevel , METASOUND_LOCTEXT ( " DelayNode_DryLevelTooltip " , " The dry level of the delay. " ) , 0.0f ) ,
TInputDataVertexModel < float > ( Delay : : InParamNameWetLevel , METASOUND_LOCTEXT ( " DelayNode_WetlevelTooltip " , " The wet level of the delay. " ) , 1.0f ) ,
TInputDataVertexModel < float > ( Delay : : InParamNameFeedbackAmount , METASOUND_LOCTEXT ( " DelayNode_FeedbackTooltip " , " Feedback amount. " ) , 0.0f )
2021-02-23 20:16:28 -04:00
) ,
FOutputVertexInterface (
2022-02-10 18:36:47 -05:00
TOutputDataVertexModel < FAudioBuffer > ( Delay : : OutParamNameAudio , METASOUND_LOCTEXT ( " DelayNode_DelayOutputTooltip " , " Audio output. " ) )
2021-02-23 20:16:28 -04:00
)
) ;
return Interface ;
}
const FNodeClassMetadata & FDelayOperator : : GetNodeInfo ( )
{
auto InitNodeInfo = [ ] ( ) - > FNodeClassMetadata
{
FNodeClassMetadata Info ;
2022-01-18 17:44:56 -05:00
Info . ClassName = { StandardNodes : : Namespace , " Delay " , StandardNodes : : AudioVariant } ;
2021-02-23 20:16:28 -04:00
Info . MajorVersion = 1 ;
2022-01-18 17:44:56 -05:00
Info . MinorVersion = 1 ;
2022-02-10 18:36:47 -05:00
Info . DisplayName = METASOUND_LOCTEXT ( " DelayNode_DisplayName " , " Delay " ) ;
Info . Description = METASOUND_LOCTEXT ( " DelayNode_Description " , " Delays an audio buffer by the specified amount. " ) ;
2021-02-23 20:16:28 -04:00
Info . Author = PluginAuthor ;
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = GetVertexInterface ( ) ;
2021-08-09 15:13:40 -04:00
Info . CategoryHierarchy . Emplace ( NodeCategories : : Delays ) ;
2021-02-23 20:16:28 -04:00
return Info ;
} ;
static const FNodeClassMetadata Info = InitNodeInfo ( ) ;
return Info ;
}
TUniquePtr < IOperator > FDelayOperator : : CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors )
{
const FDataReferenceCollection & InputCollection = InParams . InputDataReferences ;
const FInputVertexInterface & InputInterface = GetVertexInterface ( ) . GetInputInterface ( ) ;
FAudioBufferReadRef AudioIn = InputCollection . GetDataReadReferenceOrConstruct < FAudioBuffer > ( Delay : : InParamNameAudioInput , InParams . OperatorSettings ) ;
2021-03-23 22:43:28 -04:00
FTimeReadRef DelayTime = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < FTime > ( InputInterface , Delay : : InParamNameDelayTime , InParams . OperatorSettings ) ;
FFloatReadRef DryLevel = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < float > ( InputInterface , Delay : : InParamNameDryLevel , InParams . OperatorSettings ) ;
FFloatReadRef WetLevel = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < float > ( InputInterface , Delay : : InParamNameWetLevel , InParams . OperatorSettings ) ;
FFloatReadRef Feedback = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < float > ( InputInterface , Delay : : InParamNameFeedbackAmount , InParams . OperatorSettings ) ;
2021-02-23 20:16:28 -04:00
return MakeUnique < FDelayOperator > ( InParams . OperatorSettings , AudioIn , DelayTime , DryLevel , WetLevel , Feedback ) ;
}
2021-04-03 18:40:14 -04:00
class FDelayNode : public FNodeFacade
2021-02-23 20:16:28 -04:00
{
2021-04-03 18:40:14 -04:00
public :
/**
* Constructor used by the Metasound Frontend .
*/
FDelayNode ( const FNodeInitData & InitData )
: FNodeFacade ( InitData . InstanceName , InitData . InstanceID , TFacadeOperatorClass < FDelayOperator > ( ) )
{
}
} ;
2021-02-23 20:16:28 -04:00
METASOUND_REGISTER_NODE ( FDelayNode )
}
# undef LOCTEXT_NAMESPACE