// Copyright Epic Games, Inc. All Rights Reserved. #include "AudioDevice.h" #include "Components/AudioComponent.h" #include "IAudioParameterInterfaceRegistry.h" #include "Interfaces/IPluginManager.h" #include "Interfaces/MetasoundOutputFormatInterfaces.h" #include "Interfaces/MetasoundFrontendSourceInterface.h" #include "MetasoundBuilderSubsystem.h" #include "MetasoundDataReference.h" #include "MetasoundFrontendController.h" #include "MetasoundFrontendSearchEngine.h" #include "MetasoundSource.h" #include "Misc/AutomationTest.h" #include "Misc/Paths.h" #include "Tests/AutomationCommon.h" #if WITH_DEV_AUTOMATION_TESTS namespace EngineTestMetasoundSourcePrivate { struct FInitTestBuilderOutput { FMetaSoundBuilderNodeOutputHandle OnPlayOutput; FMetaSoundBuilderNodeInputHandle OnFinishedInput; TArray AudioOutNodeInputs; }; static FString GetPluginContentDirectory() { TSharedPtr Plugin = IPluginManager::Get().FindPlugin(TEXT("Metasound")); if (ensure(Plugin.IsValid())) { return Plugin->GetContentDir(); } return FString(); } static FString GetPathToTestFilesDir() { FString OutPath = FPaths::Combine(GetPluginContentDirectory(), TEXT("Test")); OutPath = FPaths::ConvertRelativePathToFull(OutPath); FPaths::NormalizeDirectoryName(OutPath); return OutPath; } static FString GetPathToGeneratedFilesDir() { FString OutPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Metasounds")); OutPath = FPaths::ConvertRelativePathToFull(OutPath); FPaths::NormalizeDirectoryName(OutPath); return OutPath; } static FString GetPathToGeneratedAssetsDir() { FString OutPath = TEXT("/Game/Metasound/Generated/"); FPaths::NormalizeDirectoryName(OutPath); return OutPath; } Metasound::Frontend::FNodeHandle AddNode(Metasound::Frontend::IGraphController& InGraph, const Metasound::FNodeClassName& InClassName, int32 InMajorVersion) { Metasound::Frontend::FNodeHandle Node = Metasound::Frontend::INodeController::GetInvalidHandle(); FMetasoundFrontendClass NodeClass; if (ensure(Metasound::Frontend::ISearchEngine::Get().FindClassWithHighestMinorVersion(InClassName, InMajorVersion, NodeClass))) { Node = InGraph.AddNode(NodeClass.Metadata); check(Node->IsValid()); } return Node; } UMetaSoundSourceBuilder& CreateMetaSoundSourceBuilder(EMetaSoundOutputAudioFormat OutputFormat, bool bIsOneShot, FInitTestBuilderOutput& Output) { using namespace Audio; using namespace Metasound; using namespace Metasound::Engine; using namespace Metasound::Frontend; EMetaSoundBuilderResult Result; UMetaSoundSourceBuilder* Builder = UMetaSoundBuilderSubsystem::GetChecked().CreateSourceBuilder( "Unit Test Graph Builder", Output.OnPlayOutput, Output.OnFinishedInput, Output.AudioOutNodeInputs, Result, OutputFormat, bIsOneShot); checkf(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to create MetaSoundSourceBuilder")); return *Builder; } UMetaSoundSourceBuilder& CreateMetaSoundMonoSourceSinGenBuilder( FAutomationTestBase& Test, FMetaSoundBuilderNodeInputHandle* GenInputNodeFreq = nullptr, FMetaSoundBuilderNodeInputHandle* MonoOutNodeInput = nullptr, float InDefaultFreq = 100.f) { using namespace EngineTestMetasoundSourcePrivate; using namespace Metasound; using namespace Metasound::Engine; using namespace Metasound::Frontend; constexpr EMetaSoundOutputAudioFormat OutputFormat = EMetaSoundOutputAudioFormat::Mono; constexpr bool bIsOneShot = false; FInitTestBuilderOutput Output; UMetaSoundSourceBuilder& Builder = CreateMetaSoundSourceBuilder(EMetaSoundOutputAudioFormat::Mono, bIsOneShot, Output); EMetaSoundBuilderResult Result = EMetaSoundBuilderResult::Failed; if (MonoOutNodeInput) { *MonoOutNodeInput = { }; } // Input on Play const FMetaSoundNodeHandle OnPlayOutputNode = Builder.FindGraphInputNode(SourceInterface::Inputs::OnPlay, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && OnPlayOutputNode.IsSet(), TEXT("Failed to create MetaSound OnPlay input")); // Input Frequency FMetasoundFrontendLiteral DefaultFreq; DefaultFreq.Set(InDefaultFreq); const FMetaSoundBuilderNodeOutputHandle FrequencyNodeOutput = Builder.AddGraphInputNode("Frequency", GetMetasoundDataTypeName(), DefaultFreq, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && FrequencyNodeOutput.IsSet(), TEXT("Failed to create new MetaSound graph input")); // Sine Oscillator Node const FMetaSoundNodeHandle OscNode = Builder.AddNodeByClassName({ "UE", "Sine", "Audio" }, 1, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && OscNode.IsSet(), TEXT("Failed to create new MetaSound node by class name")); // Make connections: const FMetaSoundBuilderNodeInputHandle OscNodeFrequencyInput = Builder.FindNodeInputByName(OscNode, "Frequency", Result); if (GenInputNodeFreq) { *GenInputNodeFreq = OscNodeFrequencyInput; } Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && OscNodeFrequencyInput.IsSet(), TEXT("Failed to find Sine Oscillator node input 'Frequency'")); const FMetaSoundBuilderNodeOutputHandle OscNodeAudioOutput = Builder.FindNodeOutputByName(OscNode, "Audio", Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && OscNodeAudioOutput.IsSet(), TEXT("Failed to find Sine Oscillator node output 'Audio'")); // Frequency input "Frequency" -> oscillator "Frequency" Builder.ConnectNodes(FrequencyNodeOutput, OscNodeFrequencyInput, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to connect 'Frequency' input to node input 'Frequency'")); // Oscillator to Output Node Test.AddErrorIfFalse(Output.AudioOutNodeInputs.Num() == 1, TEXT("Should only ever have one output node for mono")); if (MonoOutNodeInput) { *MonoOutNodeInput = Output.AudioOutNodeInputs.Last(); } Builder.ConnectNodes(OscNodeAudioOutput, Output.AudioOutNodeInputs.Last(), Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to connect 'Audio' Sine Oscillator output to MetaSound graph's 'Mono Output'")); return Builder; } FMetasoundFrontendDocument CreateMetaSoundMonoSourceDocument() { using namespace Audio; using namespace Metasound; using namespace Metasound::Engine; using namespace Metasound::Frontend; FMetasoundFrontendDocument Document; Document.RootGraph.Metadata.SetClassName(FMetasoundFrontendClassName { "Namespace", "Unit Test Node", *LexToString(FGuid::NewGuid()) }); Document.RootGraph.Metadata.SetType(EMetasoundFrontendClassType::Graph); FDocumentHandle DocumentHandle = IDocumentController::CreateDocumentHandle(Document); FGraphHandle RootGraph = DocumentHandle->GetRootGraph(); check(RootGraph->IsValid()); // Add default source & mono interface members (OnPlay, OnFinished & Mono Out) FModifyRootGraphInterfaces InterfaceTransform( { }, { SourceInterface::GetVersion(), SourceOneShotInterface::GetVersion(), OutputFormatMonoInterface::GetVersion() }); InterfaceTransform.Transform(DocumentHandle); // Input on Play FNodeHandle OnPlayOutputNode = RootGraph->GetInputNodeWithName(SourceInterface::Inputs::OnPlay); check(OnPlayOutputNode->IsValid()); // Input Frequency FMetasoundFrontendClassInput FrequencyInput; FrequencyInput.Name = "Frequency"; FrequencyInput.TypeName = GetMetasoundDataTypeName(); FrequencyInput.VertexID = FGuid::NewGuid(); FrequencyInput.DefaultLiteral.Set(100.f); FNodeHandle FrequencyInputNode = RootGraph->AddInputVertex(FrequencyInput); check(FrequencyInputNode->IsValid()); // Output On Finished FNodeHandle OnFinishedOutputNode = RootGraph->GetOutputNodeWithName(SourceOneShotInterface::Outputs::OnFinished); check(OnFinishedOutputNode->IsValid()); // Output Audio FNodeHandle AudioOutputNode = RootGraph->GetOutputNodeWithName(OutputFormatMonoInterface::Outputs::MonoOut); check(AudioOutputNode->IsValid()); // osc node FNodeHandle OscNode = AddNode(*RootGraph, { "UE", "Sine", "Audio" }, 1); // Make connections: // frequency input "Frequency" -> oscillator "Frequency" FOutputHandle OutputToConnect = FrequencyInputNode->GetOutputWithVertexName("Frequency"); FInputHandle InputToConnect = OscNode->GetInputWithVertexName("Frequency"); ensure(InputToConnect->Connect(*OutputToConnect)); // oscillator to output OutputToConnect = OscNode->GetOutputWithVertexName("Audio"); InputToConnect = AudioOutputNode->GetInputWithVertexName(OutputFormatMonoInterface::Outputs::MonoOut); ensure(InputToConnect->Connect(*OutputToConnect)); return Document; } } // EngineTestMetasoundSourcePrivate DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FAudioComponentPlayLatentCommand, UAudioComponent*, AudioComponent); bool FAudioComponentPlayLatentCommand::Update() { if (AudioComponent) { AudioComponent->Play(); return true; } return false; } DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FAudioComponentStopLatentCommand, UAudioComponent*, AudioComponent); bool FAudioComponentStopLatentCommand::Update() { if (AudioComponent) { AudioComponent->Stop(); return true; } return false; } DEFINE_LATENT_AUTOMATION_COMMAND_THREE_PARAMETER(FMetaSoundSourceBuilderAuditionLatentCommand, UMetaSoundSourceBuilder*, Builder, UAudioComponent*, AudioComponent, bool, bEnableLiveUpdates); bool FMetaSoundSourceBuilderAuditionLatentCommand::Update() { if (Builder && AudioComponent) { Builder->Audition(nullptr, AudioComponent, { }, true); return true; } return false; } DEFINE_LATENT_AUTOMATION_COMMAND_THREE_PARAMETER(FMetaSoundSourceBuilderDisconnectInputLatentCommand, FAutomationTestBase&, Test, UMetaSoundBuilderBase*, Builder, FMetaSoundBuilderNodeInputHandle, InputToDisconnect); bool FMetaSoundSourceBuilderDisconnectInputLatentCommand::Update() { EMetaSoundBuilderResult Result = EMetaSoundBuilderResult::Failed; if (Builder) { Builder->DisconnectNodeInput(InputToDisconnect, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to disconnect MetaSound node's input")); } return Result == EMetaSoundBuilderResult::Succeeded; } DEFINE_LATENT_AUTOMATION_COMMAND_FOUR_PARAMETER(FMetaSoundSourceBuilderSetLiteralLatentCommand, FAutomationTestBase&, Test, UMetaSoundBuilderBase*, Builder, FMetaSoundBuilderNodeInputHandle, NodeInput, FMetasoundFrontendLiteral, NewValue); bool FMetaSoundSourceBuilderSetLiteralLatentCommand::Update() { EMetaSoundBuilderResult Result = EMetaSoundBuilderResult::Failed; if (Builder) { Builder->SetNodeInputDefault(NodeInput, NewValue, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to disconnect MetaSound node's input")); } return Result == EMetaSoundBuilderResult::Succeeded; } DEFINE_LATENT_AUTOMATION_COMMAND_THREE_PARAMETER(FMetaSoundSourceBuilderRemoveNodeDefaultLiteralLatentCommand, FAutomationTestBase&, Test, UMetaSoundBuilderBase*, Builder, FMetaSoundBuilderNodeInputHandle, NodeInput); bool FMetaSoundSourceBuilderRemoveNodeDefaultLiteralLatentCommand::Update() { EMetaSoundBuilderResult Result = EMetaSoundBuilderResult::Failed; if (Builder) { Builder->RemoveNodeInputDefault(NodeInput, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to disconnect MetaSound node's input")); } return Result == EMetaSoundBuilderResult::Succeeded; } DEFINE_LATENT_AUTOMATION_COMMAND_THREE_PARAMETER(FMetaSoundSourceBuilderCreateAndConnectTriGeneratorNodeLatentCommand, FAutomationTestBase&, Test, UMetaSoundSourceBuilder*, Builder, FMetaSoundBuilderNodeInputHandle, AudioOutNodeInput); bool FMetaSoundSourceBuilderCreateAndConnectTriGeneratorNodeLatentCommand::Update() { EMetaSoundBuilderResult Result = EMetaSoundBuilderResult::Failed; if (Builder) { // Tri Oscillator Node FMetaSoundNodeHandle TriNode = Builder->AddNodeByClassName({ "UE", "Triangle", "Audio" }, 1, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded && TriNode.IsSet(), TEXT("Failed to create node by class name 'UE:Triangle:Audio")); FMetaSoundBuilderNodeOutputHandle TriNodeAudioOutput = Builder->FindNodeOutputByName(TriNode, "Audio", Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to find Triangle Oscillator node output 'Audio'")); Builder->ConnectNodes(TriNodeAudioOutput, AudioOutNodeInput, Result); Test.AddErrorIfFalse(Result == EMetaSoundBuilderResult::Succeeded, TEXT("Failed to connect 'Audio' Triangle Oscillator output to MetaSound graph's 'Mono Output'")); } return Result == EMetaSoundBuilderResult::Succeeded; } DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FAudioComponentRemoveFromRootLatentCommand, UAudioComponent*, AudioComponent); bool FAudioComponentRemoveFromRootLatentCommand::Update() { if (AudioComponent) { AudioComponent->RemoveFromRoot(); return true; } return false; } DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FBuilderRemoveFromRootLatentCommand, UMetaSoundBuilderBase*, Builder); bool FBuilderRemoveFromRootLatentCommand::Update() { if (Builder) { Builder->RemoveFromRoot(); return true; } return false; } // This test creates a MetaSound from the legacy controller document editing system and attempts to play it directly. IMPLEMENT_SIMPLE_AUTOMATION_TEST(FAudioMetasoundSourceTest, "Audio.Metasound.PlayMetasoundSource", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FAudioMetasoundSourceTest::RunTest(const FString& Parameters) { UMetaSoundSource* MetaSoundSource = NewObject(GetTransientPackage(), FName(*LexToString(FGuid::NewGuid())));; if (ensure(nullptr != MetaSoundSource)) { MetaSoundSource->SetDocument(EngineTestMetasoundSourcePrivate::CreateMetaSoundMonoSourceDocument()); if (FAudioDevice* AudioDevice = GEngine->GetMainAudioDeviceRaw()) { UAudioComponent* AudioComponent = FAudioDevice::CreateComponent(MetaSoundSource); AddErrorIfFalse(AudioComponent != nullptr, "Failed to create audio component"); if (AudioComponent) { AudioComponent->bIsUISound = true; AudioComponent->bAllowSpatialization = false; AudioComponent->SetVolumeMultiplier(1.0f); AudioComponent->AddToRoot(); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentPlayLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentStopLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.5f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentRemoveFromRootLatentCommand(AudioComponent)); } } } return true; } // This test creates a MetaSound source from a SourceBuilder, adds a simple sine tone generator with a connected graph input frequency, and attempts to audition it. IMPLEMENT_SIMPLE_AUTOMATION_TEST(FAudioMetasoundSourceBuilderTest, "Audio.Metasound.AuditionMetasoundSource", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FAudioMetasoundSourceBuilderTest::RunTest(const FString& Parameters) { using namespace EngineTestMetasoundSourcePrivate; // using namespace Metasound; // using namespace Metasound::Engine; // using namespace Metasound::Frontend; FMetaSoundBuilderNodeInputHandle MonoOutNodeInput; UMetaSoundSourceBuilder& Builder = CreateMetaSoundMonoSourceSinGenBuilder(*this, nullptr, &MonoOutNodeInput); Builder.AddToRoot(); if (FAudioDevice* AudioDevice = GEngine->GetMainAudioDeviceRaw()) { constexpr bool bAddToRegistry = false; TScriptInterface BuiltDocumentInterface = Builder.Build(nullptr, FMetaSoundBuilderOptions { "BuildAndPlayMetasoundSource", bAddToRegistry }); UMetaSoundSource* MetaSoundSource = CastChecked(BuiltDocumentInterface.GetObject()); UAudioComponent* AudioComponent = FAudioDevice::CreateComponent(MetaSoundSource); AddErrorIfFalse(AudioComponent != nullptr, "Failed to create audio component"); if (AudioComponent) { AudioComponent->bAutoActivate = false; AudioComponent->bIsUISound = true; // play while "paused" AudioComponent->AudioDeviceID = AudioDevice->DeviceID; AudioComponent->bAllowSpatialization = false; AudioComponent->SetVolumeMultiplier(1.0f); AudioComponent->AddToRoot(); constexpr bool bEnableLiveUpdate = false; ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderAuditionLatentCommand(&Builder, AudioComponent, bEnableLiveUpdate)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentStopLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.5f)); ADD_LATENT_AUTOMATION_COMMAND(FBuilderRemoveFromRootLatentCommand(&Builder)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentRemoveFromRootLatentCommand(AudioComponent)); } } return true; } // This test creates a MetaSound source from a SourceBuilder, adds a simple sine tone generator with a connected graph input frequency, attempts to audition it, and then switches to a new tri tone generator during playback. IMPLEMENT_SIMPLE_AUTOMATION_TEST(FAudioMetasoundSourceBuilderLiveUpdateNode, "Audio.Metasound.LiveUpdateNodeMetaSoundSource", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FAudioMetasoundSourceBuilderLiveUpdateNode::RunTest(const FString& Parameters) { using namespace EngineTestMetasoundSourcePrivate; using namespace Metasound; using namespace Metasound::Engine; using namespace Metasound::Frontend; FMetaSoundBuilderNodeInputHandle MonoOutNodeInput; UMetaSoundSourceBuilder& Builder = CreateMetaSoundMonoSourceSinGenBuilder(*this, nullptr, &MonoOutNodeInput); Builder.AddToRoot(); if (FAudioDevice* AudioDevice = GEngine->GetMainAudioDeviceRaw()) { UAudioComponent* AudioComponent = NewObject(); AddErrorIfFalse(AudioComponent != nullptr, "Failed to create audio component"); if (AudioComponent) { AudioComponent->bAutoActivate = false; AudioComponent->bIsUISound = true; // play while "paused" AudioComponent->AudioDeviceID = AudioDevice->DeviceID; AudioComponent->bAllowSpatialization = false; AudioComponent->SetVolumeMultiplier(1.0f); AudioComponent->AddToRoot(); constexpr bool bEnableLiveUpdate = true; ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderAuditionLatentCommand(&Builder, AudioComponent, bEnableLiveUpdate)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.f)); // Disconnect graph audio output from existing sinosc output and connect to added triosc ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderDisconnectInputLatentCommand(*this, &Builder, MonoOutNodeInput)); ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderCreateAndConnectTriGeneratorNodeLatentCommand(*this, &Builder, MonoOutNodeInput)); FName DataTypeName; FMetasoundFrontendLiteral NewValue; NewValue.Set(FMetasoundFrontendLiteral::FDefault{ }); ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderSetLiteralLatentCommand(*this, &Builder, MonoOutNodeInput, NewValue)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentStopLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.5f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentRemoveFromRootLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FBuilderRemoveFromRootLatentCommand(&Builder)); } } return true; } // This test creates a MetaSound source from a SourceBuilder, adds a simple sine tone generator with a connected graph input frequency, attempts to audition it, // disconnects frequency input, sets the sinosc frequency literal value to a new value, and finally removes the literal value default to have it return to the // class default. IMPLEMENT_SIMPLE_AUTOMATION_TEST(FAudioMetasoundSourceBuilderLiveUpdateLiteral, "Audio.Metasound.LiveUpdateLiteralMetaSoundSource", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FAudioMetasoundSourceBuilderLiveUpdateLiteral::RunTest(const FString& Parameters) { using namespace EngineTestMetasoundSourcePrivate; FMetaSoundBuilderNodeInputHandle MonoOutNodeInput; FMetaSoundBuilderNodeInputHandle GenNodeFreqInput; UMetaSoundSourceBuilder& Builder = CreateMetaSoundMonoSourceSinGenBuilder(*this, &GenNodeFreqInput , &MonoOutNodeInput, 220.f); Builder.AddToRoot(); if (FAudioDevice* AudioDevice = GEngine->GetMainAudioDeviceRaw()) { UAudioComponent* AudioComponent = NewObject(); AddErrorIfFalse(AudioComponent != nullptr, "Failed to create audio component"); if (AudioComponent) { AudioComponent->bAutoActivate = false; AudioComponent->bIsUISound = true; // play while "paused" AudioComponent->AudioDeviceID = AudioDevice->DeviceID; AudioComponent->bAllowSpatialization = false; AudioComponent->SetVolumeMultiplier(1.0f); AudioComponent->AddToRoot(); constexpr bool bEnableLiveUpdate = true; ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderAuditionLatentCommand(&Builder, AudioComponent, bEnableLiveUpdate)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.25f)); // Disconnects freq input node output from sinosc freq input. Initially was set to 220Hz above, and node's default is 440Hz, // resulting in an octive pitch up. ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderDisconnectInputLatentCommand(*this, &Builder, GenNodeFreqInput)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.25f)); // Sets literal value on the sinosc freq input to 880Hz, pitching an octive yet again from previous. FName DataTypeName; FMetasoundFrontendLiteral NewValue = UMetaSoundBuilderSubsystem::GetChecked().CreateFloatMetaSoundLiteral(880.f, DataTypeName); AddErrorIfFalse(DataTypeName == Metasound::GetMetasoundDataTypeName(), "Setting MetaSound Float literal returns non-float DataTypeName."); ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderSetLiteralLatentCommand(*this, &Builder, GenNodeFreqInput, NewValue)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.25f)); // Removes the literal value on the sinosc freq input set to 880Hz, reverting back to the class literal of 440Hz. ADD_LATENT_AUTOMATION_COMMAND(FMetaSoundSourceBuilderRemoveNodeDefaultLiteralLatentCommand(*this, &Builder, GenNodeFreqInput)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.25f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentStopLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.25f)); ADD_LATENT_AUTOMATION_COMMAND(FAudioComponentRemoveFromRootLatentCommand(AudioComponent)); ADD_LATENT_AUTOMATION_COMMAND(FBuilderRemoveFromRootLatentCommand(&Builder)); } } return true; } #endif // WITH_DEV_AUTOMATION_TESTS