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

204 lines
5.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetasoundGenerator.h"
#include "NodeTestGraphBuilder.h"
#include "HarmonixDsp/AudioAnalysis/FFTAnalyzer.h"
#include "HarmonixDsp/Generate.h"
#include "HarmonixMetasound/Common.h"
#include "HarmonixMetasound/DataTypes/FFTAnalyzerResult.h"
#include "Misc/AutomationTest.h"
#if WITH_DEV_AUTOMATION_TESTS
namespace HarmonixMetasoundTests::FFTAnalyzerNode
{
BEGIN_DEFINE_SPEC(
FHarmonixMetasoundFFTAnalyzerNodeSpec,
"Harmonix.Metasound.Nodes.FFTAnalyzer",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
END_DEFINE_SPEC(FHarmonixMetasoundFFTAnalyzerNodeSpec)
void FHarmonixMetasoundFFTAnalyzerNodeSpec::Define()
{
Describe("When passing in some white noise", [this]()
{
It("reports the same result as the raw DSP", [this]()
{
const TUniquePtr<Metasound::FMetasoundGenerator> Generator =
Metasound::Test::FNodeTestGraphBuilder::MakeSingleNodeGraph(
{HarmonixMetasound::HarmonixNodeNamespace, TEXT("FFT Analyzer"), TEXT("")},
0);
Harmonix::Dsp::AudioAnalysis::FFFTAnalyzer FFTAnalyzerForComparison{ Generator->OperatorSettings.GetSampleRate() };
// Make some noise and copy it to the node's input
Audio::FAlignedFloatBuffer InputBuffer;
InputBuffer.SetNumUninitialized(Generator->OperatorSettings.GetNumFramesPerBlock());
HarmonixDsp::GenerateWhiteNoise(InputBuffer.GetData(), InputBuffer.Num());
auto GeneratorInputBuffer = Generator->GetInputWriteReference<Metasound::FAudioBuffer>("In");
if (!TestTrue("Got audio input", GeneratorInputBuffer.IsSet()))
{
return;
}
FHarmonixFFTAnalyzerResults RawResults;
constexpr int32 NumBlocksToRender = 4;
Metasound::FAudioBuffer OutputBuffer{ InputBuffer.Num() };
for (int32 i = 0; i < NumBlocksToRender; ++i)
{
FMemory::Memcpy(
(*GeneratorInputBuffer)->GetData(),
InputBuffer.GetData(),
InputBuffer.Num() * sizeof(float));
// Process the node
Generator->OnGenerateAudio(OutputBuffer.GetData(), OutputBuffer.Num());
// Process the raw DSP
FFTAnalyzerForComparison.Process(InputBuffer, RawResults);
}
auto GeneratorResults = Generator->GetOutputReadReference<FHarmonixFFTAnalyzerResults>("Results");
if (!TestTrue("Got results output", GeneratorResults.IsSet()))
{
return;
}
if (!TestEqual("Same number of result bins", (*GeneratorResults)->Spectrum.Num(), RawResults.Spectrum.Num()))
{
return;
}
if (!TestTrue("More than zero result bins", RawResults.Spectrum.Num() > 0))
{
return;
}
for (int32 BinIdx = 0; BinIdx < RawResults.Spectrum.Num(); ++BinIdx)
{
if (!TestTrue("Bin matches", FMath::IsNearlyEqual(RawResults.Spectrum[BinIdx], (*GeneratorResults)->Spectrum[BinIdx])))
{
return;
}
}
});
It("reports nothing when disabled", [this]()
{
TUniquePtr<Metasound::FMetasoundGenerator> Generator =
Metasound::Test::FNodeTestGraphBuilder::MakeSingleNodeGraph(
{HarmonixMetasound::HarmonixNodeNamespace, TEXT("FFT Analyzer"), TEXT("")},
0);
auto GeneratorInputBuffer = Generator->GetInputWriteReference<Metasound::FAudioBuffer>("In");
if (!TestTrue("Got audio input", GeneratorInputBuffer.IsSet()))
{
return;
}
auto GeneratorResults = Generator->GetOutputReadReference<FHarmonixFFTAnalyzerResults>("Results");
if (!TestTrue("Got results output", GeneratorResults.IsSet()))
{
return;
}
// Render a few times and expect some results
constexpr int32 NumBlocksToRender = 4;
for (int32 i = 0; i < NumBlocksToRender; ++i)
{
// Make some noise
HarmonixDsp::GenerateWhiteNoise((*GeneratorInputBuffer)->GetData(), (*GeneratorInputBuffer)->Num());
// Process the node
Metasound::FAudioBuffer OutputBuffer{ (*GeneratorInputBuffer)->Num() };
Generator->OnGenerateAudio(OutputBuffer.GetData(), OutputBuffer.Num());
}
if (!TestTrue("More than zero result bins", (*GeneratorResults)->Spectrum.Num() > 0))
{
return;
}
bool GotSomeValidResults = false;
for (int32 BinIdx = 0; BinIdx < (*GeneratorResults)->Spectrum.Num(); ++BinIdx)
{
if ((*GeneratorResults)->Spectrum[BinIdx] > 0.0f)
{
GotSomeValidResults = true;
return;
}
}
if (!TestTrue("Got some valid results", GotSomeValidResults))
{
return;
}
// Disable and expect no results
Generator->SetInputValue("Enable", false);
for (int32 i = 0; i < NumBlocksToRender; ++i)
{
// Make some noise
HarmonixDsp::GenerateWhiteNoise((*GeneratorInputBuffer)->GetData(), (*GeneratorInputBuffer)->Num());
// Process the node
Metasound::FAudioBuffer OutputBuffer{ (*GeneratorInputBuffer)->Num() };
Generator->OnGenerateAudio(OutputBuffer.GetData(), OutputBuffer.Num());
if (!TestTrue("Zero result bins", (*GeneratorResults)->Spectrum.Num() == 0))
{
return;
}
}
// Re-enable and expect some results
for (int32 i = 0; i < NumBlocksToRender; ++i)
{
// Make some noise
HarmonixDsp::GenerateWhiteNoise((*GeneratorInputBuffer)->GetData(), (*GeneratorInputBuffer)->Num());
// Process the node
Metasound::FAudioBuffer OutputBuffer{ (*GeneratorInputBuffer)->Num() };
Generator->OnGenerateAudio(OutputBuffer.GetData(), OutputBuffer.Num());
}
if (!TestTrue("More than zero result bins", (*GeneratorResults)->Spectrum.Num() > 0))
{
return;
}
GotSomeValidResults = false;
for (int32 BinIdx = 0; BinIdx < (*GeneratorResults)->Spectrum.Num(); ++BinIdx)
{
if ((*GeneratorResults)->Spectrum[BinIdx] > 0.0f)
{
GotSomeValidResults = true;
return;
}
}
if (!TestTrue("Got some valid results", GotSomeValidResults))
{
return;
}
});
});
}
}
#endif