Files
UnrealEngine/Engine/Plugins/Runtime/Metasound/Source/MetasoundStandardNodes/Private/MetasoundSampleAndHoldNode.cpp
2025-05-18 13:04:45 +08:00

169 lines
5.3 KiB
C++

// 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"
#include "MetasoundStandardNodesCategories.h"
#include "MetasoundFacade.h"
#include "MetasoundParamHelper.h"
#define LOCTEXT_NAMESPACE "MetasoundStandardNodes_SampleAndHold"
namespace Metasound
{
namespace SampleAndHoldVertexNames
{
METASOUND_PARAM(InputTriggerSampleAndHold, "Sample And Hold", "Trigger to sample and hold the input audio.");
METASOUND_PARAM(InputAudio, "In", "The audio input to sample.");
METASOUND_PARAM(OutputOnSampleAndHold, "On Sample And Hold", "Triggers when the input sample and hold is triggered.");
METASOUND_PARAM(OutputAudio, "Out", "Sampled output value.");
}
class FSampleAndHoldOperator : public TExecutableOperator<FSampleAndHoldOperator>
{
public:
static const FNodeClassMetadata& GetNodeInfo()
{
auto CreateNodeClassMetadata = []() -> FNodeClassMetadata
{
FVertexInterface NodeInterface = GetVertexInterface();
FNodeClassMetadata Metadata
{
FNodeClassName { "SampleAndHold", "Sample And Hold", StandardNodes::AudioVariant },
1, // Major Version
0, // Minor Version
METASOUND_LOCTEXT("SampleAndHoldDisplayName", "Sample And Hold"),
METASOUND_LOCTEXT("SampleAndHoldDesc", "Will output a single value of the input audio signal when triggered."),
PluginAuthor,
PluginNodeMissingPrompt,
NodeInterface,
{ NodeCategories::Filters },
{ },
FNodeDisplayStyle()
};
return Metadata;
};
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata();
return Metadata;
}
static const FVertexInterface& GetVertexInterface()
{
using namespace SampleAndHoldVertexNames;
static const FVertexInterface Interface(
FInputVertexInterface(
TInputDataVertex<FTrigger>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputTriggerSampleAndHold)),
TInputDataVertex<FAudioBuffer>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputAudio))
),
FOutputVertexInterface(
TOutputDataVertex<FTrigger>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputOnSampleAndHold)),
TOutputDataVertex<FAudioBuffer>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputAudio))
)
);
return Interface;
}
static TUniquePtr<IOperator> CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults)
{
using namespace SampleAndHoldVertexNames;
const FInputVertexInterfaceData& InputData = InParams.InputData;
FTriggerReadRef InputTriggerSampleAndHold = InputData.GetOrCreateDefaultDataReadReference<FTrigger>(METASOUND_GET_PARAM_NAME(InputTriggerSampleAndHold), InParams.OperatorSettings);
FAudioBufferReadRef InputAudio = InputData.GetOrCreateDefaultDataReadReference<FAudioBuffer>(METASOUND_GET_PARAM_NAME(InputAudio), InParams.OperatorSettings);
return MakeUnique<FSampleAndHoldOperator>(InParams, InputTriggerSampleAndHold, InputAudio);
}
FSampleAndHoldOperator(const FBuildOperatorParams& InParams, const FTriggerReadRef& InTriggerSampleAndHold, const FAudioBufferReadRef& InAudioInput)
: AudioInput(InAudioInput)
, TriggerSampleAndHold(InTriggerSampleAndHold)
, AudioOutput(FAudioBufferWriteRef::CreateNew(InParams.OperatorSettings))
{
Reset(InParams);
}
virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override
{
using namespace SampleAndHoldVertexNames;
InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputAudio), AudioInput);
InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputTriggerSampleAndHold), TriggerSampleAndHold);
}
virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override
{
using namespace SampleAndHoldVertexNames;
InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputOnSampleAndHold), TriggerSampleAndHold);
InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputAudio), AudioOutput);
}
void Execute()
{
TriggerSampleAndHold->ExecuteBlock(
// On Pre-Trigger, continue outputting the held value
[this](int32 StartFrame, int32 EndFrame)
{
SetToHold(StartFrame, EndFrame);
},
// On Trigger, get a new hold value, then output for the rest of the block
[this](int32 StartFrame, int32 EndFrame)
{
// Get a new value to sample and hold
HoldValue = AudioInput->GetData()[StartFrame];
SetToHold(StartFrame, EndFrame);
}
);
}
void Reset(const IOperator::FResetParams& InParams)
{
HoldValue = AudioInput->GetData()[0];
SetToHold(0, AudioOutput->Num());
}
private:
void SetToHold(int32 StartFrame, int32 EndFrame)
{
check(AudioOutput->Num() >= EndFrame);
float* OutputBufferPtr = AudioOutput->GetData();
float* CurrOutputPtr = &OutputBufferPtr[StartFrame];
const int32 NumSamples = EndFrame - StartFrame;
// non-SIMD version
for (int32 i = 0; i < NumSamples; ++i)
{
CurrOutputPtr[i] = HoldValue;
}
}
FAudioBufferReadRef AudioInput;
FTriggerReadRef TriggerSampleAndHold;
FAudioBufferWriteRef AudioOutput;
float HoldValue = 0.0f;
};
using FSampleAndHoldNode = TNodeFacade<FSampleAndHoldOperator>;
METASOUND_REGISTER_NODE(FSampleAndHoldNode)
}
#undef LOCTEXT_NAMESPACE