2022-12-15 17:03:02 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "MetasoundGeneratorHandle.h"
|
|
|
|
|
|
|
|
|
|
#include "Components/AudioComponent.h"
|
|
|
|
|
#include "AudioDeviceManager.h"
|
|
|
|
|
#include "MetasoundGenerator.h"
|
|
|
|
|
#include "MetasoundSource.h"
|
2023-04-04 12:40:45 -04:00
|
|
|
#include "MetasoundTrace.h"
|
2022-12-15 17:03:02 -05:00
|
|
|
#include "MetasoundParameterPack.h"
|
|
|
|
|
|
2023-04-05 17:42:36 -04:00
|
|
|
TMap<FName, FName> UMetasoundGeneratorHandle::PassthroughAnalyzers{};
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
UMetasoundGeneratorHandle* UMetasoundGeneratorHandle::CreateMetaSoundGeneratorHandle(UAudioComponent* OnComponent)
|
|
|
|
|
{
|
2023-04-05 17:42:36 -04:00
|
|
|
METASOUND_LLM_SCOPE;
|
|
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::CreateMetaSoundGeneratorHandle);
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
if (!OnComponent)
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 12:28:48 -04:00
|
|
|
if (OnComponent->bCanPlayMultipleInstances)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(
|
|
|
|
|
LogMetaSound,
|
|
|
|
|
Warning,
|
|
|
|
|
TEXT("Created a UMetaSoundGeneratorHandle for a UAudioComponent that is allowed to play multiple instances. This may not work as expected."))
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
UMetasoundGeneratorHandle* Result = NewObject<UMetasoundGeneratorHandle>();
|
|
|
|
|
Result->SetAudioComponent(OnComponent);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 17:42:36 -04:00
|
|
|
void UMetasoundGeneratorHandle::BeginDestroy()
|
|
|
|
|
{
|
|
|
|
|
Super::BeginDestroy();
|
|
|
|
|
DetachGeneratorDelegates();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UMetasoundGeneratorHandle::IsValid() const
|
|
|
|
|
{
|
|
|
|
|
return AudioComponent.IsValid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64 UMetasoundGeneratorHandle::GetAudioComponentId() const
|
|
|
|
|
{
|
|
|
|
|
if (AudioComponent.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return AudioComponent->GetAudioComponentID();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return INDEX_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
void UMetasoundGeneratorHandle::ClearCachedData()
|
|
|
|
|
{
|
|
|
|
|
DetachGeneratorDelegates();
|
|
|
|
|
AudioComponent = nullptr;
|
|
|
|
|
CachedMetasoundSource = nullptr;
|
|
|
|
|
CachedGeneratorPtr = nullptr;
|
|
|
|
|
CachedParameterPack = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::SetAudioComponent(UAudioComponent* InAudioComponent)
|
|
|
|
|
{
|
|
|
|
|
if (InAudioComponent != AudioComponent)
|
|
|
|
|
{
|
|
|
|
|
ClearCachedData();
|
|
|
|
|
AudioComponent = InAudioComponent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::CacheMetasoundSource()
|
|
|
|
|
{
|
2023-04-05 17:42:36 -04:00
|
|
|
if (!AudioComponent.IsValid())
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UMetaSoundSource* CurrentMetasoundSource = Cast<UMetaSoundSource>(AudioComponent->GetSound());
|
|
|
|
|
if (CachedMetasoundSource == CurrentMetasoundSource)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DetachGeneratorDelegates();
|
|
|
|
|
CachedGeneratorPtr = nullptr;
|
|
|
|
|
CachedMetasoundSource = CurrentMetasoundSource;
|
|
|
|
|
|
2023-04-05 17:42:36 -04:00
|
|
|
if (CachedMetasoundSource.IsValid())
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
|
|
|
|
AttachGeneratorDelegates();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::AttachGeneratorDelegates()
|
|
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
// These delegates can be called on a separate thread, so we need to take steps
|
|
|
|
|
// to assure this UObject hasn't been garbage collected before trying to dereference.
|
|
|
|
|
// That is why we capture a TWeakObjectPtr and try to dereference that later!
|
|
|
|
|
GeneratorCreatedDelegateHandle = CachedMetasoundSource->OnGeneratorInstanceCreated.AddLambda(
|
|
|
|
|
[WeakGeneratorHandlePtr = TWeakObjectPtr<UMetasoundGeneratorHandle>(this),
|
|
|
|
|
StatId = this->GetStatID(true)]
|
|
|
|
|
(uint64 InAudioComponentId, TSharedPtr<Metasound::FMetasoundGenerator> InGenerator)
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
// We are in the audio render (or control) thread here, so create a "dispatch task" to be
|
|
|
|
|
// executed later on the game thread...
|
|
|
|
|
FFunctionGraphTask::CreateAndDispatchWhenReady(
|
|
|
|
|
[WeakGeneratorHandlePtr, InAudioComponentId, InGenerator]()
|
|
|
|
|
{
|
2023-04-04 12:40:45 -04:00
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::CallingGeneratorAttachedDelegates);
|
2023-04-03 23:30:36 -04:00
|
|
|
check(IsInGameThread());
|
|
|
|
|
// Now, since we are in the game thread, try to dereference the pointer to
|
|
|
|
|
// to the UMetasoundGeneratorHandle. This should only succeed if the UObject
|
|
|
|
|
// hasn't been garbage collected.
|
|
|
|
|
if (UMetasoundGeneratorHandle* TheHandle = WeakGeneratorHandlePtr.Get())
|
|
|
|
|
{
|
|
|
|
|
TheHandle->OnSourceCreatedAGenerator(InAudioComponentId, InGenerator);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
StatId, nullptr, ENamedThreads::GameThread);
|
2022-12-15 17:03:02 -05:00
|
|
|
});
|
2023-04-03 23:32:48 -04:00
|
|
|
GeneratorDestroyedDelegateHandle = CachedMetasoundSource->OnGeneratorInstanceDestroyed.AddLambda(
|
|
|
|
|
[WeakGeneratorHandlePtr = TWeakObjectPtr<UMetasoundGeneratorHandle>(this),
|
|
|
|
|
StatId = this->GetStatID(true)]
|
|
|
|
|
(uint64 InAudioComponentId, TSharedPtr<Metasound::FMetasoundGenerator> InGenerator)
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
2023-04-03 23:32:48 -04:00
|
|
|
// We are in the audio render (or control) thread here, so create a "dispatch task" to be
|
|
|
|
|
// executed later on the game thread...
|
|
|
|
|
FFunctionGraphTask::CreateAndDispatchWhenReady(
|
|
|
|
|
[WeakGeneratorHandlePtr, InAudioComponentId, InGenerator]()
|
|
|
|
|
{
|
2023-04-04 12:40:45 -04:00
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::CallingGeneratorDetachedDelegates);
|
2023-04-03 23:32:48 -04:00
|
|
|
check(IsInGameThread());
|
|
|
|
|
// Now, since we are in the game thread, try to dereference the pointer to
|
|
|
|
|
// to the UMetasoundGeneratorHandle. This should only succeed if the UObject
|
|
|
|
|
// hasn't been garbage collected.
|
|
|
|
|
if (UMetasoundGeneratorHandle* TheHandle = WeakGeneratorHandlePtr.Get())
|
|
|
|
|
{
|
|
|
|
|
TheHandle->OnSourceDestroyedAGenerator(InAudioComponentId, InGenerator);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
StatId, nullptr, ENamedThreads::GameThread);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::AttachGraphChangedDelegate()
|
|
|
|
|
{
|
|
|
|
|
if (!CachedGeneratorPtr.IsValid() || !OnGeneratorsGraphChanged.IsBound())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> Generator = CachedGeneratorPtr.Pin();
|
|
|
|
|
if (Generator)
|
|
|
|
|
{
|
|
|
|
|
// We're about to add a delegate to the generator. This delegate will be called on
|
|
|
|
|
// the audio render thread so we need to take steps to assure this UMetasoundGeneratorHandle
|
|
|
|
|
// hasn't been garbage collected before trying to dereference it later when the delegate "fires".
|
|
|
|
|
// That is why we capture a TWeakObjectPtr and try to dereference that later!
|
|
|
|
|
GeneratorGraphChangedDelegateHandle = Generator->OnSetGraph.AddLambda(
|
|
|
|
|
[WeakGeneratorHandlePtr = TWeakObjectPtr<UMetasoundGeneratorHandle>(this),
|
|
|
|
|
StatId = this->GetStatID(true)]
|
|
|
|
|
()
|
|
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
// We are in the audio render thread here, so create a "dispatch task" to be
|
|
|
|
|
// executed later on the game thread...
|
|
|
|
|
FFunctionGraphTask::CreateAndDispatchWhenReady(
|
|
|
|
|
[WeakGeneratorHandlePtr]()
|
|
|
|
|
{
|
2023-04-04 12:40:45 -04:00
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::CallingGraphChangedDelegates);
|
2023-04-03 23:30:36 -04:00
|
|
|
check(IsInGameThread());
|
|
|
|
|
// Now, since we are in the game thread, try to dereference the pointer to
|
|
|
|
|
// to the UMetasoundGeneratorHandle. This should only succeed if the UObject
|
|
|
|
|
// hasn't been garbage collected.
|
|
|
|
|
if (UMetasoundGeneratorHandle* TheHandle = WeakGeneratorHandlePtr.Get())
|
|
|
|
|
{
|
|
|
|
|
TheHandle->OnGeneratorsGraphChanged.Broadcast();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
StatId, nullptr, ENamedThreads::GameThread);
|
2022-12-15 17:03:02 -05:00
|
|
|
});
|
|
|
|
|
}
|
2023-04-03 23:30:36 -04:00
|
|
|
}
|
2022-12-15 17:03:02 -05:00
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::DetachGeneratorDelegates()
|
|
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
// First detach any callbacks that tell us when a generator is created or destroyed
|
|
|
|
|
// for the UMetasoundSource of interest...
|
2023-04-05 17:42:36 -04:00
|
|
|
if (CachedMetasoundSource.IsValid())
|
2023-04-03 23:30:36 -04:00
|
|
|
{
|
2022-12-15 17:03:02 -05:00
|
|
|
CachedMetasoundSource->OnGeneratorInstanceCreated.Remove(GeneratorCreatedDelegateHandle);
|
|
|
|
|
GeneratorCreatedDelegateHandle.Reset();
|
|
|
|
|
CachedMetasoundSource->OnGeneratorInstanceDestroyed.Remove(GeneratorDestroyedDelegateHandle);
|
|
|
|
|
GeneratorDestroyedDelegateHandle.Reset();
|
|
|
|
|
}
|
2023-04-03 23:30:36 -04:00
|
|
|
// Now detach any callback we may have registered to get callbacks when the generator's
|
|
|
|
|
// graph has been changed...
|
|
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> PinnedGenerator = CachedGeneratorPtr.Pin();
|
|
|
|
|
if (PinnedGenerator)
|
|
|
|
|
{
|
|
|
|
|
PinnedGenerator->OnSetGraph.Remove(GeneratorGraphChangedDelegateHandle);
|
|
|
|
|
GeneratorGraphChangedDelegateHandle.Reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-15 17:03:02 -05:00
|
|
|
|
|
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> UMetasoundGeneratorHandle::PinGenerator()
|
|
|
|
|
{
|
|
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> PinnedGenerator = CachedGeneratorPtr.Pin();
|
2023-04-05 17:42:36 -04:00
|
|
|
if (PinnedGenerator.IsValid() || !CachedMetasoundSource.IsValid())
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
|
|
|
|
return PinnedGenerator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The first attempt to pin failed, so reach out to the MetaSoundSource and see if it has a
|
|
|
|
|
// generator for our AudioComponent...
|
2023-04-05 17:42:36 -04:00
|
|
|
check(AudioComponent.IsValid()); // expect the audio component to still be valid if the generator is.
|
|
|
|
|
CachedGeneratorPtr = CachedMetasoundSource->GetGeneratorForAudioComponent(AudioComponent->GetAudioComponentID());
|
2022-12-15 17:03:02 -05:00
|
|
|
PinnedGenerator = CachedGeneratorPtr.Pin();
|
|
|
|
|
return PinnedGenerator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UMetasoundGeneratorHandle::ApplyParameterPack(UMetasoundParameterPack* Pack)
|
|
|
|
|
{
|
2023-04-05 17:42:36 -04:00
|
|
|
METASOUND_LLM_SCOPE;
|
|
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::ApplyParameterPack);
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
if (!Pack)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a copy of the parameter pack and cache it.
|
|
|
|
|
CachedParameterPack = Pack->GetCopyOfParameterStorage();
|
|
|
|
|
|
|
|
|
|
// No point in continuing if the parameter pack is not valid for any reason.
|
|
|
|
|
if (!CachedParameterPack.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assure that our MetaSoundSource is up to date. It is possible that this has been
|
|
|
|
|
// changed via script since we were first created.
|
|
|
|
|
CacheMetasoundSource();
|
|
|
|
|
|
|
|
|
|
// Now we can try to pin the generator.
|
|
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> PinnedGenerator = PinGenerator();
|
|
|
|
|
|
|
|
|
|
if (!PinnedGenerator.IsValid())
|
|
|
|
|
{
|
|
|
|
|
// Failed to pin the generator, but we have cached the parameter pack,
|
|
|
|
|
// so if our delegate gets called when a new generator is created we can
|
|
|
|
|
// apply the cached parameters then.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally... send down the parameter pack.
|
|
|
|
|
PinnedGenerator->QueueParameterPack(CachedParameterPack);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 17:38:24 -04:00
|
|
|
TSharedPtr<Metasound::FMetasoundGenerator> UMetasoundGeneratorHandle::GetGenerator()
|
|
|
|
|
{
|
|
|
|
|
// Attach if we aren't attached, check for changes, etc...
|
|
|
|
|
CacheMetasoundSource();
|
|
|
|
|
|
|
|
|
|
return PinGenerator();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-03 23:31:26 -04:00
|
|
|
FDelegateHandle UMetasoundGeneratorHandle::AddGraphSetCallback(const UMetasoundGeneratorHandle::FOnSetGraph& Delegate)
|
2023-04-03 23:30:36 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
FDelegateHandle Handle = OnGeneratorsGraphChanged.Add(Delegate);
|
|
|
|
|
CacheMetasoundSource();
|
|
|
|
|
AttachGraphChangedDelegate();
|
|
|
|
|
return Handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UMetasoundGeneratorHandle::RemoveGraphSetCallback(const FDelegateHandle& Handle)
|
|
|
|
|
{
|
|
|
|
|
return OnGeneratorsGraphChanged.Remove(Handle);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 17:42:36 -04:00
|
|
|
bool UMetasoundGeneratorHandle::WatchOutput(
|
|
|
|
|
FName OutputName,
|
|
|
|
|
const FOnMetasoundOutputValueChanged& OnOutputValueChanged,
|
|
|
|
|
FName AnalyzerName,
|
|
|
|
|
FName AnalyzerOutputName)
|
2023-04-03 23:30:36 -04:00
|
|
|
{
|
2023-04-05 17:42:36 -04:00
|
|
|
METASOUND_LLM_SCOPE;
|
|
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::WatchOutput);
|
|
|
|
|
|
|
|
|
|
if (!IsValid())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CacheMetasoundSource();
|
|
|
|
|
|
|
|
|
|
if (!CachedMetasoundSource.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the node id and type name
|
|
|
|
|
const Metasound::Frontend::FNodeHandle Node = CachedMetasoundSource->GetRootGraphHandle()->GetOutputNodeWithName(OutputName);
|
|
|
|
|
|
|
|
|
|
if (!Node->IsValid())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const FGuid NodeId = Node->GetID();
|
|
|
|
|
|
|
|
|
|
// We expect an output node to have exactly one output
|
|
|
|
|
if (!ensure(Node->GetNumOutputs() == 1))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const FName TypeName = Node->GetOutputs()[0]->GetDataType();
|
|
|
|
|
|
|
|
|
|
// Make the analyzer address
|
|
|
|
|
Metasound::Frontend::FAnalyzerAddress AnalyzerAddress;
|
|
|
|
|
AnalyzerAddress.DataType = TypeName;
|
|
|
|
|
AnalyzerAddress.InstanceID = AudioComponent->GetAudioComponentID();
|
|
|
|
|
AnalyzerAddress.OutputName = OutputName;
|
|
|
|
|
|
|
|
|
|
// If no analyzer name was provided, try to find a passthrough analyzer
|
|
|
|
|
if (AnalyzerName.IsNone())
|
|
|
|
|
{
|
|
|
|
|
if (!PassthroughAnalyzers.Contains(TypeName))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AnalyzerName = PassthroughAnalyzers[TypeName];
|
|
|
|
|
AnalyzerOutputName = "Value";
|
|
|
|
|
}
|
|
|
|
|
AnalyzerAddress.AnalyzerName = AnalyzerName;
|
|
|
|
|
|
|
|
|
|
AnalyzerAddress.AnalyzerInstanceID = FGuid::NewGuid();
|
|
|
|
|
AnalyzerAddress.NodeID = NodeId;
|
|
|
|
|
|
|
|
|
|
// if we already have a generator, go ahead and make the analyzer and watcher
|
|
|
|
|
if (const TSharedPtr<Metasound::FMetasoundGenerator> PinnedGenerator = PinGenerator())
|
|
|
|
|
{
|
|
|
|
|
CreateAnalyzerAndWatcher(PinnedGenerator, MoveTemp(AnalyzerAddress));
|
|
|
|
|
}
|
|
|
|
|
// otherwise enqueue it for later
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OutputAnalyzersToAdd.Enqueue(MoveTemp(AnalyzerAddress));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// either way, add the delegate to the map
|
|
|
|
|
OutputListenerMap.FindOrAdd(OutputName).FindOrAdd(AnalyzerOutputName).AddUnique(OnOutputValueChanged);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::RegisterPassthroughAnalyzerForType(FName TypeName, FName AnalyzerName)
|
|
|
|
|
{
|
|
|
|
|
check(!PassthroughAnalyzers.Contains(TypeName));
|
|
|
|
|
PassthroughAnalyzers.Add(TypeName, AnalyzerName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::UpdateWatchers()
|
|
|
|
|
{
|
|
|
|
|
METASOUND_LLM_SCOPE;
|
|
|
|
|
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE(UMetasoundGeneratorHandle::UpdateWatchers);
|
|
|
|
|
|
|
|
|
|
for (auto& Watcher : OutputWatchers)
|
|
|
|
|
{
|
|
|
|
|
Watcher.Update([this](FName OutputName, const FMetaSoundOutput& Output)
|
|
|
|
|
{
|
|
|
|
|
if (TMap<FName, FOnOutputValueChangedMulticast>* Map = OutputListenerMap.Find(OutputName))
|
|
|
|
|
{
|
|
|
|
|
if (const FOnOutputValueChangedMulticast* Delegate = Map->Find(Output.Name))
|
|
|
|
|
{
|
|
|
|
|
Delegate->Broadcast(OutputName, Output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-04-03 23:30:36 -04:00
|
|
|
}
|
|
|
|
|
|
2022-12-15 17:03:02 -05:00
|
|
|
void UMetasoundGeneratorHandle::OnSourceCreatedAGenerator(uint64 InAudioComponentId, TSharedPtr<Metasound::FMetasoundGenerator> InGenerator)
|
|
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
check(IsInGameThread());
|
2023-04-05 17:42:36 -04:00
|
|
|
|
|
|
|
|
if (!AudioComponent.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InAudioComponentId == AudioComponent->GetAudioComponentID())
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
|
|
|
|
CachedGeneratorPtr = InGenerator;
|
2023-03-20 17:38:24 -04:00
|
|
|
if (InGenerator)
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
2023-04-05 17:42:36 -04:00
|
|
|
// If there is a parameter pack to apply, apply it
|
2023-03-20 17:38:24 -04:00
|
|
|
if (CachedParameterPack)
|
|
|
|
|
{
|
|
|
|
|
InGenerator->QueueParameterPack(CachedParameterPack);
|
|
|
|
|
}
|
2023-04-05 17:42:36 -04:00
|
|
|
|
|
|
|
|
// If there are analyzers to create, create them
|
|
|
|
|
{
|
|
|
|
|
Metasound::Frontend::FAnalyzerAddress Address;
|
|
|
|
|
while (OutputAnalyzersToAdd.Dequeue(Address))
|
|
|
|
|
{
|
|
|
|
|
CreateAnalyzerAndWatcher(InGenerator, MoveTemp(Address));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 17:38:24 -04:00
|
|
|
OnGeneratorHandleAttached.Broadcast();
|
2023-04-03 23:30:36 -04:00
|
|
|
// If anyone has told us they are interested in being notified when a generator's
|
|
|
|
|
// graph has changed go ahead and set that up now...
|
|
|
|
|
AttachGraphChangedDelegate();
|
2022-12-15 17:03:02 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMetasoundGeneratorHandle::OnSourceDestroyedAGenerator(uint64 InAudioComponentId, TSharedPtr<Metasound::FMetasoundGenerator> InGenerator)
|
|
|
|
|
{
|
2023-04-03 23:30:36 -04:00
|
|
|
check(IsInGameThread());
|
2023-04-05 17:42:36 -04:00
|
|
|
|
|
|
|
|
if (!AudioComponent.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InAudioComponentId == AudioComponent->GetAudioComponentID())
|
2022-12-15 17:03:02 -05:00
|
|
|
{
|
2023-03-20 17:38:24 -04:00
|
|
|
if (CachedGeneratorPtr.IsValid())
|
|
|
|
|
{
|
|
|
|
|
OnGeneratorHandleDetached.Broadcast();
|
|
|
|
|
}
|
2022-12-15 17:03:02 -05:00
|
|
|
CachedGeneratorPtr = nullptr;
|
2023-04-18 08:44:35 -04:00
|
|
|
|
|
|
|
|
// Clear out the watchers, but keep track of what we had in case the sound comes back
|
|
|
|
|
for (const Metasound::Private::FMetasoundOutputWatcher& Watcher : OutputWatchers)
|
|
|
|
|
{
|
|
|
|
|
OutputAnalyzersToAdd.Enqueue(Watcher.GetAddress());
|
|
|
|
|
}
|
|
|
|
|
OutputWatchers.Empty();
|
2022-12-15 17:03:02 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 17:42:36 -04:00
|
|
|
void UMetasoundGeneratorHandle::CreateAnalyzerAndWatcher(
|
|
|
|
|
const TSharedPtr<Metasound::FMetasoundGenerator> Generator,
|
|
|
|
|
Metasound::Frontend::FAnalyzerAddress&& AnalyzerAddress)
|
|
|
|
|
{
|
|
|
|
|
if (!IsValid())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the analyzer (will skip if there's already one)
|
|
|
|
|
Generator->AddOutputVertexAnalyzer(AnalyzerAddress);
|
|
|
|
|
|
|
|
|
|
// Create the watcher
|
|
|
|
|
if (!OutputWatchers.ContainsByPredicate(
|
|
|
|
|
[&AnalyzerAddress](const Metasound::Private::FMetasoundOutputWatcher& Watcher)
|
|
|
|
|
{
|
|
|
|
|
return AnalyzerAddress.OutputName == Watcher.Name;
|
|
|
|
|
}))
|
|
|
|
|
{
|
|
|
|
|
OutputWatchers.Emplace(MoveTemp(AnalyzerAddress), Generator->OperatorSettings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-03 23:30:36 -04:00
|
|
|
|