2020-07-23 20:32:26 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundSource.h"
2023-11-03 19:38:58 -04:00
# include "Algo/AnyOf.h"
2023-03-07 17:01:52 -05:00
# include "Algo/Find.h"
2021-11-18 14:37:34 -05:00
# include "Algo/Transform.h"
2022-05-02 18:59:38 -04:00
# include "AssetRegistry/AssetRegistryModule.h"
2022-01-11 13:52:09 -05:00
# include "AudioDeviceManager.h"
2023-10-03 12:17:13 -04:00
# include "Containers/Ticker.h"
2021-12-10 20:37:31 -05:00
# include "IAudioParameterInterfaceRegistry.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"
2020-07-23 20:32:26 -04:00
# include "Internationalization/Text.h"
# include "MetasoundAssetBase.h"
2022-08-10 14:18:10 -04:00
# include "MetasoundAssetManager.h"
2020-12-14 15:48:27 -04:00
# include "MetasoundAudioFormats.h"
2023-03-07 17:01:52 -05:00
# include "MetasoundBuilderSubsystem.h"
2023-09-05 17:54:02 -04:00
# include "MetasoundDocumentInterface.h"
2023-06-14 17:01:59 -04:00
# include "MetasoundDynamicOperatorTransactor.h"
2022-10-13 17:38:11 -04:00
# include "MetasoundEngineAsset.h"
2020-11-17 17:52:08 -04:00
# include "MetasoundEngineEnvironment.h"
2021-06-08 10:52:31 -04:00
# include "MetasoundEnvironment.h"
2021-01-22 03:05:22 -04:00
# include "MetasoundFrontendController.h"
2021-08-30 14:08:45 -04:00
# include "MetasoundFrontendDataTypeRegistry.h"
2023-03-07 17:01:52 -05:00
# include "MetasoundFrontendDocumentBuilder.h"
2023-08-21 12:25:01 -04:00
# include "MetasoundFrontendDocumentIdGenerator.h"
2021-01-22 03:05:22 -04:00
# include "MetasoundFrontendQuery.h"
# include "MetasoundFrontendQuerySteps.h"
2021-05-28 16:17:48 -04:00
# include "MetasoundFrontendTransform.h"
2020-07-23 20:32:26 -04:00
# include "MetasoundGenerator.h"
2020-12-14 15:48:27 -04:00
# include "MetasoundLog.h"
2022-05-10 16:51:39 -04:00
# include "MetasoundOperatorBuilderSettings.h"
2023-10-13 17:49:56 -04:00
# include "MetasoundOperatorCacheSubsystem.h"
2021-02-16 01:26:50 -04:00
# include "MetasoundOperatorSettings.h"
2021-08-30 14:08:45 -04:00
# include "MetasoundParameterTransmitter.h"
2020-07-23 20:32:26 -04:00
# include "MetasoundPrimitives.h"
2021-01-22 03:05:22 -04:00
# include "MetasoundReceiveNode.h"
2022-03-01 21:25:04 -05:00
# include "MetasoundSettings.h"
2021-08-11 15:22:01 -04:00
# include "MetasoundTrace.h"
2021-01-27 15:54:01 -04:00
# include "MetasoundTrigger.h"
2021-06-08 10:52:31 -04:00
# include "MetasoundUObjectRegistry.h"
# include "UObject/ObjectSaveContext.h"
2023-03-07 17:01:52 -05:00
# include "UObject/ScriptInterface.h"
2020-07-23 20:32:26 -04:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(MetasoundSource)
2020-07-23 20:32:26 -04:00
# if WITH_EDITORONLY_DATA
# include "EdGraph/EdGraph.h"
# endif // WITH_EDITORONLY_DATA
2021-12-10 20:37:31 -05:00
# define LOCTEXT_NAMESPACE "MetaSound"
2021-04-02 02:09:50 -04:00
2022-01-11 13:52:09 -05:00
namespace Metasound
2021-07-28 17:12:57 -04:00
{
2023-11-01 12:17:48 -04:00
namespace ConsoleVariables
{
2023-11-02 18:41:29 -04:00
bool bEnableExperimentalRuntimePresetGraphInflation = true ;
2023-11-01 12:17:48 -04:00
}
2022-01-11 13:52:09 -05:00
namespace SourcePrivate
2023-07-20 14:02:15 -04:00
{
2023-11-02 18:41:29 -04:00
static const FLazyName TriggerName = " Trigger " ;
2023-10-04 17:59:57 -04:00
// Holds onto a global static TSet for tracking which error/warning logs have been
// trigger in order to avoid log spam.
bool HasNotBeenLoggedForThisObject ( const UMetaSoundSource & InMetaSound , uint32 InLogLineNumber )
{
using FObjectAddressAndLineNum = TTuple < const void * , uint32 > ;
static TSet < FObjectAddressAndLineNum > LoggedSet ;
bool bIsAlreadyInSet = false ;
LoggedSet . Add ( FObjectAddressAndLineNum ( & InMetaSound , InLogLineNumber ) , & bIsAlreadyInSet ) ;
return ! bIsAlreadyInSet ;
}
2022-08-10 14:18:10 -04:00
Frontend : : FMetaSoundAssetRegistrationOptions GetInitRegistrationOptions ( )
{
Frontend : : FMetaSoundAssetRegistrationOptions RegOptions ;
RegOptions . bForceReregister = false ;
2023-08-21 12:25:01 -04:00
# if !WITH_EDITOR
if ( Frontend : : MetaSoundEnableCookDeterministicIDGeneration ! = 0 )
{
2023-09-05 17:54:02 -04:00
// When without editor, don't AutoUpdate or ResolveDocument at runtime. This only happens at cook or save.
// When with editor, those are needed because sounds are not necessarily saved before previewing.
2023-08-21 12:25:01 -04:00
RegOptions . bAutoUpdate = false ;
}
# endif // !WITH_EDITOR
2022-08-10 14:18:10 -04:00
if ( const UMetaSoundSettings * Settings = GetDefault < UMetaSoundSettings > ( ) )
{
RegOptions . bAutoUpdateLogWarningOnDroppedConnection = Settings - > bAutoUpdateLogWarningOnDroppedConnection ;
}
return RegOptions ;
}
2023-04-05 17:38:47 -04:00
class FParameterRouter
{
2023-04-06 18:47:28 -04:00
struct FQueueState
{
TWeakPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > DataChannel ;
bool bWriterAvailable = true ;
} ;
2023-04-05 17:38:47 -04:00
public :
using FAudioDeviceIDAndInstanceID = TTuple < Audio : : DeviceID , uint64 > ;
2023-04-06 18:47:28 -04:00
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > FindOrCreateDataChannelForReader ( Audio : : DeviceID InDeviceID , uint64 InstanceID )
2023-04-05 17:38:47 -04:00
{
2023-04-06 18:47:28 -04:00
constexpr bool bIsForWriter = false ;
return FindOrCreateDataChannel ( InDeviceID , InstanceID , bIsForWriter ) ;
}
2023-04-05 17:38:47 -04:00
2023-04-06 18:47:28 -04:00
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > FindOrCreateDataChannelForWriter ( Audio : : DeviceID InDeviceID , uint64 InstanceID )
{
constexpr bool bIsForWriter = true ;
return FindOrCreateDataChannel ( InDeviceID , InstanceID , bIsForWriter ) ;
2023-04-05 17:38:47 -04:00
}
private :
2023-04-06 18:47:28 -04:00
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > FindOrCreateDataChannel ( Audio : : DeviceID InDeviceID , uint64 InstanceID , bool bIsForWriter )
{
FScopeLock Lock ( & DataChannelMapCS ) ;
FAudioDeviceIDAndInstanceID Key = { InDeviceID , InstanceID } ;
const bool bIsForReader = ! bIsForWriter ;
if ( FQueueState * State = DataChannels . Find ( Key ) )
{
// Allow multiple readers to be returned because FMetaSoundGenerators are recreated when they come out of virtualization.
// Only allow a single writer to be returned because FMetaSoundParameterTransmitters are only created once
const bool bIsAvailable = bIsForReader | | ( State - > bWriterAvailable & & bIsForWriter ) ;
if ( bIsAvailable )
{
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > Channel = State - > DataChannel . Pin ( ) ;
if ( Channel . IsValid ( ) )
{
if ( bIsForWriter )
{
State - > bWriterAvailable = false ;
}
return Channel ;
}
}
}
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > NewChannel = MakeShared < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > ( ) ;
FQueueState NewState ;
NewState . DataChannel = NewChannel ;
if ( bIsForWriter )
{
NewState . bWriterAvailable = false ;
}
DataChannels . Add ( Key , NewState ) ;
return NewChannel ;
}
2023-04-05 17:38:47 -04:00
FCriticalSection DataChannelMapCS ;
2023-04-06 18:47:28 -04:00
TSortedMap < FAudioDeviceIDAndInstanceID , FQueueState > DataChannels ;
2023-04-05 17:38:47 -04:00
} ;
2023-11-01 12:17:48 -04:00
2023-11-02 18:41:29 -04:00
void CreateUObjectProxies ( const Frontend : : IDataTypeRegistry & InRegistry , FName InVertexTypeName , bool bClearUObjectPointers , FAudioParameter & InOutParamToInit )
{
using namespace Metasound ;
switch ( InOutParamToInit . ParamType )
{
case EAudioParameterType : : Object :
{
TSharedPtr < Audio : : IProxyData > ProxyPtr = InRegistry . CreateProxyFromUObject ( InVertexTypeName , InOutParamToInit . ObjectParam ) ;
InOutParamToInit . ObjectProxies . Emplace ( MoveTemp ( ProxyPtr ) ) ;
if ( bClearUObjectPointers )
{
InOutParamToInit . ObjectParam = nullptr ;
}
}
break ;
case EAudioParameterType : : ObjectArray :
{
const FName ElementTypeName = CreateElementTypeNameFromArrayTypeName ( InVertexTypeName ) ;
for ( TObjectPtr < UObject > & Object : InOutParamToInit . ArrayObjectParam )
{
TSharedPtr < Audio : : IProxyData > ProxyPtr = InRegistry . CreateProxyFromUObject ( ElementTypeName , Object ) ;
InOutParamToInit . ObjectProxies . Emplace ( MoveTemp ( ProxyPtr ) ) ;
}
if ( bClearUObjectPointers )
{
InOutParamToInit . ArrayObjectParam . Reset ( ) ;
}
}
break ;
default :
break ;
}
}
FAudioParameter MakeAudioParameter ( const Frontend : : IDataTypeRegistry & InRegistry , FName InParamName , FName InTypeName , const FMetasoundFrontendLiteral & InLiteral , bool bCreateUObjectProxies )
{
constexpr bool bClearUObjectPointers = false ;
FAudioParameter Params ;
Params . ParamName = InParamName ;
Params . TypeName = InTypeName ;
switch ( InLiteral . GetType ( ) )
{
case EMetasoundFrontendLiteralType : : Boolean :
{
if ( Params . TypeName = = TriggerName )
{
Params . ParamType = EAudioParameterType : : Trigger ;
}
else
{
Params . ParamType = EAudioParameterType : : Boolean ;
}
ensure ( InLiteral . TryGet ( Params . BoolParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : BooleanArray :
{
Params . ParamType = EAudioParameterType : : BooleanArray ;
ensure ( InLiteral . TryGet ( Params . ArrayBoolParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : Integer :
{
Params . ParamType = EAudioParameterType : : Integer ;
ensure ( InLiteral . TryGet ( Params . IntParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : IntegerArray :
{
Params . ParamType = EAudioParameterType : : IntegerArray ;
ensure ( InLiteral . TryGet ( Params . ArrayIntParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : Float :
{
Params . ParamType = EAudioParameterType : : Float ;
ensure ( InLiteral . TryGet ( Params . FloatParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : FloatArray :
{
Params . ParamType = EAudioParameterType : : FloatArray ;
ensure ( InLiteral . TryGet ( Params . ArrayFloatParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : String :
{
Params . ParamType = EAudioParameterType : : String ;
ensure ( InLiteral . TryGet ( Params . StringParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : StringArray :
{
Params . ParamType = EAudioParameterType : : StringArray ;
ensure ( InLiteral . TryGet ( Params . ArrayStringParam ) ) ;
}
break ;
case EMetasoundFrontendLiteralType : : UObject :
{
Params . ParamType = EAudioParameterType : : Object ;
UObject * Object = nullptr ;
ensure ( InLiteral . TryGet ( Object ) ) ;
Params . ObjectParam = Object ;
if ( bCreateUObjectProxies )
{
CreateUObjectProxies ( InRegistry , InTypeName , bClearUObjectPointers , Params ) ;
}
}
break ;
case EMetasoundFrontendLiteralType : : UObjectArray :
{
Params . ParamType = EAudioParameterType : : ObjectArray ;
ensure ( InLiteral . TryGet ( MutableView ( Params . ArrayObjectParam ) ) ) ;
if ( bCreateUObjectProxies )
{
CreateUObjectProxies ( InRegistry , InTypeName , bClearUObjectPointers , Params ) ;
}
}
break ;
default :
break ;
}
return Params ;
}
2022-01-11 13:52:09 -05:00
} // namespace SourcePrivate
} // namespace Metasound
2021-04-02 02:09:50 -04:00
2023-11-01 12:17:48 -04:00
FAutoConsoleVariableRef CVarMetaSoundEnableExperimentalRUntimePresetGraphInflation (
TEXT ( " au.MetaSound.Experimental.EnableRuntimePresetGraphInflation " ) ,
Metasound : : ConsoleVariables : : bEnableExperimentalRuntimePresetGraphInflation ,
TEXT ( " Enables experimental feature of MetaSounds which reduces overhead of preset graphs \n " )
2023-11-02 18:41:29 -04:00
TEXT ( " Default: true " ) ,
2023-11-01 12:17:48 -04:00
ECVF_Default ) ;
2021-02-19 18:30:53 -04:00
2021-04-02 02:09:50 -04:00
UMetaSoundSource : : UMetaSoundSource ( const FObjectInitializer & ObjectInitializer )
2020-07-23 20:32:26 -04:00
: Super ( ObjectInitializer )
2021-06-08 10:52:31 -04:00
, FMetasoundAssetBase ( )
2020-07-23 20:32:26 -04:00
{
2022-11-17 16:36:26 -05:00
bProcedural = true ;
2021-01-11 14:18:46 -04:00
bRequiresStopFade = true ;
2020-07-23 20:32:26 -04:00
NumChannels = 1 ;
}
2023-05-10 20:28:39 -04:00
const UClass & UMetaSoundSource : : GetBaseMetaSoundUClass ( ) const
{
return * UMetaSoundSource : : StaticClass ( ) ;
}
2023-09-22 15:01:07 -04:00
const FMetasoundFrontendDocument & UMetaSoundSource : : GetConstDocument ( ) const
2023-05-10 20:28:39 -04:00
{
return RootMetasoundDocument ;
}
2020-12-14 15:48:27 -04:00
# if WITH_EDITOR
2021-04-02 02:09:50 -04:00
void UMetaSoundSource : : PostEditUndo ( )
2021-03-02 21:39:09 -04:00
{
Super : : PostEditUndo ( ) ;
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : PostEditUndo ( * this ) ;
2021-03-02 21:39:09 -04:00
}
2021-07-28 16:21:39 -04:00
void UMetaSoundSource : : PostDuplicate ( EDuplicateMode : : Type InDuplicateMode )
{
Super : : PostDuplicate ( InDuplicateMode ) ;
2021-11-22 15:55:50 -05:00
// Guid is reset as asset may share implementation from
// asset duplicated from but should not be registered as such.
if ( InDuplicateMode = = EDuplicateMode : : Normal )
{
2022-03-10 21:19:13 -05:00
AssetClassID = FGuid : : NewGuid ( ) ;
Metasound : : Frontend : : FRenameRootGraphClass : : Generate ( GetDocumentHandle ( ) , AssetClassID ) ;
2021-11-22 15:55:50 -05:00
}
2021-07-28 16:21:39 -04:00
}
2021-04-02 02:09:50 -04:00
void UMetaSoundSource : : PostEditChangeProperty ( FPropertyChangedEvent & InEvent )
2020-12-14 15:48:27 -04:00
{
Super : : PostEditChangeProperty ( InEvent ) ;
2021-06-08 10:52:31 -04:00
if ( InEvent . GetPropertyName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSource , OutputFormat ) )
2020-12-14 15:48:27 -04:00
{
2022-08-15 10:49:30 -04:00
PostEditChangeOutputFormat ( ) ;
}
2023-09-21 18:46:14 -04:00
if ( InEvent . GetPropertyName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSource , SampleRateOverride ) | |
InEvent . GetPropertyName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSource , BlockRateOverride ) | |
InEvent . GetPropertyName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSource , QualitySetting ) )
{
PostEditChangeQualitySettings ( ) ;
}
}
bool UMetaSoundSource : : CanEditChange ( const FProperty * InProperty ) const
{
if ( ! Super : : CanEditChange ( InProperty ) )
{
return false ;
}
// Allow changes to quality if we don't have any overrides.
if ( InProperty - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSource , QualitySetting ) )
{
2023-10-11 19:55:59 -04:00
const TArray < FName > Platforms = FDataDrivenPlatformInfoRegistry : : GetSortedPlatformNames ( EPlatformInfoType : : AllPlatformInfos ) ;
const int32 DefaultBlockRate = BlockRateOverride . GetDefault ( ) ;
const float DefaultSampleRate = BlockRateOverride . GetDefault ( ) ;
2023-09-21 18:46:14 -04:00
2023-10-11 19:55:59 -04:00
if ( DefaultBlockRate > 0 & & DefaultSampleRate > 0 )
{
return false ;
}
for ( const FName Platform : Platforms )
{
if ( BlockRateOverride . GetValueForPlatform ( Platform ) ! = DefaultBlockRate )
{
return false ;
}
if ( ! FMath : : IsNearlyEqual ( SampleRateOverride . GetValueForPlatform ( Platform ) , DefaultSampleRate ) )
{
return false ;
}
}
2023-09-21 18:46:14 -04:00
}
return true ;
2022-08-15 10:49:30 -04:00
}
2021-06-08 10:52:31 -04:00
2022-08-15 10:49:30 -04:00
void UMetaSoundSource : : PostEditChangeOutputFormat ( )
{
using namespace Metasound : : Frontend ;
2023-08-02 14:35:48 -04:00
EMetaSoundBuilderResult Result = EMetaSoundBuilderResult : : Failed ;
{
const UMetaSoundBuilderSubsystem & BuilderSubsystem = UMetaSoundBuilderSubsystem : : GetConstChecked ( ) ;
2023-08-09 16:45:21 -04:00
2023-08-02 14:35:48 -04:00
UMetaSoundSourceBuilder * SourceBuilder = BuilderSubsystem . AttachSourceBuilderToAsset ( this ) ;
2023-08-09 16:45:21 -04:00
check ( SourceBuilder ) ;
2023-08-02 14:35:48 -04:00
SourceBuilder - > SetFormat ( OutputFormat , Result ) ;
2023-08-09 16:45:21 -04:00
2023-08-02 14:35:48 -04:00
// TODO: Once builders are notified of controller changes and can be safely persistent, this
// can be removed so builders can be shared and not have to be created for each change output
// format mutation transaction.
2023-09-22 15:01:07 -04:00
BuilderSubsystem . DetachBuilderFromAsset ( GetConstDocument ( ) . RootGraph . Metadata . GetClassName ( ) ) ;
2023-08-02 14:35:48 -04:00
}
2023-03-07 17:01:52 -05:00
if ( Result = = EMetaSoundBuilderResult : : Succeeded )
2022-08-15 10:49:30 -04:00
{
// Update the data in this UMetaSoundSource to reflect what is in the metasound document.
ConformObjectDataToInterfaces ( ) ;
// Use the editor form of register to ensure other editors'
// MetaSounds are auto-updated if they are referencing this graph.
if ( Graph )
2021-06-08 10:52:31 -04:00
{
2022-08-15 10:49:30 -04:00
Graph - > RegisterGraphWithFrontend ( ) ;
2020-12-14 15:48:27 -04:00
}
2022-08-15 10:49:30 -04:00
MarkMetasoundDocumentDirty ( ) ;
2020-12-14 15:48:27 -04:00
}
}
2023-09-21 18:46:14 -04:00
void UMetaSoundSource : : PostEditChangeQualitySettings ( )
{
// Re-cache Operator settings by clearing the Optional.
OperatorSettings . Reset ( ) ;
// Refresh the SampleRate (which is what the engine sees from the operator settings).
2023-10-11 19:55:59 -04:00
SampleRate = GetOperatorSettings ( CachedAudioDeviceSampleRate ) . GetSampleRate ( ) ;
2023-09-21 18:46:14 -04:00
// Always refresh the GUID with the selection.
if ( const UMetaSoundSettings * Settings = GetDefault < UMetaSoundSettings > ( ) )
{
auto FindByName = [ & Name = QualitySetting ] ( const FMetaSoundQualitySettings & Q ) - > bool { return Q . Name = = Name ; } ;
if ( const FMetaSoundQualitySettings * Found = Settings - > QualitySettings . FindByPredicate ( FindByName ) )
{
QualitySettingGuid = Found - > UniqueId ;
}
}
}
2021-06-08 10:52:31 -04:00
# endif // WITH_EDITOR
2020-12-14 15:48:27 -04:00
2021-11-22 15:55:50 -05:00
bool UMetaSoundSource : : ConformObjectDataToInterfaces ( )
2021-08-18 15:16:57 -04:00
{
2023-03-07 17:01:52 -05:00
using namespace Metasound : : Engine ;
using namespace Metasound : : Frontend ;
2021-12-10 20:37:31 -05:00
2022-08-15 10:49:30 -04:00
bool bDidAlterObjectData = false ;
// Update the OutputFormat and NumChannels to match the audio format interface
// on the root document.
2023-03-07 17:01:52 -05:00
const FOutputAudioFormatInfoMap & FormatInfo = GetOutputAudioFormatInfo ( ) ;
for ( const FOutputAudioFormatInfoPair & Pair : FormatInfo )
2021-08-18 15:16:57 -04:00
{
2022-08-15 10:49:30 -04:00
if ( RootMetasoundDocument . Interfaces . Contains ( Pair . Value . InterfaceVersion ) )
2021-08-18 15:16:57 -04:00
{
2022-08-15 10:49:30 -04:00
if ( ( OutputFormat ! = Pair . Key ) | | ( NumChannels ! = Pair . Value . OutputVertexChannelOrder . Num ( ) ) )
{
OutputFormat = Pair . Key ;
NumChannels = Pair . Value . OutputVertexChannelOrder . Num ( ) ;
bDidAlterObjectData = true ;
}
break ;
2021-08-18 15:16:57 -04:00
}
}
2022-08-15 10:49:30 -04:00
return bDidAlterObjectData ;
2021-08-18 15:16:57 -04:00
}
2023-12-15 13:21:12 -05:00
FTopLevelAssetPath UMetaSoundSource : : GetAssetPathChecked ( ) const
{
return Metasound : : FMetaSoundEngineAssetHelper : : GetAssetPathChecked ( * this ) ;
}
2021-07-27 15:36:03 -04:00
void UMetaSoundSource : : BeginDestroy ( )
{
2023-12-19 15:55:04 -05:00
OnNotifyBeginDestroy ( ) ;
2021-07-27 15:36:03 -04:00
Super : : BeginDestroy ( ) ;
}
void UMetaSoundSource : : PreSave ( FObjectPreSaveContext InSaveContext )
{
Super : : PreSave ( InSaveContext ) ;
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : PreSaveAsset ( * this , InSaveContext ) ;
2021-07-27 15:36:03 -04:00
}
void UMetaSoundSource : : Serialize ( FArchive & InArchive )
{
Super : : Serialize ( InArchive ) ;
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : SerializeToArchive ( * this , InArchive ) ;
2021-07-27 15:36:03 -04:00
}
2022-10-13 17:38:11 -04:00
# if WITH_EDITOR
void UMetaSoundSource : : SetReferencedAssetClasses ( TSet < Metasound : : Frontend : : IMetaSoundAssetManager : : FAssetInfo > & & InAssetClasses )
2021-08-18 15:16:57 -04:00
{
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : SetReferencedAssetClasses ( * this , MoveTemp ( InAssetClasses ) ) ;
}
# endif // WITH_EDITOR
TArray < FMetasoundAssetBase * > UMetaSoundSource : : GetReferencedAssets ( )
{
return Metasound : : FMetaSoundEngineAssetHelper : : GetReferencedAssets ( * this ) ;
2021-08-18 15:16:57 -04:00
}
2022-10-13 17:38:11 -04:00
const TSet < FSoftObjectPath > & UMetaSoundSource : : GetAsyncReferencedAssetClassPaths ( ) const
2021-11-07 23:43:01 -05:00
{
return ReferenceAssetClassCache ;
}
2022-10-13 17:38:11 -04:00
void UMetaSoundSource : : OnAsyncReferencedAssetsLoaded ( const TArray < FMetasoundAssetBase * > & InAsyncReferences )
2021-08-18 15:16:57 -04:00
{
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : OnAsyncReferencedAssetsLoaded ( * this , InAsyncReferences ) ;
2021-08-18 15:16:57 -04:00
}
2021-06-08 10:52:31 -04:00
# if WITH_EDITORONLY_DATA
2022-10-13 17:38:11 -04:00
2021-06-08 10:52:31 -04:00
UEdGraph * UMetaSoundSource : : GetGraph ( )
{
return Graph ;
}
const UEdGraph * UMetaSoundSource : : GetGraph ( ) const
{
return Graph ;
}
UEdGraph & UMetaSoundSource : : GetGraphChecked ( )
{
check ( Graph ) ;
return * Graph ;
}
const UEdGraph & UMetaSoundSource : : GetGraphChecked ( ) const
{
check ( Graph ) ;
return * Graph ;
}
FText UMetaSoundSource : : GetDisplayName ( ) const
{
FString TypeName = UMetaSoundSource : : StaticClass ( ) - > GetName ( ) ;
return FMetasoundAssetBase : : GetDisplayName ( MoveTemp ( TypeName ) ) ;
}
2021-06-16 11:21:13 -04:00
2022-10-13 17:38:11 -04:00
void UMetaSoundSource : : SetRegistryAssetClassInfo ( const Metasound : : Frontend : : FNodeClassInfo & InNodeInfo )
{
Metasound : : FMetaSoundEngineAssetHelper : : SetMetaSoundRegistryAssetClassInfo ( * this , InNodeInfo ) ;
}
# endif // WITH_EDITORONLY_DATA
2022-02-25 15:48:47 -05:00
void UMetaSoundSource : : PostLoad ( )
{
Super : : PostLoad ( ) ;
2022-10-13 17:38:11 -04:00
Metasound : : FMetaSoundEngineAssetHelper : : PostLoad ( * this ) ;
2022-02-25 15:48:47 -05:00
Duration = GetDuration ( ) ;
bLooping = IsLooping ( ) ;
2023-09-21 18:46:14 -04:00
PostLoadQualitySettings ( ) ;
}
void UMetaSoundSource : : PostLoadQualitySettings ( )
{
# if WITH_EDITORONLY_DATA
// Ensure that our Quality settings resolve.
if ( UMetaSoundSettings * Settings = GetMutableDefault < UMetaSoundSettings > ( ) )
{
ResolveQualitySettings ( Settings ) ;
// Register for any changes to the settings while we're open in the editor.
Settings - > OnSettingChanged ( ) . AddWeakLambda ( this , [ WeakSource = MakeWeakObjectPtr ( this ) ] ( UObject * InObj , struct FPropertyChangedEvent & InEvent )
{
if (
WeakSource . IsValid ( ) & &
InEvent . GetMemberPropertyName ( ) = = GET_MEMBER_NAME_CHECKED ( UMetaSoundSettings , QualitySettings )
)
{
WeakSource - > ResolveQualitySettings ( CastChecked < UMetaSoundSettings > ( InObj ) ) ;
}
} ) ;
2023-10-10 13:37:20 -04:00
// Register for changes from the CVars that control overrides.
// We cache the OperatorSettings, so reset when these change.
auto ResetOperatorSettings = [ WeakSource = MakeWeakObjectPtr ( this ) ] ( IConsoleVariable * Var )
{
if ( WeakSource . IsValid ( ) )
{
WeakSource - > ResolveQualitySettings ( GetMutableDefault < UMetaSoundSettings > ( ) ) ;
WeakSource - > OperatorSettings . Reset ( ) ;
// Override SampleRate with the Operator settings version which uses our Quality settings.
2023-10-11 19:55:59 -04:00
WeakSource - > SampleRate = WeakSource - > GetOperatorSettings ( WeakSource - > CachedAudioDeviceSampleRate ) . GetSampleRate ( ) ;
2023-10-10 13:37:20 -04:00
}
} ;
Metasound : : Frontend : : GetBlockRateOverrideChangedDelegate ( ) . AddWeakLambda ( this , ResetOperatorSettings ) ;
Metasound : : Frontend : : GetSampleRateOverrideChangedDelegate ( ) . AddWeakLambda ( this , ResetOperatorSettings ) ;
2023-09-21 18:46:14 -04:00
}
# endif //WITH_EDITORONLY_DATA
}
void UMetaSoundSource : : ResolveQualitySettings ( const UMetaSoundSettings * Settings )
{
const FMetaSoundQualitySettings * Resolved = nullptr ;
// 1. Try and resolve by name. (most should resolve unless its been renamed, deleted).
auto FindByName = [ & Name = QualitySetting ] ( const FMetaSoundQualitySettings & Q ) - > bool { return Q . Name = = Name ; } ;
Resolved = Settings - > QualitySettings . FindByPredicate ( FindByName ) ;
# if WITH_EDITORONLY_DATA
// 2. If that failed, try by guid (if its been renamed in the settings, we can still find it).
if ( ! Resolved & & QualitySettingGuid . IsValid ( ) )
{
auto FindByGuid = [ & Guid = QualitySettingGuid ] ( const FMetaSoundQualitySettings & Q ) - > bool { return Q . UniqueId = = Guid ; } ;
Resolved = Settings - > QualitySettings . FindByPredicate ( FindByName ) ;
}
// 3. If still failed to resolve, use defaults and warn.
if ( ! Resolved )
{
2023-09-21 20:57:04 -04:00
// Disable the warning for now.
//UE_LOG(LogMetaSound, Warning, TEXT("Failed to resolve Quality '%s', resetting to the default."), *QualitySetting.ToString());
2023-09-21 18:46:14 -04:00
// Reset to defaults. (and make sure they are sane)
QualitySetting = GetDefault < UMetaSoundSource > ( ) - > QualitySetting ;
QualitySettingGuid = GetDefault < UMetaSoundSource > ( ) - > QualitySettingGuid ;
if ( ! Settings - > QualitySettings . FindByPredicate ( FindByName ) & & ! Settings - > QualitySettings . IsEmpty ( ) )
{
// Default doesn't point to anything, use first one in the list.
QualitySetting = Settings - > QualitySettings [ 0 ] . Name ;
QualitySettingGuid = Settings - > QualitySettings [ 0 ] . UniqueId ;
}
}
// Refresh the guid/name now we've resolved to correctly reflect.
if ( Resolved )
{
QualitySetting = Resolved - > Name ;
QualitySettingGuid = Resolved - > UniqueId ;
}
# endif //WITH_EDITORONLY_DATA
2022-02-25 15:48:47 -05:00
}
2022-09-28 22:19:31 -04:00
void UMetaSoundSource : : InitParameters ( TArray < FAudioParameter > & ParametersToInit , FName InFeatureName )
2021-08-30 14:08:45 -04:00
{
2023-10-04 17:59:57 -04:00
using namespace Metasound : : SourcePrivate ;
2022-03-23 15:56:20 -04:00
METASOUND_LLM_SCOPE ;
2023-08-10 20:04:23 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( UMetaSoundSource : : InitParameters ) ;
2022-03-23 15:56:20 -04:00
2023-11-02 18:41:29 -04:00
2023-10-13 17:49:56 -04:00
if ( bIsBuilderActive )
2023-10-03 12:17:13 -04:00
{
2023-11-02 18:41:29 -04:00
// Do not create UObject proxies in the runtime input map because they proxies
// stored there will not be used. The necessary proxies in the ParametersToInit
// will be created and used instead.
constexpr bool bCreateUObjectProxiesInRuntimeInputMap = false ;
InitParametersInternal ( CreateRuntimeInputMap ( bCreateUObjectProxiesInRuntimeInputMap ) , ParametersToInit , InFeatureName ) ;
2023-10-03 12:17:13 -04:00
}
else
{
const bool bIsRuntimeInputDataValid = RuntimeInputData . bIsValid . load ( ) ;
if ( bIsRuntimeInputDataValid )
{
InitParametersInternal ( RuntimeInputData . InputMap , ParametersToInit , InFeatureName ) ;
}
else
{
// The runtime input data should have been cached, but is not so we use
// a fallback method. If this is occurring, then callers need to ensure
// that InitResources has been called before this method executes or else
// suffer the consequences of incurring significant performance losses
// each time a parameter is set on the MetaSound.
2023-10-04 17:59:57 -04:00
UE_CLOG ( HasNotBeenLoggedForThisObject ( * this , __LINE__ ) , LogMetaSound , Warning , TEXT ( " Initializing parameters on uninitialized UMetaSoundSource %s will result in slower performance. UMetaSoundSource::InitResources should finish executing on the game thread before attempting to call UMetaSoundSource::InitParameters(...) " ) , * GetOwningAssetName ( ) ) ;
2023-11-02 18:41:29 -04:00
// Do not create UObject proxies in the runtime input map because they proxies
// stored there will not be used. The necessary proxies in the ParametersToInit
// will be created and used instead.
constexpr bool bCreateUObjectProxiesInRuntimeInputMap = false ;
InitParametersInternal ( CreateRuntimeInputMap ( bCreateUObjectProxiesInRuntimeInputMap ) , ParametersToInit , InFeatureName ) ;
2023-10-03 12:17:13 -04:00
}
}
}
void UMetaSoundSource : : InitResources ( )
{
using namespace Metasound : : Frontend ;
using namespace Metasound : : SourcePrivate ;
METASOUND_LLM_SCOPE ;
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( UMetaSoundSource : : InitResources ) ;
2023-10-09 14:40:31 -04:00
ensureMsgf ( ! IsRunningCookCommandlet ( ) , TEXT ( " UMetaSoundSource::InitResources should not be called during cook. " ) ) ;
2023-10-03 12:17:13 -04:00
if ( IsInGameThread ( ) )
2023-09-22 15:01:07 -04:00
{
RegisterGraphWithFrontend ( GetInitRegistrationOptions ( ) ) ;
2023-10-03 12:17:13 -04:00
}
else
{
const bool bIsInGCSafeThread = IsInAudioThread ( ) | | IsInAsyncLoadingThread ( ) ; // Audio Thread is safe from GC, so we can safely construct the TWeakObjectPtr<> to this.
if ( ! bIsInGCSafeThread )
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Attempt to call UMetaSoundSource::InitResources() on %s from thread which may not provide garbage collection safety of the UMetaSoundSource " ) , * GetOwningAssetName ( ) ) ;
}
ExecuteOnGameThread (
UE_SOURCE_LOCATION ,
[ MetaSoundSourcePtr = TWeakObjectPtr < UMetaSoundSource > ( this ) ] ( )
{
if ( UMetaSoundSource * Source = MetaSoundSourcePtr . Get ( ) )
{
Source - > InitResources ( ) ;
}
}
) ;
}
}
void UMetaSoundSource : : RegisterGraphWithFrontend ( Metasound : : Frontend : : FMetaSoundAssetRegistrationOptions InRegistrationOptions )
{
check ( IsInGameThread ( ) ) ;
2023-10-05 13:40:45 -04:00
FMetasoundAssetBase : : RegisterGraphWithFrontend ( InRegistrationOptions ) ;
2023-10-03 12:17:13 -04:00
const bool bIsRuntimeInputDataValid = RuntimeInputData . bIsValid . load ( ) ;
2023-11-08 16:09:39 -05:00
// Runtime data does not need to and should not be created at cook
if ( ! bIsRuntimeInputDataValid & & ! IsRunningCookCommandlet ( ) )
2023-10-03 12:17:13 -04:00
{
CacheRuntimeInputData ( ) ;
}
}
bool UMetaSoundSource : : IsPlayable ( ) const
{
return true ;
}
float UMetaSoundSource : : GetDuration ( ) const
{
// This is an unfortunate function required by logic in determining what sounds can be potentially
// culled (in this case prematurally). MetaSound OneShots are stopped either by internally logic that
// triggers OnFinished, or if an external system requests the sound to be stopped. Setting the duration
// as a "close to" maximum length without being considered looping avoids the MetaSound from being
// culled inappropriately.
return IsOneShot ( ) ? INDEFINITELY_LOOPING_DURATION - 1.0f : INDEFINITELY_LOOPING_DURATION ;
}
Metasound : : Frontend : : FDocumentAccessPtr UMetaSoundSource : : GetDocumentAccessPtr ( )
{
using namespace Metasound : : Frontend ;
// Mutation of a document via the soft deprecated access ptr/controller system is not tracked by
// the builder registry, so the document cache is invalidated here. It is discouraged to mutate
// documents using both systems at the same time as it can corrupt a builder document's cache.
if ( UMetaSoundBuilderSubsystem * BuilderSubsystem = UMetaSoundBuilderSubsystem : : Get ( ) )
{
const FMetasoundFrontendClassName & Name = RootMetasoundDocument . RootGraph . Metadata . GetClassName ( ) ;
BuilderSubsystem - > InvalidateDocumentCache ( Name ) ;
2023-09-22 15:01:07 -04:00
}
2023-10-03 12:17:13 -04:00
// Return document using FAccessPoint to inform the TAccessPtr when the
// object is no longer valid.
return MakeAccessPtr < FDocumentAccessPtr > ( RootMetasoundDocument . AccessPoint , RootMetasoundDocument ) ;
}
Metasound : : Frontend : : FConstDocumentAccessPtr UMetaSoundSource : : GetDocumentConstAccessPtr ( ) const
{
using namespace Metasound : : Frontend ;
// Return document using FAccessPoint to inform the TAccessPtr when the
// object is no longer valid.
return MakeAccessPtr < FConstDocumentAccessPtr > ( RootMetasoundDocument . AccessPoint , RootMetasoundDocument ) ;
}
bool UMetaSoundSource : : ImplementsParameterInterface ( Audio : : FParameterInterfacePtr InInterface ) const
{
const FMetasoundFrontendVersion Version { InInterface - > GetName ( ) , { InInterface - > GetVersion ( ) . Major , InInterface - > GetVersion ( ) . Minor } } ;
return GetDocumentChecked ( ) . Interfaces . Contains ( Version ) ;
}
ISoundGeneratorPtr UMetaSoundSource : : CreateSoundGenerator ( const FSoundGeneratorInitParams & InParams , TArray < FAudioParameter > & & InDefaultParameters )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
using namespace Metasound : : Engine ;
using namespace Metasound : : SourcePrivate ;
METASOUND_LLM_SCOPE ;
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( UMetaSoundSource : : CreateSoundGenerator ) ;
FOperatorSettings InSettings = GetOperatorSettings ( static_cast < FSampleRate > ( InParams . SampleRate ) ) ;
SampleRate = InSettings . GetSampleRate ( ) ;
FMetasoundEnvironment Environment = CreateEnvironment ( InParams ) ;
FParameterRouter & Router = UMetaSoundSource : : GetParameterRouter ( ) ;
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > DataChannel = Router . FindOrCreateDataChannelForReader ( InParams . AudioDeviceID , InParams . InstanceID ) ;
FOperatorBuilderSettings BuilderSettings = FOperatorBuilderSettings : : GetDefaultSettings ( ) ;
// Graph analyzer currently only enabled for preview sounds (but can theoretically be supported for all sounds)
BuilderSettings . bPopulateInternalDataReferences = InParams . bIsPreviewSound ;
constexpr bool bBuildSynchronous = false ;
const bool bIsDynamic = DynamicTransactor . IsValid ( ) ;
TSharedPtr < FMetasoundGenerator > Generator ;
if ( bIsDynamic )
{
// In order to ensure synchronization and avoid race conditions the current state
// of the graph is copied and transform queue created here. This ensures that:
//
// 1. Modifications to the underlying FGraph in the FDynamicOperatorTransactor can continue
// while the generator is being constructed on an async task. If this were not ensured,
// a race condition would be introduced wherein the FGraph could be manipulated while the
// graph is being read while building the generator.
//
// 2. The state of the FGraph and TransformQueue are synchronized so that any additional
// changes applied to the FDynamicOperatorTransactor will be placed in the TransformQueue.
// The dynamic operator & generator will then consume these transforms after it has finished
// being built.
BuilderSettings . bEnableOperatorRebind = true ;
FMetasoundDynamicGraphGeneratorInitParams InitParams
{
{
InSettings ,
MoveTemp ( BuilderSettings ) ,
MakeShared < FGraph > ( DynamicTransactor - > GetGraph ( ) ) , // Make a copy of the graph.
Environment ,
GetName ( ) ,
GetOutputAudioChannelOrder ( ) ,
MoveTemp ( InDefaultParameters ) ,
bBuildSynchronous ,
DataChannel
} ,
DynamicTransactor - > CreateTransformQueue ( InSettings , Environment ) // Create transaction queue
} ;
TSharedPtr < FMetasoundDynamicGraphGenerator > DynamicGenerator = MakeShared < FMetasoundDynamicGraphGenerator > ( InSettings ) ;
DynamicGenerator - > Init ( MoveTemp ( InitParams ) ) ;
Generator = MoveTemp ( DynamicGenerator ) ;
}
2023-11-01 12:17:48 -04:00
else
2023-10-03 12:17:13 -04:00
{
2023-11-01 12:17:48 -04:00
// By default, the sound generator for a metasound preset uses a graph specifically
// associated with the UMetaSoundSource_Preset. The overridden defaults for that
// preset are baked into the IGraph. Unfortunately, this makes the MetaSound
// operator pool less efficient because it associates the operator with the IGraph.
// The way the presets use the IGraph mean that there is less sharing of cached
// operators.
//
// To improve the efficiency of the operator pool, we have presets use their
// base IGraphs so that more MetaSounds utilize the same IGraph. This requires
// us to retrieve that specific graph. We also supply the parameters that were overridden
// in the preset to the FMetaSoundGenerator, because they are not backed into
// the base IGraph.
2023-11-03 19:38:58 -04:00
if ( ConsoleVariables : : bEnableExperimentalRuntimePresetGraphInflation & & bIsPresetGraphInflationSupported )
2023-10-03 12:17:13 -04:00
{
2023-12-15 13:21:12 -05:00
// Get the graph associated with base graph which this preset wraps.
2023-11-01 12:17:48 -04:00
TSharedPtr < const IGraph > MetasoundGraph = TryGetMetaSoundPresetBaseGraph ( ) ;
if ( MetasoundGraph . IsValid ( ) )
{
// Combine the supplied parameters with the overridden parameters set on the preset.
TArray < FAudioParameter > MergedParameters ;
MergePresetOverridesAndSuppliedDefaults ( InDefaultParameters , MergedParameters ) ;
// Create generator.
FMetasoundGeneratorInitParams InitParams
{
InSettings ,
MoveTemp ( BuilderSettings ) ,
MetasoundGraph ,
Environment ,
GetName ( ) ,
GetOutputAudioChannelOrder ( ) ,
MoveTemp ( MergedParameters ) ,
bBuildSynchronous ,
DataChannel
} ;
Generator = MakeShared < FMetasoundConstGraphGenerator > ( MoveTemp ( InitParams ) ) ;
}
2023-10-03 12:17:13 -04:00
}
2023-11-01 12:17:48 -04:00
if ( ! Generator . IsValid ( ) )
2023-10-03 12:17:13 -04:00
{
2023-12-15 13:21:12 -05:00
TSharedPtr < const FGraph > MetasoundGraph = FMetasoundFrontendRegistryContainer : : Get ( ) - > GetGraph ( GetGraphRegistryKey ( ) ) ;
2023-11-01 12:17:48 -04:00
if ( ! MetasoundGraph . IsValid ( ) )
{
return ISoundGeneratorPtr ( nullptr ) ;
}
2023-10-03 12:17:13 -04:00
2023-11-01 12:17:48 -04:00
FMetasoundGeneratorInitParams InitParams
{
InSettings ,
MoveTemp ( BuilderSettings ) ,
MetasoundGraph ,
Environment ,
GetName ( ) ,
GetOutputAudioChannelOrder ( ) ,
MoveTemp ( InDefaultParameters ) ,
bBuildSynchronous ,
DataChannel
} ;
Generator = MakeShared < FMetasoundConstGraphGenerator > ( MoveTemp ( InitParams ) ) ;
}
2023-10-03 12:17:13 -04:00
}
if ( Generator . IsValid ( ) )
{
TrackGenerator ( InParams . AudioComponentId , Generator ) ;
}
return ISoundGeneratorPtr ( Generator ) ;
}
void UMetaSoundSource : : OnEndGenerate ( ISoundGeneratorPtr Generator )
{
using namespace Metasound ;
ForgetGenerator ( Generator ) ;
}
bool UMetaSoundSource : : GetAllDefaultParameters ( TArray < FAudioParameter > & OutParameters ) const
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
using namespace Metasound : : Engine ;
if ( ! RuntimeInputData . bIsValid . load ( ) )
{
2023-10-04 17:59:57 -04:00
UE_CLOG ( SourcePrivate : : HasNotBeenLoggedForThisObject ( * this , __LINE__ ) , LogMetaSound , Warning , TEXT ( " Default parameters will be ommitted. Accessing invalid runtime data on MetaSound %s. Ensure that UMetaSoundSource::InitResources() is executed on the game thread before calling UMetaSoundSource::GetAllDefaultParameters(...) " ) , * GetOwningAssetName ( ) ) ;
2023-10-03 12:17:13 -04:00
return false ;
}
for ( const TPair < FVertexName , FRuntimeInput > & Pair : RuntimeInputData . InputMap )
{
2023-11-02 18:41:29 -04:00
OutParameters . Add ( Pair . Value . DefaultParameter ) ;
2023-10-03 12:17:13 -04:00
}
return true ;
}
void UMetaSoundSource : : InitParametersInternal ( const Metasound : : TSortedVertexNameMap < FRuntimeInput > & InInputMap , TArray < FAudioParameter > & ParametersToInit , FName InFeatureName ) const
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
METASOUND_LLM_SCOPE ;
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( UMetaSoundSource : : InitParametersInternal ) ;
checkf ( IsInGameThread ( ) | | IsInAudioThread ( ) , TEXT ( " Parameter initialization must happen on the GameThread or AudioThread to allow for safe creation of UObject proxies " ) ) ;
2023-09-14 13:49:21 -04:00
IDataTypeRegistry & DataTypeRegistry = IDataTypeRegistry : : Get ( ) ;
// Removes values that are not explicitly defined by the ParamType
2023-10-03 12:17:13 -04:00
auto Sanitize = [ ] ( FAudioParameter & Parameter )
2022-08-18 13:55:13 -04:00
{
2021-08-30 14:08:45 -04:00
switch ( Parameter . ParamType )
{
2023-01-17 18:39:44 -05:00
case EAudioParameterType : : Trigger :
{
Parameter = FAudioParameter ( Parameter . ParamName , EAudioParameterType : : Trigger ) ;
}
break ;
2021-08-30 14:08:45 -04:00
case EAudioParameterType : : Boolean :
{
Parameter = FAudioParameter ( Parameter . ParamName , Parameter . BoolParam ) ;
}
break ;
case EAudioParameterType : : BooleanArray :
{
TArray < bool > TempArray = Parameter . ArrayBoolParam ;
Parameter = FAudioParameter ( Parameter . ParamName , MoveTemp ( TempArray ) ) ;
}
break ;
case EAudioParameterType : : Float :
{
Parameter = FAudioParameter ( Parameter . ParamName , Parameter . FloatParam ) ;
}
break ;
case EAudioParameterType : : FloatArray :
{
TArray < float > TempArray = Parameter . ArrayFloatParam ;
Parameter = FAudioParameter ( Parameter . ParamName , MoveTemp ( TempArray ) ) ;
}
break ;
case EAudioParameterType : : Integer :
{
Parameter = FAudioParameter ( Parameter . ParamName , Parameter . IntParam ) ;
}
break ;
case EAudioParameterType : : IntegerArray :
{
TArray < int32 > TempArray = Parameter . ArrayIntParam ;
Parameter = FAudioParameter ( Parameter . ParamName , MoveTemp ( TempArray ) ) ;
}
break ;
case EAudioParameterType : : Object :
{
Parameter = FAudioParameter ( Parameter . ParamName , Parameter . ObjectParam ) ;
}
break ;
case EAudioParameterType : : ObjectArray :
{
TArray < UObject * > TempArray = Parameter . ArrayObjectParam ;
Parameter = FAudioParameter ( Parameter . ParamName , MoveTemp ( TempArray ) ) ;
}
break ;
case EAudioParameterType : : String :
{
Parameter = FAudioParameter ( Parameter . ParamName , Parameter . StringParam ) ;
}
break ;
case EAudioParameterType : : StringArray :
{
TArray < FString > TempArray = Parameter . ArrayStringParam ;
Parameter = FAudioParameter ( Parameter . ParamName , MoveTemp ( TempArray ) ) ;
}
break ;
case EAudioParameterType : : None :
default :
break ;
}
} ;
2022-09-28 22:19:31 -04:00
for ( int32 i = ParametersToInit . Num ( ) - 1 ; i > = 0 ; - - i )
2021-08-30 14:08:45 -04:00
{
2023-09-14 13:49:21 -04:00
bool bIsParameterValid = false ;
2022-09-28 22:19:31 -04:00
FAudioParameter & Parameter = ParametersToInit [ i ] ;
2023-10-03 12:17:13 -04:00
if ( const FRuntimeInput * Input = InInputMap . Find ( Parameter . ParamName ) )
2021-11-18 14:37:34 -05:00
{
2023-10-03 12:17:13 -04:00
if ( IsParameterValidInternal ( Parameter , Input - > TypeName , DataTypeRegistry ) )
2022-02-25 09:59:46 -05:00
{
2023-09-14 13:49:21 -04:00
Sanitize ( Parameter ) ;
2023-11-02 18:41:29 -04:00
constexpr bool bClearUObjectPointers = true ; // protect against leaking UObject ptrs to the audio thread
SourcePrivate : : CreateUObjectProxies ( DataTypeRegistry , Input - > TypeName , bClearUObjectPointers , Parameter ) ;
2023-09-14 13:49:21 -04:00
bIsParameterValid = true ;
2022-03-10 22:07:08 -05:00
}
2021-11-18 14:37:34 -05:00
}
2023-09-14 13:49:21 -04:00
if ( ! bIsParameterValid )
2021-11-18 14:37:34 -05:00
{
2022-02-25 09:59:46 -05:00
constexpr bool bAllowShrinking = false ;
2022-09-28 22:19:31 -04:00
ParametersToInit . RemoveAtSwap ( i , 1 , bAllowShrinking ) ;
2022-09-21 15:42:38 -04:00
2022-03-04 04:08:58 -05:00
# if !NO_LOGGING
2022-09-21 15:42:38 -04:00
if ( : : Metasound : : MetaSoundParameterEnableWarningOnIgnoredParameterCVar )
{
2023-09-14 13:49:21 -04:00
const FString AssetName = GetName ( ) ;
2022-09-21 15:42:38 -04:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to set parameter '%s' in asset '%s': No name specified, no transmittable input found, or type mismatch. " ) , * Parameter . ParamName . ToString ( ) , * AssetName ) ;
}
2022-03-04 04:08:58 -05:00
# endif // !NO_LOGGING
2021-11-18 14:37:34 -05:00
}
2021-08-30 14:08:45 -04:00
}
}
bool UMetaSoundSource : : IsParameterValid ( const FAudioParameter & InParameter ) const
2021-11-18 14:37:34 -05:00
{
2023-07-20 14:02:15 -04:00
const TArray < FMetasoundFrontendClassInput > & Inputs = GetDocumentChecked ( ) . RootGraph . Interface . Inputs ;
const FMetasoundFrontendVertex * Vertex = Algo : : FindByPredicate ( Inputs , [ & InParameter ] ( const FMetasoundFrontendClassInput & Input )
2021-11-18 14:37:34 -05:00
{
2023-07-20 14:02:15 -04:00
return Input . Name = = InParameter . ParamName ;
2021-11-18 14:37:34 -05:00
} ) ;
2023-09-14 13:49:21 -04:00
if ( Vertex )
{
2023-10-03 12:17:13 -04:00
return IsParameterValidInternal ( InParameter , Vertex - > TypeName , Metasound : : Frontend : : IDataTypeRegistry : : Get ( ) ) ;
2023-09-14 13:49:21 -04:00
}
else
{
return false ;
}
2021-11-18 14:37:34 -05:00
}
2023-10-03 12:17:13 -04:00
bool UMetaSoundSource : : IsParameterValidInternal ( const FAudioParameter & InParameter , const FName & InTypeName , Metasound : : Frontend : : IDataTypeRegistry & InDataTypeRegistry ) const
2021-01-22 03:05:22 -04:00
{
2021-08-30 14:08:45 -04:00
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2022-03-10 22:07:08 -05:00
if ( InParameter . ParamName . IsNone ( ) )
{
2023-09-14 13:49:21 -04:00
// Invalid parameter name
2022-03-10 22:07:08 -05:00
return false ;
}
2023-09-14 13:49:21 -04:00
if ( ! InParameter . TypeName . IsNone ( ) & & InParameter . TypeName ! = InTypeName )
2023-07-20 14:02:15 -04:00
{
2023-09-14 13:49:21 -04:00
// Mismatched parameter type and vertex data type
2023-07-20 14:02:15 -04:00
return false ;
}
2023-09-14 13:49:21 -04:00
// Special handling for UObject proxies
if ( InParameter . ParamType = = EAudioParameterType : : Object )
2021-08-30 14:08:45 -04:00
{
2023-09-14 13:49:21 -04:00
return InDataTypeRegistry . IsValidUObjectForDataType ( InTypeName , InParameter . ObjectParam ) ;
}
else if ( InParameter . ParamType = = EAudioParameterType : : ObjectArray )
{
bool bIsValid = true ;
const FName ElementTypeName = CreateElementTypeNameFromArrayTypeName ( InTypeName ) ;
for ( const UObject * Object : InParameter . ArrayObjectParam )
{
bIsValid = InDataTypeRegistry . IsValidUObjectForDataType ( ElementTypeName , Object ) ;
if ( ! bIsValid )
{
break ;
}
}
return bIsValid ;
}
const IDataTypeRegistryEntry * RegistryEntry = InDataTypeRegistry . FindDataTypeRegistryEntry ( InTypeName ) ;
if ( ! RegistryEntry )
{
// Unregistered MetaSound data type
2021-11-18 14:37:34 -05:00
return false ;
}
2021-08-30 14:08:45 -04:00
2021-11-18 14:37:34 -05:00
switch ( InParameter . ParamType )
{
2023-01-17 18:39:44 -05:00
case EAudioParameterType : : Trigger :
2021-11-18 14:37:34 -05:00
case EAudioParameterType : : Boolean :
2021-08-30 14:08:45 -04:00
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsBoolParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : BooleanArray :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsBoolArrayParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : Float :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsFloatParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : FloatArray :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsFloatArrayParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : Integer :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsIntParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : IntegerArray :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsIntArrayParsable ;
2021-08-30 14:08:45 -04:00
}
2021-11-18 14:37:34 -05:00
break ;
case EAudioParameterType : : String :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsStringParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : StringArray :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsStringArrayParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
case EAudioParameterType : : NoneArray :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsDefaultArrayParsable ;
2021-11-18 14:37:34 -05:00
}
2023-09-14 13:49:21 -04:00
2021-11-18 14:37:34 -05:00
case EAudioParameterType : : None :
{
2023-09-14 13:49:21 -04:00
return RegistryEntry - > GetDataTypeInfo ( ) . bIsDefaultParsable ;
2021-11-18 14:37:34 -05:00
}
break ;
2023-09-14 13:49:21 -04:00
default :
{
// All parameter types should be covered.
static_assert ( static_cast < uint8 > ( EAudioParameterType : : COUNT ) = = 13 , " Possible unhandled EAudioParameterType " ) ;
checkNoEntry ( ) ;
// Unhandled parameter type
return false ;
}
2021-11-18 14:37:34 -05:00
}
2021-08-30 14:08:45 -04:00
}
2022-01-18 18:05:56 -05:00
bool UMetaSoundSource : : IsLooping ( ) const
2022-01-13 17:34:30 -05:00
{
return ! IsOneShot ( ) ;
}
2022-01-18 18:05:56 -05:00
bool UMetaSoundSource : : IsOneShot ( ) const
2021-12-15 23:11:10 -05:00
{
using namespace Metasound : : Frontend ;
// If the metasound source implements the one-shot interface, then it's a one-shot metasound
return IsInterfaceDeclared ( SourceOneShotInterface : : GetVersion ( ) ) ;
}
2022-09-28 22:19:31 -04:00
TSharedPtr < Audio : : IParameterTransmitter > UMetaSoundSource : : CreateParameterTransmitter ( Audio : : FParameterTransmitterInitParams & & InParams ) const
2021-08-30 14:08:45 -04:00
{
2023-04-05 17:38:47 -04:00
using namespace Metasound ;
using namespace Metasound : : SourcePrivate ;
2022-03-23 15:56:20 -04:00
METASOUND_LLM_SCOPE ;
2023-10-03 12:17:13 -04:00
auto CreateParameterTransmitterInternal = [ this ] ( const TSortedVertexNameMap < FRuntimeInput > & InInputMap , Audio : : FParameterTransmitterInitParams & InParams )
2023-09-22 15:01:07 -04:00
{
2023-10-03 12:17:13 -04:00
// Build list of parameters that can be set at runtime.
TArray < FName > ValidParameters ;
for ( const TPair < FVertexName , FRuntimeInput > & Pair : InInputMap )
2023-09-14 13:49:21 -04:00
{
2023-10-03 12:17:13 -04:00
if ( Pair . Value . bIsTransmittable & & ( Pair . Value . AccessType = = EMetasoundFrontendVertexAccessType : : Reference ) )
{
ValidParameters . Add ( Pair . Value . Name ) ;
}
2023-09-14 13:49:21 -04:00
}
2023-10-03 12:17:13 -04:00
FParameterRouter & Router = UMetaSoundSource : : GetParameterRouter ( ) ;
TSharedPtr < TSpscQueue < FMetaSoundParameterTransmitter : : FParameter > > DataChannel = Router . FindOrCreateDataChannelForWriter ( InParams . AudioDeviceID , InParams . InstanceID ) ;
Metasound : : FMetaSoundParameterTransmitter : : FInitParams InitParams
(
GetOperatorSettings ( InParams . SampleRate ) ,
InParams . InstanceID ,
MoveTemp ( InParams . DefaultParams ) ,
MoveTemp ( ValidParameters ) ,
DataChannel
) ;
InitParams . DebugMetaSoundName = this - > GetFName ( ) ;
return MakeShared < Metasound : : FMetaSoundParameterTransmitter > ( MoveTemp ( InitParams ) ) ;
} ;
const bool bIsRuntimeInputDataValid = RuntimeInputData . bIsValid . load ( ) ;
const bool bCreateInputMapOnTheFly = bIsBuilderActive | | ! bIsRuntimeInputDataValid ;
if ( bCreateInputMapOnTheFly )
{
if ( ! bIsBuilderActive )
{
// If we're not using a builder, that means the metasound cannot change and that the runtime input data should have been cached.
UE_LOG ( LogMetaSound , Warning , TEXT ( " Creating a Parameter Transmiiter on uninitialized UMetaSoundSource %s will result in slower performance. UMetaSoundSource::InitResources should finish executing on the game thread before attempting to call UMetaSoundSource::CreateParameterTransmitter(...) " ) , * GetOwningAssetName ( ) ) ;
}
2023-11-02 18:41:29 -04:00
// Do not create UObject proxies in the runtime input map because they proxies
// stored there will not be used. The necessary proxies in the ParametersToInit
// will be created and used instead.
constexpr bool bCreateUObjectProxiesInRuntimeInputMap = false ;
return CreateParameterTransmitterInternal ( CreateRuntimeInputMap ( bCreateUObjectProxiesInRuntimeInputMap ) , InParams ) ;
2023-10-03 12:17:13 -04:00
}
else
{
return CreateParameterTransmitterInternal ( RuntimeInputData . InputMap , InParams ) ;
2023-09-14 13:49:21 -04:00
}
2021-01-22 03:05:22 -04:00
}
2023-10-11 19:55:59 -04:00
Metasound : : FOperatorSettings UMetaSoundSource : : GetOperatorSettings ( Metasound : : FSampleRate InDeviceSampleRate ) const
2023-09-21 18:46:14 -04:00
{
2023-10-11 19:55:59 -04:00
// We should recache the operator settings if the device rate has changed.
if ( InDeviceSampleRate ! = CachedAudioDeviceSampleRate )
{
OperatorSettings . Reset ( ) ;
}
2023-09-21 18:46:14 -04:00
if ( ! OperatorSettings )
2023-09-19 18:30:29 -04:00
{
2023-09-21 18:46:14 -04:00
using namespace Metasound ;
using namespace Metasound : : SourcePrivate ;
2023-10-10 13:37:20 -04:00
2023-09-21 18:46:14 -04:00
// Lazy Query and cache on the optional.
auto QueryQualitySettings = [ & ] ( Metasound : : FSampleRate InSampleRate ) - > Metasound : : FOperatorSettings
{
static const float DefaultBlockRateConstant = 100.f ;
2023-10-11 19:55:59 -04:00
static const float DefaultSampleRateConstant = 48000.f ;
2023-09-21 18:46:14 -04:00
2023-10-11 19:55:59 -04:00
// 1. Sensible defaults. (If Device SampleRate is sensible use that as default).
FSampleRate MetasoundSampleRate = InSampleRate > 0 ? InDeviceSampleRate : DefaultSampleRateConstant ;
float MetasoundBlockRate = DefaultBlockRateConstant ;
2023-09-21 18:46:14 -04:00
2023-10-10 13:37:20 -04:00
// 2. Query our quality settings.
2023-10-06 19:42:56 -04:00
if ( const UMetaSoundSettings * Settings = GetDefault < UMetaSoundSettings > ( ) )
{
if ( const FMetaSoundQualitySettings * Found = Settings - > QualitySettings . FindByPredicate ( [ & QT = QualitySetting ] ( const FMetaSoundQualitySettings & Q ) - > bool { return Q . Name = = QT ; } ) )
{
// Allow partial applications of settings, if some are non-zero.
2023-10-10 13:37:20 -04:00
if ( const float Value = Found - > BlockRate . GetValue ( ) ; Value > 0.f )
2023-10-06 19:42:56 -04:00
{
2023-10-11 19:55:59 -04:00
MetasoundBlockRate = Value ;
2023-10-06 19:42:56 -04:00
}
2023-10-10 13:37:20 -04:00
if ( const float Value = Found - > SampleRate . GetValue ( ) ; Value > 0.f )
2023-10-06 19:42:56 -04:00
{
2023-10-11 19:55:59 -04:00
MetasoundSampleRate = Value ;
2023-10-06 19:42:56 -04:00
}
}
}
2023-10-10 13:37:20 -04:00
// 3. Do per asset overrides.
2023-10-06 19:42:56 -04:00
if ( const float SerializedBlockRate = BlockRateOverride . GetValue ( ) ; SerializedBlockRate > 0.0f )
{
2023-10-11 19:55:59 -04:00
MetasoundBlockRate = SerializedBlockRate ;
2023-10-06 19:42:56 -04:00
}
if ( const int32 SerializedSampleRate = SampleRateOverride . GetValue ( ) ; SerializedSampleRate > 0 )
{
2023-10-11 19:55:59 -04:00
MetasoundSampleRate = SerializedSampleRate ;
2023-10-06 19:42:56 -04:00
}
2023-10-06 18:51:06 -04:00
2023-10-10 13:37:20 -04:00
// 4. Query CVars. (Override with CVars if they are > 0)
using namespace Metasound : : Frontend ;
const float BlockRateCVar = GetBlockRateOverride ( ) ;
const int32 SampleRateCvar = GetSampleRateOverride ( ) ;
if ( SampleRateCvar > 0 )
{
2023-10-11 19:55:59 -04:00
MetasoundSampleRate = SampleRateCvar ;
2023-10-10 13:37:20 -04:00
}
if ( BlockRateCVar > 0 )
{
2023-10-11 19:55:59 -04:00
MetasoundBlockRate = BlockRateCVar ;
2023-10-10 13:37:20 -04:00
}
// 5. Sanity clamps.
2023-10-11 19:55:59 -04:00
const TRange < float > BlockRange = GetBlockRateClampRange ( ) ;
const TRange < int32 > RateRange = GetSampleRateClampRange ( ) ;
MetasoundBlockRate = FMath : : Clamp ( MetasoundBlockRate , BlockRange . GetLowerBoundValue ( ) , BlockRange . GetUpperBoundValue ( ) ) ;
MetasoundSampleRate = FMath : : Clamp ( MetasoundSampleRate , RateRange . GetLowerBoundValue ( ) , RateRange . GetUpperBoundValue ( ) ) ;
2023-10-10 13:37:20 -04:00
2023-10-11 19:55:59 -04:00
return Metasound : : FOperatorSettings ( MetasoundSampleRate , MetasoundBlockRate ) ;
2023-09-21 18:46:14 -04:00
} ;
2023-10-11 19:55:59 -04:00
OperatorSettings = QueryQualitySettings ( InDeviceSampleRate ) ;
2023-09-19 18:30:29 -04:00
}
2023-09-21 18:46:14 -04:00
return * OperatorSettings ;
2021-01-22 03:05:22 -04:00
}
2021-07-28 17:12:57 -04:00
Metasound : : FMetasoundEnvironment UMetaSoundSource : : CreateEnvironment ( ) const
{
using namespace Metasound ;
2021-12-10 20:37:31 -05:00
using namespace Metasound : : Frontend ;
2021-07-28 17:12:57 -04:00
2022-02-07 18:00:45 -05:00
FMetasoundEnvironment Environment ;
2021-12-10 20:37:31 -05:00
Environment . SetValue < uint32 > ( SourceInterface : : Environment : : SoundUniqueID , GetUniqueID ( ) ) ;
2021-07-28 17:12:57 -04:00
return Environment ;
}
Metasound : : FMetasoundEnvironment UMetaSoundSource : : CreateEnvironment ( const FSoundGeneratorInitParams & InParams ) const
{
using namespace Metasound ;
using namespace Metasound : : Engine ;
2021-12-10 20:37:31 -05:00
using namespace Metasound : : Frontend ;
2021-07-28 17:12:57 -04:00
FMetasoundEnvironment Environment = CreateEnvironment ( ) ;
2021-12-10 20:37:31 -05:00
Environment . SetValue < bool > ( SourceInterface : : Environment : : IsPreview , InParams . bIsPreviewSound ) ;
Environment . SetValue < uint64 > ( SourceInterface : : Environment : : TransmitterID , InParams . InstanceID ) ;
2022-02-07 18:00:45 -05:00
Environment . SetValue < Audio : : FDeviceId > ( SourceInterface : : Environment : : DeviceID , InParams . AudioDeviceID ) ;
2022-06-29 15:04:47 -04:00
Environment . SetValue < int32 > ( SourceInterface : : Environment : : AudioMixerNumOutputFrames , InParams . AudioMixerNumOutputFrames ) ;
2021-12-10 20:37:31 -05:00
2021-08-09 15:13:40 -04:00
# if WITH_METASOUND_DEBUG_ENVIRONMENT
2021-12-10 20:37:31 -05:00
Environment . SetValue < FString > ( SourceInterface : : Environment : : GraphName , GetFullName ( ) ) ;
2021-08-09 15:13:40 -04:00
# endif // WITH_METASOUND_DEBUG_ENVIRONMENT
2021-07-28 17:12:57 -04:00
return Environment ;
}
2021-08-30 14:08:45 -04:00
Metasound : : FMetasoundEnvironment UMetaSoundSource : : CreateEnvironment ( const Audio : : FParameterTransmitterInitParams & InParams ) const
2021-07-28 17:12:57 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Engine ;
2021-12-10 20:37:31 -05:00
using namespace Metasound : : Frontend ;
2021-07-28 17:12:57 -04:00
FMetasoundEnvironment Environment = CreateEnvironment ( ) ;
2021-12-10 20:37:31 -05:00
Environment . SetValue < uint64 > ( SourceInterface : : Environment : : TransmitterID , InParams . InstanceID ) ;
2021-07-28 17:12:57 -04:00
return Environment ;
}
2022-08-15 10:49:30 -04:00
const TArray < Metasound : : FVertexName > & UMetaSoundSource : : GetOutputAudioChannelOrder ( ) const
2021-07-28 17:12:57 -04:00
{
2023-03-07 17:01:52 -05:00
using namespace Metasound : : Engine ;
using namespace Metasound : : Frontend ;
2021-07-28 17:12:57 -04:00
2023-03-07 17:01:52 -05:00
if ( const FOutputAudioFormatInfo * FormatInfo = GetOutputAudioFormatInfo ( ) . Find ( OutputFormat ) )
2021-07-28 17:12:57 -04:00
{
2022-08-15 10:49:30 -04:00
return FormatInfo - > OutputVertexChannelOrder ;
2021-07-28 17:12:57 -04:00
}
else
{
// Unhandled audio format. Need to update audio output format vertex key map.
checkNoEntry ( ) ;
2021-09-13 14:14:37 -04:00
static const TArray < Metasound : : FVertexName > Empty ;
2021-07-28 17:12:57 -04:00
return Empty ;
}
}
2022-12-13 18:25:01 -05:00
void UMetaSoundSource : : TrackGenerator ( uint64 Id , TSharedPtr < Metasound : : FMetasoundGenerator > Generator )
{
FScopeLock GeneratorMapLock ( & GeneratorMapCriticalSection ) ;
Generators . Add ( Id , Generator ) ;
OnGeneratorInstanceCreated . Broadcast ( Id , Generator ) ;
}
void UMetaSoundSource : : ForgetGenerator ( ISoundGeneratorPtr Generator )
{
using namespace Metasound ;
FMetasoundGenerator * AsMetasoundGenerator = static_cast < FMetasoundGenerator * > ( Generator . Get ( ) ) ;
FScopeLock GeneratorMapLock ( & GeneratorMapCriticalSection ) ;
for ( auto It = Generators . begin ( ) ; It ! = Generators . end ( ) ; + + It )
{
if ( ( * It ) . Value . HasSameObject ( AsMetasoundGenerator ) )
{
OnGeneratorInstanceDestroyed . Broadcast ( ( * It ) . Key , StaticCastSharedPtr < Metasound : : FMetasoundGenerator > ( Generator ) ) ;
Generators . Remove ( ( * It ) . Key ) ;
return ;
}
}
}
TWeakPtr < Metasound : : FMetasoundGenerator > UMetaSoundSource : : GetGeneratorForAudioComponent ( uint64 ComponentId ) const
{
using namespace Metasound ;
FScopeLock GeneratorMapLock ( & GeneratorMapCriticalSection ) ;
const TWeakPtr < FMetasoundGenerator > * Result = Generators . Find ( ComponentId ) ;
if ( ! Result )
{
return TWeakPtr < FMetasoundGenerator > ( nullptr ) ;
}
return * Result ;
}
2023-04-05 17:38:47 -04:00
2023-10-13 17:49:56 -04:00
bool UMetaSoundSource : : IsDynamic ( ) const
{
return DynamicTransactor . IsValid ( ) ;
}
2023-04-05 17:38:47 -04:00
Metasound : : SourcePrivate : : FParameterRouter & UMetaSoundSource : : GetParameterRouter ( )
{
using namespace Metasound : : SourcePrivate ;
static FParameterRouter Router ;
return Router ;
}
2023-09-22 15:01:07 -04:00
bool UMetaSoundSource : : IsBuilderActive ( ) const
{
return bIsBuilderActive ;
}
void UMetaSoundSource : : OnBeginActiveBuilder ( )
{
2023-12-15 13:21:12 -05:00
using namespace Metasound : : Frontend ;
2023-09-22 15:01:07 -04:00
if ( bIsBuilderActive )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " OnBeginActiveBuilder() call while prior builder is still active. This may indicate that multiple builders are attempting to modify the MetaSound %s concurrently. " ) , * GetOwningAssetName ( ) )
}
// If a builder is activating, make sure any in-flight registration
// tasks have completed. Async registration tasks use the FMetasoundFrontendDocument
// that lives on this object. We need to make sure that registration task
// completes so that the FMetasoundFrontendDocument does not get modified
// by a builder while it is also being read by async registration.
2023-12-15 13:21:12 -05:00
const FGraphRegistryKey GraphKey = GetGraphRegistryKey ( ) ;
if ( GraphKey . IsValid ( ) )
{
FMetasoundFrontendRegistryContainer : : Get ( ) - > WaitForAsyncGraphRegistration ( GraphKey ) ;
}
2023-09-22 15:01:07 -04:00
bIsBuilderActive = true ;
2023-10-03 12:17:13 -04:00
// Currently we do not have information on whether inputs were added or removed
2023-12-15 13:21:12 -05:00
// from the document. We invalidate the cached runtime inputs just in case.
2023-10-03 12:17:13 -04:00
// MetaSounds which have an active builder should not be using cached runtime
// input data until the builder is no longer active.
InvalidateCachedRuntimeInputData ( ) ;
2023-09-22 15:01:07 -04:00
}
void UMetaSoundSource : : OnFinishActiveBuilder ( )
{
bIsBuilderActive = false ;
}
2023-12-15 13:21:12 -05:00
TSharedPtr < Metasound : : DynamicGraph : : FDynamicOperatorTransactor > UMetaSoundSource : : SetDynamicGeneratorEnabled ( const FTopLevelAssetPath & InAssetPath , bool bInIsEnabled )
2023-06-14 17:01:59 -04:00
{
using namespace Metasound ;
using namespace Metasound : : DynamicGraph ;
2023-08-09 16:45:21 -04:00
if ( bInIsEnabled )
2023-06-14 17:01:59 -04:00
{
2023-08-09 16:45:21 -04:00
if ( ! DynamicTransactor . IsValid ( ) )
2023-06-14 17:01:59 -04:00
{
2023-09-22 15:01:07 -04:00
// If a FGraph exists for this UMetaSoundSource, then we need to initialize
// the DynamicTransactor with the existing FGraph so it has the correct
// initial state.
//
// Currently, any existing FGraph will be stored in the node registry,
// hence we check if the graph is registered and retrieve the current
// graph to see if any FGraph already exists.
if ( IsRegistered ( ) )
2023-08-09 16:45:21 -04:00
{
2023-12-15 13:21:12 -05:00
TSharedPtr < const FGraph > CurrentGraph = FMetasoundFrontendRegistryContainer : : Get ( ) - > GetGraph ( GetGraphRegistryKey ( ) ) ;
2023-09-22 15:01:07 -04:00
if ( CurrentGraph . IsValid ( ) )
{
DynamicTransactor = MakeShared < FDynamicOperatorTransactor > ( * CurrentGraph ) ;
}
else
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to get existing graph for dynamic metasound %s. Initializing to empty graph. " ) , * GetOwningAssetName ( ) ) ;
DynamicTransactor = MakeShared < FDynamicOperatorTransactor > ( ) ;
}
2023-08-09 16:45:21 -04:00
}
else
{
DynamicTransactor = MakeShared < FDynamicOperatorTransactor > ( ) ;
}
2023-06-14 17:01:59 -04:00
}
}
else
{
DynamicTransactor . Reset ( ) ;
}
return DynamicTransactor ;
}
TSharedPtr < Metasound : : DynamicGraph : : FDynamicOperatorTransactor > UMetaSoundSource : : GetDynamicGeneratorTransactor ( ) const
{
return DynamicTransactor ;
}
2023-10-03 12:17:13 -04:00
2023-12-01 12:57:59 -05:00
UMetaSoundSource : : FRuntimeInput UMetaSoundSource : : CreateRuntimeInput ( const Metasound : : Frontend : : IDataTypeRegistry & Registry , const FMetasoundFrontendClassInput & Input , bool bCreateUObjectProxies )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
bool bIsTransmittable = false ;
if ( const IDataTypeRegistryEntry * RegistryEntry = Registry . FindDataTypeRegistryEntry ( Input . TypeName ) )
{
bIsTransmittable = RegistryEntry - > GetDataTypeInfo ( ) . bIsTransmittable ;
}
else
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to find data type '%s' in registry. Assuming data type is not transmittable " ) , * Input . TypeName . ToString ( ) ) ;
}
FAudioParameter DefaultParameter = SourcePrivate : : MakeAudioParameter ( Registry , Input . Name , Input . TypeName , Input . DefaultLiteral , bCreateUObjectProxies ) ;
return FRuntimeInput { Input . Name , Input . TypeName , Input . AccessType , DefaultParameter , bIsTransmittable } ;
}
2023-11-02 18:41:29 -04:00
Metasound : : TSortedVertexNameMap < UMetaSoundSource : : FRuntimeInput > UMetaSoundSource : : CreateRuntimeInputMap ( bool bCreateUObjectProxies ) const
2023-10-03 12:17:13 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( UMetaSoundSource : : CreateRuntimeInputMap ) ;
auto GetInputName = [ ] ( const FMetasoundFrontendClassInput & InInput ) { return InInput . Name ; } ;
2023-12-01 12:57:59 -05:00
const IDataTypeRegistry & Registry = IDataTypeRegistry : : Get ( ) ;
2023-10-03 12:17:13 -04:00
const FMetasoundFrontendDocument & Doc = GetConstDocument ( ) ;
TArray < const IInterfaceRegistryEntry * > Interfaces ;
FMetaSoundFrontendDocumentBuilder : : FindDeclaredInterfaces ( Doc , Interfaces ) ;
// Inputs which are controlled by an interface are private unless
// their router name is `Audio::IParameterTransmitter::RouterName`
TSet < FVertexName > PrivateInputs ;
for ( const IInterfaceRegistryEntry * InterfaceEntry : Interfaces )
{
if ( InterfaceEntry )
{
if ( InterfaceEntry - > GetRouterName ( ) ! = Audio : : IParameterTransmitter : : RouterName )
{
const FMetasoundFrontendInterface & Interface = InterfaceEntry - > GetInterface ( ) ;
Algo : : Transform ( Interface . Inputs , PrivateInputs , GetInputName ) ;
}
}
}
// Cache all inputs which are not private inputs.
TSortedVertexNameMap < FRuntimeInput > PublicInputs ;
for ( const FMetasoundFrontendClassInput & Input : Doc . RootGraph . Interface . Inputs )
{
if ( ! PrivateInputs . Contains ( Input . Name ) )
{
2023-12-01 12:57:59 -05:00
PublicInputs . Add ( Input . Name , CreateRuntimeInput ( Registry , Input , bCreateUObjectProxies ) ) ;
2023-10-03 12:17:13 -04:00
}
}
// Add the parameter pack input that ALL Metasounds have
FMetasoundFrontendClassInput ParameterPackInput = UMetasoundParameterPack : : GetClassInput ( ) ;
2023-11-02 18:41:29 -04:00
FAudioParameter ParameterPackDefaultParameter = SourcePrivate : : MakeAudioParameter ( Registry , ParameterPackInput . Name , ParameterPackInput . TypeName , ParameterPackInput . DefaultLiteral , bCreateUObjectProxies ) ;
PublicInputs . Add ( ParameterPackInput . Name , FRuntimeInput { ParameterPackInput . Name , ParameterPackInput . TypeName , ParameterPackInput . AccessType , ParameterPackDefaultParameter , true /* bIsTransmittable */ } ) ;
2023-10-03 12:17:13 -04:00
return PublicInputs ;
}
void UMetaSoundSource : : CacheRuntimeInputData ( )
{
2023-11-03 19:38:58 -04:00
using namespace Metasound ;
2023-10-03 12:17:13 -04:00
if ( bIsBuilderActive )
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Skipping caching of runtime inputs for UMetaSoundSource %s because there is an active builder " ) , * GetOwningAssetName ( ) ) ;
}
2023-11-02 18:41:29 -04:00
constexpr bool bCreateUObjectProxies = true ;
RuntimeInputData . InputMap = CreateRuntimeInputMap ( bCreateUObjectProxies ) ;
2023-11-03 19:38:58 -04:00
// Determine if preset graph inflation is possible
//
// Constructor inputs conflict with `Preset Graph Inflation` and `Operator Caching`.
// This logic protects against attempting to use preset graph inflation when the preset
// graph has overridden constructor pins.
//
// Operator caching of base preset graphs fail when there are constructor inputs because
// constructor inputs set on the preset cannot be updated after the base operator is
// cached.
auto IsOverriddenConstructorInput = [ & InputsInheritingDefault = RootMetasoundDocument . RootGraph . PresetOptions . InputsInheritingDefault ] ( const TPair < FVertexName , FRuntimeInput > & Pair )
{
if ( Pair . Value . AccessType = = EMetasoundFrontendVertexAccessType : : Value )
{
return ! InputsInheritingDefault . Contains ( Pair . Key ) ;
}
return false ;
} ;
bIsPresetGraphInflationSupported = RootMetasoundDocument . RootGraph . PresetOptions . bIsPreset & & ! Algo : : AnyOf ( RuntimeInputData . InputMap , IsOverriddenConstructorInput ) ;
2023-10-03 12:17:13 -04:00
RuntimeInputData . bIsValid . store ( true ) ;
}
void UMetaSoundSource : : InvalidateCachedRuntimeInputData ( )
{
2023-11-03 19:38:58 -04:00
bIsPresetGraphInflationSupported = false ;
2023-10-03 12:17:13 -04:00
RuntimeInputData . bIsValid . store ( false ) ;
}
2023-11-01 12:17:48 -04:00
TSharedPtr < const Metasound : : IGraph > UMetaSoundSource : : TryGetMetaSoundPresetBaseGraph ( ) const
{
using namespace Metasound ;
TSharedPtr < const IGraph > MetasoundGraph ;
if ( ReferencedAssetClassObjects . Num ( ) = = 1 )
{
// Get first element from TSet<>
TObjectPtr < const UObject > BaseGraph ;
for ( const TObjectPtr < UObject > & ReferencedGraph : ReferencedAssetClassObjects )
{
BaseGraph = ReferencedGraph ;
break ;
}
// Get the reference graph as a UMetaSoundSource
TObjectPtr < const UMetaSoundSource > BaseMetaSoundSource = Cast < const UMetaSoundSource > ( BaseGraph ) ;
if ( BaseMetaSoundSource )
{
2023-12-15 13:21:12 -05:00
// Get registered graph of base metasound source.
MetasoundGraph = FMetasoundFrontendRegistryContainer : : Get ( ) - > GetGraph ( BaseMetaSoundSource - > GetGraphRegistryKey ( ) ) ;
2023-11-01 12:17:48 -04:00
}
}
else
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Attempt to reference parent of metasound preset failed due to unexpected number of reference asses (%d) from MetaSound Preset %s " ) , ReferencedAssetClassObjects . Num ( ) , * GetOwningAssetName ( ) ) ;
}
return MetasoundGraph ;
}
void UMetaSoundSource : : MergePresetOverridesAndSuppliedDefaults ( const TArray < FAudioParameter > & InSuppliedDefaults , TArray < FAudioParameter > & OutMerged )
{
using namespace Metasound ;
if ( ! RuntimeInputData . bIsValid . load ( ) )
{
UE_CLOG ( SourcePrivate : : HasNotBeenLoggedForThisObject ( * this , __LINE__ ) , LogMetaSound , Warning , TEXT ( " Failed to use experimental preset inflation due to invalid runtime data on MetaSound %s. Ensure that UMetaSoundSource::InitResources() is executed on the game thread before CreateSoundGenerator " ) , * GetOwningAssetName ( ) ) ;
}
else
{
// Get parameters that are overridden in the Preset.
for ( const TPair < FVertexName , FRuntimeInput > & Pair : RuntimeInputData . InputMap )
{
if ( ! RootMetasoundDocument . RootGraph . PresetOptions . InputsInheritingDefault . Contains ( Pair . Key ) )
{
2023-11-02 18:41:29 -04:00
OutMerged . Add ( Pair . Value . DefaultParameter ) ;
2023-11-01 12:17:48 -04:00
}
}
// Instead of using `FAudioParameter::Merge(...)` we roll custom merge logic because
// FAudioParameter::Merge(...) requires us to move the InSuppliedDefaults into the
// OutMerged array. This unwanted in this case because we want to maintain InSuppliedDefaults
// for the scenario that the experimental preset override fails to create a generator
// in which case we are forced to use the prior codepath which does not merge these
// two parameter arrays.
for ( const FAudioParameter & SuppliedParameter : InSuppliedDefaults )
{
auto HasSameName = [ & Name = SuppliedParameter . ParamName ] ( const FAudioParameter & InOtherParam ) - > bool
{
return InOtherParam . ParamName = = Name ;
} ;
// The supplied defaults generally come from BP and should override any other parameters.
if ( FAudioParameter * Param = OutMerged . FindByPredicate ( HasSameName ) )
{
* Param = SuppliedParameter ;
}
else
{
OutMerged . Add ( SuppliedParameter ) ;
}
}
}
}
2021-12-10 20:37:31 -05:00
# undef LOCTEXT_NAMESPACE // MetaSound