Files
2025-05-18 13:04:45 +08:00

137 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NodeTestGraphBuilder.h"
#include "HarmonixDsp/AudioBuffer.h"
#include "HarmonixMetasound/Common.h"
#include "HarmonixMetasound/DataTypes/MidiStream.h"
#include "HarmonixMetasound/DataTypes/MusicTimestamp.h"
#include "HarmonixMetasound/Nodes/SecsUntilMusicTimestampNode.h"
#include "Misc/AutomationTest.h"
#if WITH_DEV_AUTOMATION_TESTS
namespace HarmonixMetasoundTests::TimestampRelated
{
using FGraphBuilder = Metasound::Test::FNodeTestGraphBuilder;
using namespace Metasound;
using namespace Metasound::Frontend;
using namespace HarmonixMetasound;
using namespace HarmonixMetasound::Nodes;
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FSecsUntilTimestampTestDefaults,
"Harmonix.Metasound.Nodes.SecsUntilMusicTimestamp.Defaults",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FSecsUntilTimestampTestDefaults::RunTest(const FString&)
{
// Build the graph.
constexpr int32 NumSamplesPerBlock = 256;
const TUniquePtr<FMetasoundGenerator> Generator = FGraphBuilder::MakeSingleNodeGraph(
SecsUntilMusicTimestampNode::GetClassName(),
SecsUntilMusicTimestampNode::GetCurrentMajorVersion(),
48000,
NumSamplesPerBlock);
UTEST_TRUE("Graph successfully built", Generator.IsValid());
// execute a block
{
TAudioBuffer<float> Buffer{ Generator->GetNumChannels(), NumSamplesPerBlock, EAudioBufferCleanupMode::Delete};
Generator->OnGenerateAudio(Buffer.GetRawChannelData(0), Buffer.GetNumTotalValidSamples());
}
// Validate defaults.
TOptional<FFloatReadRef> OutputSeconds = Generator->GetOutputReadReference<float>(SecsUntilMusicTimestampNode::Outputs::SecsUntilTimestampName);
UTEST_TRUE("Output exists", OutputSeconds.IsSet());
UTEST_EQUAL("Secs. while enabled but no clock connected.", *(*OutputSeconds) , std::numeric_limits<float>::max());
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FSecsUntilTimestampTestBasic,
"Harmonix.Metasound.Nodes.SecsUntilMusicTimestamp.Basic",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FSecsUntilTimestampTestBasic::RunTest(const FString&)
{
FGraphBuilder Builder;
constexpr int32 TimeSigNumerator = 6;
constexpr int32 TimeSigDenominator = 8;
constexpr float Tempo = 60.0;
constexpr int32 Bar = 3;
constexpr float Beat = 3.0f;
const FNodeHandle TransportNode = Builder.AddNode({ HarmonixNodeNamespace, TEXT("TriggerToTransport"), TEXT("") },
0);
const FNodeHandle MetronomeNode = Builder.AddNode({HarmonixMetasound::HarmonixNodeNamespace, "Metronome", "" },
0 );
const FNodeHandle SecsUntilNode = Builder.AddNode(Nodes::SecsUntilMusicTimestampNode::GetClassName(),
Nodes::SecsUntilMusicTimestampNode::GetCurrentMajorVersion());
FNodeHandle StartInTrigger = Builder.AddAndConnectDataReferenceInput(TransportNode, CommonPinNames::Inputs::TransportPlayName, Metasound::GetMetasoundDataTypeName<FTrigger>(), "StartTest");
FNodeHandle TempoIn = Builder.AddAndConnectDataReferenceInput(MetronomeNode, CommonPinNames::Inputs::TempoName, Metasound::GetMetasoundDataTypeName<float>(), "TestTempo");
FNodeHandle TimeSigNumeratorIn = Builder.AddAndConnectDataReferenceInput(MetronomeNode, CommonPinNames::Inputs::TimeSigNumeratorName, Metasound::GetMetasoundDataTypeName<int32>(), "TestTSNum");
FNodeHandle TimeSigDenominatorIn = Builder.AddAndConnectDataReferenceInput(MetronomeNode, CommonPinNames::Inputs::TimeSigDenominatorName, Metasound::GetMetasoundDataTypeName<int32>(), "TestTSDenom");
FNodeHandle TimestampIn = Builder.AddAndConnectDataReferenceInput(SecsUntilNode, SecsUntilMusicTimestampNode::Inputs::TimestampName, Metasound::GetMetasoundDataTypeName<FMusicTimestamp>(), "TestTimestamp");
FNodeHandle SecsOut = Builder.AddAndConnectDataReferenceOutput(SecsUntilNode, SecsUntilMusicTimestampNode::Outputs::SecsUntilTimestampName, Metasound::GetMetasoundDataTypeName<float>(), "TestResult");
UTEST_TRUE("Connected Transport To Metronome",
FGraphBuilder::ConnectNodes(TransportNode, CommonPinNames::Outputs::TransportName,
MetronomeNode, CommonPinNames::Inputs::TransportName));
UTEST_TRUE("Connected Metronome To Secs Until Node",
FGraphBuilder::ConnectNodes(MetronomeNode, CommonPinNames::Outputs::MidiClockName,
SecsUntilNode, CommonPinNames::Inputs::MidiClockName));
Builder.AddOutput("AudioOut", GetMetasoundDataTypeName<FAudioBuffer>());
constexpr int32 NumSamplesPerBlock = 256;
constexpr FSampleRate SampleRate = 48000;
TUniquePtr<FMetasoundGenerator> Generator = Builder.BuildGenerator(48000, NumSamplesPerBlock);
UTEST_TRUE("Made Generator", Generator.IsValid());
FAudioBuffer Buffer{ Generator->OperatorSettings };
TOptional<FTriggerWriteRef> StartTestTrigger = Generator->GetInputWriteReference<FTrigger>("StartTest");
UTEST_TRUE("Got Start Test Trigger", StartTestTrigger.IsSet());
TOptional<FFloatWriteRef> TestTempoInput = Generator->GetInputWriteReference<float>("TestTempo");
UTEST_TRUE("Got Test Tempo Input", TestTempoInput.IsSet());
TOptional<FInt32WriteRef> TestTSNumInput = Generator->GetInputWriteReference<int32>("TestTSNum");
UTEST_TRUE("Got Test Time Sig Numerator Input", TestTSNumInput.IsSet());
TOptional<FInt32WriteRef> TestTSDenomInput = Generator->GetInputWriteReference<int32>("TestTSDenom");
UTEST_TRUE("Got Test Time Sig Denominator Input", TestTSDenomInput.IsSet());
TOptional<FMusicTimestampWriteRef> TestTimestampInput = Generator->GetInputWriteReference<FMusicTimestamp>("TestTimestamp");
UTEST_TRUE("Got Test Timestamp Input", TestTimestampInput.IsSet());
TOptional<FFloatReadRef> ResultOutput = Generator->GetOutputReadReference<float>("TestResult");
UTEST_TRUE("Got Test Result Output", ResultOutput.IsSet());
*(*TestTempoInput) = Tempo;
(*TestTimestampInput)->Bar = Bar;
(*TestTimestampInput)->Beat = Beat;
*(*TestTSNumInput) = TimeSigNumerator;
*(*TestTSDenomInput) = TimeSigDenominator;
(*StartTestTrigger)->TriggerFrame(0);
Generator->OnGenerateAudio(Buffer.GetData(), Buffer.Num());
float TotalBeats = ((Bar - 1) * TimeSigNumerator) + (Beat - 1);
float SecsPerQuarter = 60.0f / Tempo;
float SecsPerBeat = SecsPerQuarter / (TimeSigDenominator / 4);
float ExpectedSeconds = (SecsPerBeat * TotalBeats) - ((float)NumSamplesPerBlock / (float)SampleRate);
float SecsUntil = *(*ResultOutput);
UTEST_TRUE("Time Until As Expected", FMath::IsNearlyEqual(SecsUntil, ExpectedSeconds, 0.0005));
return true;
}
}
#endif