2021-09-08 12:59:26 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "AudioDevice.h"
# include "Components/AudioComponent.h"
2021-12-10 20:37:43 -05:00
# include "IAudioParameterInterfaceRegistry.h"
2021-09-08 12:59:26 -04:00
# include "Interfaces/IPluginManager.h"
2023-03-07 17:01:52 -05:00
# include "Interfaces/MetasoundOutputFormatInterfaces.h"
2022-08-19 12:14:31 -04:00
# include "Interfaces/MetasoundFrontendSourceInterface.h"
2023-06-16 17:55:08 -04:00
# include "MetasoundBuilderSubsystem.h"
2023-06-20 13:58:22 -04:00
# include "MetasoundDataReference.h"
2021-09-08 12:59:26 -04:00
# include "MetasoundFrontendController.h"
# include "MetasoundFrontendSearchEngine.h"
# include "MetasoundSource.h"
# include "Misc/AutomationTest.h"
2023-02-11 12:39:56 -05:00
# include "Misc/Paths.h"
2021-09-08 12:59:26 -04:00
# include "Tests/AutomationCommon.h"
2023-06-20 13:58:22 -04:00
2021-09-08 12:59:26 -04:00
# if WITH_DEV_AUTOMATION_TESTS
namespace EngineTestMetasoundSourcePrivate
{
2023-06-16 17:55:08 -04:00
struct FInitTestBuilderOutput
{
FMetaSoundBuilderNodeOutputHandle OnPlayOutput ;
FMetaSoundBuilderNodeInputHandle OnFinishedInput ;
TArray < FMetaSoundBuilderNodeInputHandle > AudioOutNodeInputs ;
} ;
2021-09-08 12:59:26 -04:00
static FString GetPluginContentDirectory ( )
{
TSharedPtr < IPlugin > 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 ;
}
2022-04-27 19:04:31 -04:00
Metasound : : Frontend : : FNodeHandle AddNode ( Metasound : : Frontend : : IGraphController & InGraph , const Metasound : : FNodeClassName & InClassName , int32 InMajorVersion )
2021-09-08 12:59:26 -04:00
{
2022-04-27 19:04:31 -04:00
Metasound : : Frontend : : FNodeHandle Node = Metasound : : Frontend : : INodeController : : GetInvalidHandle ( ) ;
2021-09-08 12:59:26 -04:00
FMetasoundFrontendClass NodeClass ;
2022-04-27 19:04:31 -04:00
if ( ensure ( Metasound : : Frontend : : ISearchEngine : : Get ( ) . FindClassWithHighestMinorVersion ( InClassName , InMajorVersion , NodeClass ) ) )
{
Node = InGraph . AddNode ( NodeClass . Metadata ) ;
check ( Node - > IsValid ( ) ) ;
}
2021-09-08 12:59:26 -04:00
return Node ;
}
2023-06-16 17:55:08 -04:00
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 < float > ( ) , 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 ;
}
2021-09-08 12:59:26 -04:00
FMetasoundFrontendDocument CreateMetaSoundMonoSourceDocument ( )
{
2021-12-10 20:37:43 -05:00
using namespace Audio ;
2021-09-08 12:59:26 -04:00
using namespace Metasound ;
2023-03-07 17:01:52 -05:00
using namespace Metasound : : Engine ;
2021-09-08 12:59:26 -04:00
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 ) ;
2021-12-10 20:37:43 -05:00
FGraphHandle RootGraph = DocumentHandle - > GetRootGraph ( ) ;
2021-09-08 12:59:26 -04:00
check ( RootGraph - > IsValid ( ) ) ;
2021-12-10 20:37:43 -05:00
// Add default source & mono interface members (OnPlay, OnFinished & Mono Out)
2021-12-15 23:11:10 -05:00
FModifyRootGraphInterfaces InterfaceTransform (
2022-01-06 16:44:09 +00:00
{ } ,
2023-03-07 17:01:52 -05:00
{
SourceInterface : : GetVersion ( ) ,
2021-12-15 23:11:10 -05:00
SourceOneShotInterface : : GetVersion ( ) ,
2023-03-07 17:01:52 -05:00
OutputFormatMonoInterface : : GetVersion ( )
2022-01-06 16:44:09 +00:00
} ) ;
2021-12-10 20:37:43 -05:00
InterfaceTransform . Transform ( DocumentHandle ) ;
2021-09-08 12:59:26 -04:00
2021-12-10 20:37:43 -05:00
// Input on Play
2022-01-06 16:44:09 +00:00
FNodeHandle OnPlayOutputNode = RootGraph - > GetInputNodeWithName ( SourceInterface : : Inputs : : OnPlay ) ;
2021-12-10 20:37:43 -05:00
check ( OnPlayOutputNode - > IsValid ( ) ) ;
2021-09-08 12:59:26 -04:00
// Input Frequency
FMetasoundFrontendClassInput FrequencyInput ;
2021-12-10 20:37:43 -05:00
FrequencyInput . Name = " Frequency " ;
2021-09-08 12:59:26 -04:00
FrequencyInput . TypeName = GetMetasoundDataTypeName < float > ( ) ;
FrequencyInput . VertexID = FGuid : : NewGuid ( ) ;
FrequencyInput . DefaultLiteral . Set ( 100.f ) ;
FNodeHandle FrequencyInputNode = RootGraph - > AddInputVertex ( FrequencyInput ) ;
check ( FrequencyInputNode - > IsValid ( ) ) ;
2021-12-10 20:37:43 -05:00
// Output On Finished
2021-12-15 23:11:10 -05:00
FNodeHandle OnFinishedOutputNode = RootGraph - > GetOutputNodeWithName ( SourceOneShotInterface : : Outputs : : OnFinished ) ;
2021-12-10 20:37:43 -05:00
check ( OnFinishedOutputNode - > IsValid ( ) ) ;
2021-09-08 12:59:26 -04:00
// Output Audio
2021-12-10 20:37:43 -05:00
FNodeHandle AudioOutputNode = RootGraph - > GetOutputNodeWithName ( OutputFormatMonoInterface : : Outputs : : MonoOut ) ;
check ( AudioOutputNode - > IsValid ( ) ) ;
2021-09-08 12:59:26 -04:00
2021-12-10 20:37:43 -05:00
// osc node
2022-04-27 19:04:31 -04:00
FNodeHandle OscNode = AddNode ( * RootGraph , { " UE " , " Sine " , " Audio " } , 1 ) ;
2021-09-08 12:59:26 -04:00
// Make connections:
// frequency input "Frequency" -> oscillator "Frequency"
2021-10-12 21:21:22 -04:00
FOutputHandle OutputToConnect = FrequencyInputNode - > GetOutputWithVertexName ( " Frequency " ) ;
FInputHandle InputToConnect = OscNode - > GetInputWithVertexName ( " Frequency " ) ;
2021-09-08 12:59:26 -04:00
ensure ( InputToConnect - > Connect ( * OutputToConnect ) ) ;
// oscillator to output
2021-10-12 21:21:22 -04:00
OutputToConnect = OscNode - > GetOutputWithVertexName ( " Audio " ) ;
2021-12-10 20:37:43 -05:00
InputToConnect = AudioOutputNode - > GetInputWithVertexName ( OutputFormatMonoInterface : : Outputs : : MonoOut ) ;
2021-09-08 12:59:26 -04:00
ensure ( InputToConnect - > Connect ( * OutputToConnect ) ) ;
return Document ;
}
2023-06-16 17:55:08 -04:00
} // EngineTestMetasoundSourcePrivate
2021-09-08 12:59:26 -04:00
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 ;
}
2023-06-16 17:55:08 -04:00
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 ;
}
2021-09-08 12:59:26 -04:00
DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER ( FAudioComponentRemoveFromRootLatentCommand , UAudioComponent * , AudioComponent ) ;
bool FAudioComponentRemoveFromRootLatentCommand : : Update ( )
{
if ( AudioComponent )
{
AudioComponent - > RemoveFromRoot ( ) ;
return true ;
}
return false ;
}
2023-06-16 17:55:08 -04:00
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 )
2021-09-08 12:59:26 -04:00
bool FAudioMetasoundSourceTest : : RunTest ( const FString & Parameters )
{
UMetaSoundSource * MetaSoundSource = NewObject < UMetaSoundSource > ( GetTransientPackage ( ) , FName ( * LexToString ( FGuid : : NewGuid ( ) ) ) ) ; ;
if ( ensure ( nullptr ! = MetaSoundSource ) )
{
MetaSoundSource - > SetDocument ( EngineTestMetasoundSourcePrivate : : CreateMetaSoundMonoSourceDocument ( ) ) ;
if ( FAudioDevice * AudioDevice = GEngine - > GetMainAudioDeviceRaw ( ) )
{
UAudioComponent * AudioComponent = FAudioDevice : : CreateComponent ( MetaSoundSource ) ;
2023-06-16 17:55:08 -04:00
AddErrorIfFalse ( AudioComponent ! = nullptr , " Failed to create audio component " ) ;
2021-09-08 12:59:26 -04:00
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 ;
}
2023-06-16 17:55:08 -04:00
// 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;
2021-09-08 12:59:26 -04:00
2023-06-16 17:55:08 -04:00
FMetaSoundBuilderNodeInputHandle MonoOutNodeInput ;
UMetaSoundSourceBuilder & Builder = CreateMetaSoundMonoSourceSinGenBuilder ( * this , nullptr , & MonoOutNodeInput ) ;
Builder . AddToRoot ( ) ;
2021-09-08 12:59:26 -04:00
2023-06-16 17:55:08 -04:00
if ( FAudioDevice * AudioDevice = GEngine - > GetMainAudioDeviceRaw ( ) )
{
constexpr bool bAddToRegistry = false ;
TScriptInterface < IMetaSoundDocumentInterface > BuiltDocumentInterface = Builder . Build ( nullptr , FMetaSoundBuilderOptions { " BuildAndPlayMetasoundSource " , bAddToRegistry } ) ;
UMetaSoundSource * MetaSoundSource = CastChecked < UMetaSoundSource > ( 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 < UAudioComponent > ( ) ;
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 ) ) ;
2023-06-20 13:58:22 -04:00
// Disconnect graph audio output from existing sinosc output and connect to added triosc
2023-06-16 17:55:08 -04:00
ADD_LATENT_AUTOMATION_COMMAND ( FMetaSoundSourceBuilderDisconnectInputLatentCommand ( * this , & Builder , MonoOutNodeInput ) ) ;
ADD_LATENT_AUTOMATION_COMMAND ( FMetaSoundSourceBuilderCreateAndConnectTriGeneratorNodeLatentCommand ( * this , & Builder , MonoOutNodeInput ) ) ;
2023-06-20 13:58:22 -04:00
FName DataTypeName ;
FMetasoundFrontendLiteral NewValue ;
NewValue . Set ( FMetasoundFrontendLiteral : : FDefault { } ) ;
ADD_LATENT_AUTOMATION_COMMAND ( FMetaSoundSourceBuilderSetLiteralLatentCommand ( * this , & Builder , MonoOutNodeInput , NewValue ) ) ;
2023-06-16 17:55:08 -04:00
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 < UAudioComponent > ( ) ;
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 ) ) ;
2023-06-20 13:58:22 -04:00
// 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.
2023-06-16 17:55:08 -04:00
ADD_LATENT_AUTOMATION_COMMAND ( FMetaSoundSourceBuilderDisconnectInputLatentCommand ( * this , & Builder , GenNodeFreqInput ) ) ;
ADD_LATENT_AUTOMATION_COMMAND ( FEngineWaitLatentCommand ( 0.25f ) ) ;
2023-06-20 13:58:22 -04:00
// Sets literal value on the sinosc freq input to 880Hz, pitching an octive yet again from previous.
2023-06-16 17:55:08 -04:00
FName DataTypeName ;
FMetasoundFrontendLiteral NewValue = UMetaSoundBuilderSubsystem : : GetChecked ( ) . CreateFloatMetaSoundLiteral ( 880.f , DataTypeName ) ;
AddErrorIfFalse ( DataTypeName = = Metasound : : GetMetasoundDataTypeName < float > ( ) ,
" Setting MetaSound Float literal returns non-float DataTypeName. " ) ;
ADD_LATENT_AUTOMATION_COMMAND ( FMetaSoundSourceBuilderSetLiteralLatentCommand ( * this , & Builder , GenNodeFreqInput , NewValue ) ) ;
ADD_LATENT_AUTOMATION_COMMAND ( FEngineWaitLatentCommand ( 0.25f ) ) ;
2023-06-20 13:58:22 -04:00
// Removes the literal value on the sinosc freq input set to 880Hz, reverting back to the class literal of 440Hz.
2023-06-16 17:55:08 -04:00
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