// Copyright Epic Games, Inc. All Rights Reserved. #include "NodeTestGraphBuilder.h" #include "HarmonixMetasound/DataTypes/MidiStream.h" #include "HarmonixMetasound/Nodes/MidiStreamTransposerNode.h" #include "Misc/AutomationTest.h" #if WITH_DEV_AUTOMATION_TESTS namespace HarmonixMetasoundTests::MidiNoteTransposeNode { using GraphBuilder = Metasound::Test::FNodeTestGraphBuilder; using namespace Metasound; using namespace Metasound::Frontend; using namespace HarmonixMetasound; IMPLEMENT_SIMPLE_AUTOMATION_TEST( FMidiStreamTransposerCreateNodeTest, "Harmonix.Metasound.Nodes.MidiNoteTransposeNode.Basic", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMidiStreamTransposerCreateNodeTest::RunTest(const FString&) { // Build the graph. constexpr int32 NumSamplesPerBlock = 256; const TUniquePtr Generator = GraphBuilder::MakeSingleNodeGraph( Nodes::MidiNoteTranspose::GetClassName(), Nodes::MidiNoteTranspose::GetCurrentMajorVersion(), 48000, NumSamplesPerBlock); UTEST_TRUE("Graph successfully built", Generator.IsValid()); TOptional> NodeMidiOutput = Generator->GetOutputReadReference(Nodes::MidiNoteTranspose::Outputs::MidiStreamName); UTEST_TRUE("Got node MIDI output", NodeMidiOutput.IsSet()); FAudioBuffer Buffer{ Generator->OperatorSettings }; const TArray NoteNumbers{ 3, 23, 57, 87, 99, 120 }; // Default: no transposition { // Add the notes Generator->ApplyToInputValue( Nodes::MidiNoteTranspose::Inputs::MidiStreamName, [&NoteNumbers](FMidiStream& Stream) { for (const uint8 NoteNumber : NoteNumbers) { Stream.AddMidiEvent({ static_cast(0), FMidiMsg::CreateNoteOn(7, NoteNumber, 127) }); } }); // Process Generator->OnGenerateAudio(Buffer.GetData(), Buffer.Num()); // The notes should be unchanged in the output const TArray& Events = (*NodeMidiOutput)->GetEventsInBlock(); UTEST_EQUAL("Got the right number of events", Events.Num(), NoteNumbers.Num()); const TArray ExpectedNoteNumbers = NoteNumbers; for (const FMidiStreamEvent& Event : Events) { if (Event.MidiMessage.IsNoteOn()) { UTEST_TRUE("Note number unchanged", ExpectedNoteNumbers.Contains(Event.MidiMessage.GetStdData1())); } else { UTEST_TRUE("Unexpected event", false); } } } // Try some different transposition values const TArray TranspositionAmounts{ -23, -3, 0, 4, 12, 40, 1000 }; for (const int32 TranspositionAmount : TranspositionAmounts) { // Set transposition Generator->SetInputValue(Nodes::MidiNoteTranspose::Inputs::TranspositionName, TranspositionAmount); // Process Generator->OnGenerateAudio(Buffer.GetData(), Buffer.Num()); // The notes should be transposed, but clamped to MIDI note range (0-127) const TArray& Events = (*NodeMidiOutput)->GetEventsInBlock(); UTEST_EQUAL("Got the right number of events", Events.Num(), NoteNumbers.Num()); TArray ExpectedNoteNumbers; for (const uint8 OriginalNoteNumber : NoteNumbers) { ExpectedNoteNumbers.Add(FMath::Clamp(OriginalNoteNumber + TranspositionAmount, 0, 127)); } for (const FMidiStreamEvent& Event : Events) { if (Event.MidiMessage.IsNoteOn()) { UTEST_TRUE("Note number unchanged", ExpectedNoteNumbers.Contains(Event.MidiMessage.GetStdData1())); } else { UTEST_TRUE("Unexpected event", false); } } } return true; } } #endif