2021-03-02 23:16:11 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
#include "MetasoundTriggerOnThresholdNode.h"
|
|
|
|
|
|
|
|
|
|
#include "MetasoundAudioBuffer.h"
|
2021-04-05 20:22:19 -04:00
|
|
|
#include "MetasoundEnumRegistrationMacro.h"
|
2021-03-02 23:16:11 -04:00
|
|
|
#include "MetasoundExecutableOperator.h"
|
|
|
|
|
#include "MetasoundFacade.h"
|
|
|
|
|
#include "MetasoundNodeRegistrationMacro.h"
|
|
|
|
|
#include "MetasoundDataTypeRegistrationMacro.h"
|
|
|
|
|
#include "MetasoundOperatorSettings.h"
|
|
|
|
|
#include "MetasoundPrimitives.h"
|
|
|
|
|
#include "MetasoundStandardNodesNames.h"
|
|
|
|
|
#include "MetasoundTrigger.h"
|
|
|
|
|
#include "MetasoundVertex.h"
|
2021-03-05 23:03:16 -04:00
|
|
|
#include "MetasoundStandardNodesCategories.h"
|
2021-03-02 23:16:11 -04:00
|
|
|
|
2021-04-07 02:57:54 -04:00
|
|
|
#define LOCTEXT_NAMESPACE "MetasoundStandardNodes_ThresholdNode"
|
2021-03-02 23:16:11 -04:00
|
|
|
|
|
|
|
|
namespace Metasound
|
|
|
|
|
{
|
|
|
|
|
enum class EBufferTriggerType
|
|
|
|
|
{
|
|
|
|
|
RisingEdge,
|
|
|
|
|
FallingEdge,
|
|
|
|
|
AbsThreshold,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DECLARE_METASOUND_ENUM(EBufferTriggerType, EBufferTriggerType::RisingEdge, METASOUNDSTANDARDNODES_API,
|
|
|
|
|
FEnumBufferTriggerType, FEnumBufferTriggerTypeInfo, FBufferTriggerTypeReadRef, FEnumBufferTriggerTypeWriteRef);
|
|
|
|
|
|
|
|
|
|
DEFINE_METASOUND_ENUM_BEGIN(EBufferTriggerType, FEnumBufferTriggerType, "BufferTriggerType")
|
2022-02-10 18:36:47 -05:00
|
|
|
DEFINE_METASOUND_ENUM_ENTRY(EBufferTriggerType::RisingEdge, "RisingEdgeDescription", "Rising Edge", "RisingEdgeDescriptionTT", ""),
|
|
|
|
|
DEFINE_METASOUND_ENUM_ENTRY(EBufferTriggerType::FallingEdge, "FallingEdgeDescription", "Falling Edge", "FallingEdgeDescriptionTT", ""),
|
|
|
|
|
DEFINE_METASOUND_ENUM_ENTRY(EBufferTriggerType::AbsThreshold, "AbsThresholdDescription", "Abs Threshold", "AbsThresholdDescriptionTT", "")
|
2022-03-17 13:14:50 -04:00
|
|
|
DEFINE_METASOUND_ENUM_END()
|
|
|
|
|
|
|
|
|
|
namespace TriggerOnThresholdVertexNames
|
|
|
|
|
{
|
|
|
|
|
METASOUND_PARAM(OutPin, "Out", "Output");
|
|
|
|
|
METASOUND_PARAM(InPin, "In", "Input");
|
|
|
|
|
METASOUND_PARAM(InThresholdPin, "Threshold", "Trigger Threshold");
|
|
|
|
|
METASOUND_PARAM(InTriggerType, "Type", "Trigger Threshold Type");
|
|
|
|
|
}
|
2021-03-02 23:16:11 -04:00
|
|
|
|
|
|
|
|
class FTriggerOnThresholdOperator : public IOperator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static constexpr float DefaultThreshold = 0.85f;
|
|
|
|
|
|
|
|
|
|
static TUniquePtr<IOperator> CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors);
|
|
|
|
|
static const FNodeClassMetadata& GetNodeInfo();
|
|
|
|
|
static FVertexInterface DeclareVertexInterface();
|
|
|
|
|
|
|
|
|
|
FTriggerOnThresholdOperator(const FOperatorSettings& InSettings, FAudioBufferReadRef InBuffer, FFloatReadRef InThreshold);
|
|
|
|
|
|
|
|
|
|
virtual FDataReferenceCollection GetInputs() const override;
|
|
|
|
|
virtual FDataReferenceCollection GetOutputs() const override;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
FAudioBufferReadRef In;
|
|
|
|
|
FFloatReadRef Threshold;
|
|
|
|
|
FTriggerWriteRef Out;
|
|
|
|
|
bool bTriggered = false;
|
|
|
|
|
float LastSample = 0.f;
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-04 22:21:33 -04:00
|
|
|
// Mac Clang requires linkage on constexpr
|
|
|
|
|
constexpr float FTriggerOnThresholdOperator::DefaultThreshold;
|
|
|
|
|
|
2021-03-02 23:16:11 -04:00
|
|
|
struct FTriggerOnThresholdOperator_EdgeCommon : public FTriggerOnThresholdOperator
|
|
|
|
|
{
|
|
|
|
|
using FTriggerOnThresholdOperator::FTriggerOnThresholdOperator;
|
|
|
|
|
|
|
|
|
|
template<typename PREDICATE>
|
|
|
|
|
void Generate(const PREDICATE& ValueTester)
|
|
|
|
|
{
|
|
|
|
|
Out->AdvanceBlock();
|
|
|
|
|
|
|
|
|
|
const float* InputBuffer = In->GetData();
|
|
|
|
|
const int32 NumFrames = In->Num();
|
|
|
|
|
const float ThreshValue = *Threshold;
|
|
|
|
|
|
|
|
|
|
float Previous = LastSample;
|
|
|
|
|
for (int32 i = 0; i < NumFrames; ++i)
|
|
|
|
|
{
|
|
|
|
|
const float Current = *InputBuffer;
|
|
|
|
|
|
|
|
|
|
// If Previous didn't trigger but current value does... fire!
|
|
|
|
|
if (!ValueTester(Previous, ThreshValue) && ValueTester(Current,ThreshValue))
|
|
|
|
|
{
|
|
|
|
|
Out->TriggerFrame(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Previous = Current;
|
|
|
|
|
++InputBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remember the last sample for next frame.
|
|
|
|
|
LastSample = Previous;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct FTriggerOnThresholdOperator_RisingEdge final : public FTriggerOnThresholdOperator_EdgeCommon
|
|
|
|
|
{
|
|
|
|
|
using FTriggerOnThresholdOperator_EdgeCommon::FTriggerOnThresholdOperator_EdgeCommon;
|
|
|
|
|
|
|
|
|
|
void Execute()
|
|
|
|
|
{
|
|
|
|
|
Generate(TGreater<float>{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExecuteFunction(IOperator* InOperator) { static_cast<FTriggerOnThresholdOperator_RisingEdge*>(InOperator)->Execute(); }
|
|
|
|
|
FExecuteFunction GetExecuteFunction() override { return &FTriggerOnThresholdOperator_RisingEdge::ExecuteFunction; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct FTriggerOnThresholdOperator_FallingEdge final : public FTriggerOnThresholdOperator_EdgeCommon
|
|
|
|
|
{
|
|
|
|
|
using FTriggerOnThresholdOperator_EdgeCommon::FTriggerOnThresholdOperator_EdgeCommon;
|
|
|
|
|
|
|
|
|
|
static void ExecuteFunction(IOperator* InOperator) { static_cast<FTriggerOnThresholdOperator_FallingEdge*>(InOperator)->Execute(); }
|
|
|
|
|
FExecuteFunction GetExecuteFunction() override { return &FTriggerOnThresholdOperator_FallingEdge::ExecuteFunction; }
|
|
|
|
|
|
|
|
|
|
void Execute()
|
|
|
|
|
{
|
|
|
|
|
Generate(TLess<float>{});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct FTriggerOnThresholdOperator_AbsThreshold final : public FTriggerOnThresholdOperator
|
|
|
|
|
{
|
|
|
|
|
using FTriggerOnThresholdOperator::FTriggerOnThresholdOperator;
|
|
|
|
|
|
|
|
|
|
static void ExecuteFunction(IOperator* InOperator) { static_cast<FTriggerOnThresholdOperator_AbsThreshold*>(InOperator)->Execute(); }
|
|
|
|
|
FExecuteFunction GetExecuteFunction() override { return &FTriggerOnThresholdOperator_AbsThreshold::ExecuteFunction; }
|
|
|
|
|
|
|
|
|
|
void Execute()
|
|
|
|
|
{
|
|
|
|
|
Out->AdvanceBlock();
|
|
|
|
|
|
|
|
|
|
const float* InputBuffer = In->GetData();
|
|
|
|
|
const int32 NumFrames = In->Num();
|
|
|
|
|
const float ThresholdSqr = *Threshold * *Threshold;
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumFrames; ++i)
|
|
|
|
|
{
|
|
|
|
|
const float Current = *InputBuffer;
|
|
|
|
|
const float CurrentSqr = Current * Current;
|
|
|
|
|
|
|
|
|
|
if (CurrentSqr > ThresholdSqr && !bTriggered)
|
|
|
|
|
{
|
|
|
|
|
bTriggered = true;
|
|
|
|
|
Out->TriggerFrame(i);
|
|
|
|
|
}
|
|
|
|
|
else if (CurrentSqr < ThresholdSqr && bTriggered)
|
|
|
|
|
{
|
|
|
|
|
bTriggered = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++InputBuffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FTriggerOnThresholdOperator::FTriggerOnThresholdOperator(const FOperatorSettings& InSettings, FAudioBufferReadRef InBuffer, FFloatReadRef InThreshold)
|
|
|
|
|
: In(InBuffer)
|
|
|
|
|
, Threshold(InThreshold)
|
|
|
|
|
, Out(FTriggerWriteRef::CreateNew(InSettings))
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
FDataReferenceCollection FTriggerOnThresholdOperator::GetInputs() const
|
|
|
|
|
{
|
2022-03-17 13:14:50 -04:00
|
|
|
using namespace TriggerOnThresholdVertexNames;
|
|
|
|
|
|
2021-03-02 23:16:11 -04:00
|
|
|
FDataReferenceCollection InputDataReferences;
|
2022-03-17 13:14:50 -04:00
|
|
|
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InThresholdPin), FFloatReadRef(Threshold));
|
|
|
|
|
InputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(InPin), FAudioBufferReadRef(In));
|
2021-03-02 23:16:11 -04:00
|
|
|
return InputDataReferences;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FDataReferenceCollection FTriggerOnThresholdOperator::GetOutputs() const
|
|
|
|
|
{
|
2022-03-17 13:14:50 -04:00
|
|
|
using namespace TriggerOnThresholdVertexNames;
|
|
|
|
|
|
2021-03-02 23:16:11 -04:00
|
|
|
FDataReferenceCollection OutputDataReferences;
|
2022-03-17 13:14:50 -04:00
|
|
|
OutputDataReferences.AddDataReadReference(METASOUND_GET_PARAM_NAME(OutPin), FTriggerWriteRef(Out));
|
2021-03-02 23:16:11 -04:00
|
|
|
return OutputDataReferences;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVertexInterface FTriggerOnThresholdOperator::DeclareVertexInterface()
|
|
|
|
|
{
|
2022-03-17 13:14:50 -04:00
|
|
|
using namespace TriggerOnThresholdVertexNames;
|
|
|
|
|
|
2021-03-02 23:16:11 -04:00
|
|
|
static const FVertexInterface Interface(
|
|
|
|
|
FInputVertexInterface(
|
2022-03-31 16:49:59 -04:00
|
|
|
TInputDataVertex<FAudioBuffer>(METASOUND_GET_PARAM_NAME_AND_METADATA(InPin)),
|
|
|
|
|
TInputDataVertex<float>(METASOUND_GET_PARAM_NAME_AND_METADATA(InThresholdPin), DefaultThreshold),
|
|
|
|
|
TInputDataVertex<FEnumBufferTriggerType>(METASOUND_GET_PARAM_NAME_AND_METADATA(InTriggerType))
|
2021-03-02 23:16:11 -04:00
|
|
|
),
|
|
|
|
|
FOutputVertexInterface(
|
2022-03-31 16:49:59 -04:00
|
|
|
TOutputDataVertex<FTrigger>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutPin))
|
2021-03-02 23:16:11 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
return Interface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const FNodeClassMetadata& FTriggerOnThresholdOperator::GetNodeInfo()
|
|
|
|
|
{
|
|
|
|
|
auto InitNodeInfo = []() -> FNodeClassMetadata
|
|
|
|
|
{
|
|
|
|
|
FNodeClassMetadata Info;
|
2021-08-09 15:08:37 -04:00
|
|
|
Info.ClassName = {StandardNodes::Namespace, TEXT("TriggerOnThreshold"), StandardNodes::AudioVariant};
|
2021-03-02 23:16:11 -04:00
|
|
|
Info.MajorVersion = 1;
|
|
|
|
|
Info.MinorVersion = 0;
|
2022-02-10 18:36:47 -05:00
|
|
|
Info.DisplayName = METASOUND_LOCTEXT("Metasound_TriggerOnThresholdNodeDisplayName", "Trigger On Threshold");
|
|
|
|
|
Info.Description = METASOUND_LOCTEXT("Metasound_TriggerOnThresholdNodeDescription", "Trigger based on a audio buffer input");
|
2021-03-02 23:16:11 -04:00
|
|
|
Info.Author = PluginAuthor;
|
|
|
|
|
Info.PromptIfMissing = PluginNodeMissingPrompt;
|
|
|
|
|
Info.DefaultInterface = DeclareVertexInterface();
|
2021-08-09 15:08:37 -04:00
|
|
|
Info.CategoryHierarchy.Emplace(NodeCategories::Trigger);
|
2021-03-02 23:16:11 -04:00
|
|
|
|
|
|
|
|
return Info;
|
|
|
|
|
};
|
|
|
|
|
static const FNodeClassMetadata Info = InitNodeInfo();
|
|
|
|
|
return Info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FTriggerOnThresholdNode::FTriggerOnThresholdNode(const FNodeInitData& InInitData)
|
|
|
|
|
: FTriggerOnThresholdNode(InInitData.InstanceName, InInitData.InstanceID, FTriggerOnThresholdOperator::DefaultThreshold)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-13 14:13:39 -04:00
|
|
|
FTriggerOnThresholdNode::FTriggerOnThresholdNode(const FVertexName& InName, const FGuid& InInstanceID, float InDefaultThreshold)
|
2021-03-02 23:16:11 -04:00
|
|
|
: FNodeFacade(InName, InInstanceID, TFacadeOperatorClass<FTriggerOnThresholdOperator>())
|
|
|
|
|
, DefaultThreshold(InDefaultThreshold)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TUniquePtr<IOperator> FTriggerOnThresholdOperator::CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors)
|
|
|
|
|
{
|
2022-03-17 13:14:50 -04:00
|
|
|
using namespace TriggerOnThresholdVertexNames;
|
|
|
|
|
|
2021-03-02 23:16:11 -04:00
|
|
|
const FTriggerOnThresholdNode& Node = static_cast<const FTriggerOnThresholdNode&>(InParams.Node);
|
|
|
|
|
const FDataReferenceCollection& InputCol = InParams.InputDataReferences;
|
|
|
|
|
const FOperatorSettings& Settings = InParams.OperatorSettings;
|
2022-03-28 12:58:42 -04:00
|
|
|
const FInputVertexInterface& InputInterface = InParams.Node.GetVertexInterface().GetInputInterface();
|
2021-03-02 23:16:11 -04:00
|
|
|
|
|
|
|
|
// Static property pin, only used for factory.
|
2022-03-17 13:14:50 -04:00
|
|
|
FBufferTriggerTypeReadRef Type = InputCol.GetDataReadReferenceOrConstruct<FEnumBufferTriggerType>(METASOUND_GET_PARAM_NAME(InTriggerType));
|
2021-03-02 23:16:11 -04:00
|
|
|
|
2022-03-17 13:14:50 -04:00
|
|
|
FAudioBufferReadRef InputBuffer = InputCol.GetDataReadReferenceOrConstruct<FAudioBuffer>(METASOUND_GET_PARAM_NAME(InPin), Settings);
|
|
|
|
|
FFloatReadRef Threshold = InputCol.GetDataReadReferenceOrConstructWithVertexDefault<float>(InputInterface, METASOUND_GET_PARAM_NAME(InThresholdPin), Settings);
|
2021-03-02 23:16:11 -04:00
|
|
|
|
|
|
|
|
switch (*Type)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case EBufferTriggerType::RisingEdge:
|
|
|
|
|
return MakeUnique<FTriggerOnThresholdOperator_RisingEdge>(InParams.OperatorSettings, InputBuffer, Threshold);
|
|
|
|
|
case EBufferTriggerType::FallingEdge:
|
|
|
|
|
return MakeUnique<FTriggerOnThresholdOperator_FallingEdge>(InParams.OperatorSettings, InputBuffer, Threshold);
|
|
|
|
|
case EBufferTriggerType::AbsThreshold:
|
|
|
|
|
return MakeUnique<FTriggerOnThresholdOperator_AbsThreshold>(InParams.OperatorSettings, InputBuffer, Threshold);
|
|
|
|
|
}
|
|
|
|
|
checkNoEntry();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
METASOUND_REGISTER_NODE(FTriggerOnThresholdNode);
|
|
|
|
|
}
|
|
|
|
|
#undef LOCTEXT_NAMESPACE //MetasoundStandardNodes
|
|
|
|
|
|