// Copyright Epic Games, Inc. All Rights Reserved. #include "Internationalization/Text.h" #include "MetasoundFacade.h" #include "MetasoundExecutableOperator.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundPrimitives.h" #include "MetasoundStandardNodesNames.h" #include "MetasoundStandardNodesCategories.h" #include "DSP/Dsp.h" #include "MetasoundParamHelper.h" #define LOCTEXT_NAMESPACE "MetasoundStandardNodes_MidiToFreqNode" namespace Metasound { namespace MidiToFrequencyVertexNames { METASOUND_PARAM(InputMidi, "MIDI In", "A value representing a MIDI note value."); METASOUND_PARAM(OutputFreq, "Out Frequency", "Output frequency value in hertz that corresponds to the input Midi note value."); } namespace MidiToFrequencyPrivate { template struct TMidiToFreqNodeSpecialization { }; template<> struct TMidiToFreqNodeSpecialization { static int32 GetFreqValue(int32 InMidi) { return Audio::GetFrequencyFromMidi(FMath::Clamp(InMidi, 0, 127)); } static bool IsValueEqual(int32 InValueA, int32 InValueB) { return InValueA == InValueB; } }; template<> struct TMidiToFreqNodeSpecialization { static float GetFreqValue(float InMidi) { return Audio::GetFrequencyFromMidi(FMath::Clamp(InMidi, 0.0f, 127.0f)); } static bool IsValueEqual(float InValueA, float InValueB) { return FMath::IsNearlyEqual(InValueA, InValueB); } }; } template class TMidiToFreqOperator : public TExecutableOperator> { public: static const FNodeClassMetadata& GetNodeInfo(); static const FVertexInterface& GetVertexInterface(); static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults); TMidiToFreqOperator(const FBuildOperatorParams& InParams, const TDataReadReference& InMidiNote); virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override; virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override; void Reset(const IOperator::FResetParams& InParams); void Execute(); private: // The input Midi value TDataReadReference MidiNote; // The output frequency FFloatWriteRef FreqOutput; // Cached Midi note value. Used to catch if the value changes to recompute freq output. ValueType PrevMidiNote; }; template TMidiToFreqOperator::TMidiToFreqOperator(const FBuildOperatorParams& InParams, const TDataReadReference& InMidiNote) : MidiNote(InMidiNote) , FreqOutput(FFloatWriteRef::CreateNew(Audio::GetFrequencyFromMidi(*InMidiNote))) , PrevMidiNote(*InMidiNote) { Reset(InParams); } template void TMidiToFreqOperator::BindInputs(FInputVertexInterfaceData& InOutVertexData) { using namespace MidiToFrequencyVertexNames; InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputMidi), MidiNote); } template void TMidiToFreqOperator::BindOutputs(FOutputVertexInterfaceData& InOutVertexData) { using namespace MidiToFrequencyVertexNames; InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputFreq), FreqOutput); } template void TMidiToFreqOperator::Reset(const IOperator::FResetParams& InParams) { using namespace MidiToFrequencyPrivate; PrevMidiNote = *MidiNote; *FreqOutput = TMidiToFreqNodeSpecialization::GetFreqValue(PrevMidiNote); } template void TMidiToFreqOperator::Execute() { using namespace MidiToFrequencyPrivate; // Only do anything if the Midi note changes if (!TMidiToFreqNodeSpecialization::IsValueEqual(*MidiNote, PrevMidiNote)) { PrevMidiNote = *MidiNote; *FreqOutput = TMidiToFreqNodeSpecialization::GetFreqValue(PrevMidiNote); } } template const FVertexInterface& TMidiToFreqOperator::GetVertexInterface() { using namespace MidiToFrequencyVertexNames; static const FVertexInterface Interface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputMidi), (ValueType)60.0f) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputFreq)) ) ); return Interface; } template const FNodeClassMetadata& TMidiToFreqOperator::GetNodeInfo() { auto InitNodeInfo = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("MIDI To Frequency"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("RandomGetArrayOpDisplayNamePattern", "MIDI To Frequency ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("Metasound_MidiToFreqNodeDescription", "Converts a Midi note value to a frequency (hz) value."); FNodeClassMetadata Info; Info.ClassName = { StandardNodes::Namespace, OperatorName, DataTypeName }; Info.MajorVersion = 1; Info.MinorVersion = 0; Info.DisplayName = NodeDisplayName; Info.Description = NodeDescription; Info.Author = PluginAuthor; Info.PromptIfMissing = PluginNodeMissingPrompt; Info.DefaultInterface = GetVertexInterface(); Info.CategoryHierarchy.Emplace(NodeCategories::Music); Info.Keywords.Add(METASOUND_LOCTEXT("MIDIToFreqPitchKeyword", "Pitch")); return Info; }; static const FNodeClassMetadata Info = InitNodeInfo(); return Info; } template TUniquePtr TMidiToFreqOperator::CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace MidiToFrequencyVertexNames; const FInputVertexInterfaceData& InputData = InParams.InputData; TDataReadReference InMidiNote = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputMidi), InParams.OperatorSettings); return MakeUnique(InParams, InMidiNote); } template using TMidiToFreqNode = TNodeFacade>; using FMidiToFreqNodeInt32 = TMidiToFreqNode; METASOUND_REGISTER_NODE(FMidiToFreqNodeInt32) using FMidiToFreqNodeFloat = TMidiToFreqNode; METASOUND_REGISTER_NODE(FMidiToFreqNodeFloat) } #undef LOCTEXT_NAMESPACE