2020-09-08 14:13:51 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "DSP/Dsp.h"
# include "DSP/MultithreadedPatching.h"
2020-11-04 14:26:37 -04:00
# include "MetasoundAudioBuffer.h"
# include "MetasoundDataFactory.h"
# include "MetasoundDataReference.h"
2021-04-06 01:41:29 -04:00
# include "MetasoundExecutableOperator.h"
2020-11-04 14:26:37 -04:00
# include "MetasoundLiteral.h"
2021-08-30 14:08:45 -04:00
# include "MetasoundLog.h"
2020-11-04 14:26:37 -04:00
# include "MetasoundOperatorInterface.h"
# include "MetasoundOperatorSettings.h"
2021-04-06 01:41:29 -04:00
# include "MetasoundTrigger.h"
2020-11-04 14:26:37 -04:00
# include "Misc/Guid.h"
2021-10-12 21:21:22 -04:00
# include "Templates/TypeHash.h"
2020-11-04 14:26:37 -04:00
# include "UObject/NameTypes.h"
2020-09-08 14:13:51 -04:00
2021-07-21 15:46:42 -04:00
# include <atomic>
2020-09-08 14:13:51 -04:00
# include <type_traits>
/**
* METASOUND TRANSMISSION SYSTEM
* This allows for transmission of arbitrary datatypes using addresses set by FNames .
* This system is used by Send and Receive nodes in the Metasound graph , but can also be used in C + + for arbitrary message passing .
*
* Typical use case :
2021-03-05 20:06:54 -04:00
* TSenderPtr < float > FloatSender FDataTransmissionCenter : : Get ( ) . RegisterNewSender < float > ( ExampleAddress , InitSettings ) ;
2020-09-08 14:13:51 -04:00
* TReceiverPtr < float > FloatReceiver FDataTransmissionCenter : : Get ( ) . RegisterNewReceiver < float > ( ExampleAddress , InitSettings ) ;
*
* //...
* FloatSender - > Push ( 4.5f ) ;
*
* //... elsewhere on a different thread:
* float ReceivedFloat = FloatReceiver - > Pop ( ) ;
*
* It ' s that easy ! ( hopefully ! )
*
* In general , some performance considerations for this system :
* 1 ) a circular buffer is allocated for each address a sender is set up for , and is sized to the max Delay of any connected sender .
* 2 ) Senders and receivers are meant to have long lifecycles , and requesting them from the FDataTransmissionCenter may be expensive if done every tick .
* 3 ) Most non - audio data types cannot be mixed , so multiple senders to the same address will cause only one of the senders to be effective .
* 4 ) while the overall system is thread safe , individual TSender and TReceiver objects should only be used by a single thread .
*/
2021-08-30 14:08:45 -04:00
2020-09-08 14:13:51 -04:00
namespace Metasound
{
2021-10-12 21:21:22 -04:00
class METASOUNDFRONTEND_API FSendAddress
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
public :
FSendAddress ( ) = default ;
2020-09-08 14:13:51 -04:00
2021-10-12 21:21:22 -04:00
// Create an address without a data type or instance ID
FSendAddress ( const FString & InChannelName ) ;
// Create an address with a channel name, data type and optionally an instance ID.
FSendAddress ( const FName & InChannelName , const FName & InDataType , uint64 InInstanceID = INDEX_NONE ) ;
friend uint32 GetTypeHash ( const FSendAddress & InAddress )
2020-09-08 14:13:51 -04:00
{
2021-12-03 01:04:54 -05:00
uint32 HashedChannel = HashCombineFast ( : : GetTypeHash ( InAddress . DataType ) , : : GetTypeHash ( InAddress . ChannelName ) ) ;
HashedChannel = HashCombineFast ( HashedChannel , : : GetTypeHash ( InAddress . InstanceID ) ) ;
2021-10-12 21:21:22 -04:00
return HashedChannel ;
2020-09-08 14:13:51 -04:00
}
FORCEINLINE bool operator = = ( const FSendAddress & Other ) const
{
2021-10-12 21:21:22 -04:00
return ( InstanceID = = Other . InstanceID ) & & ( ChannelName = = Other . ChannelName ) & & ( DataType = = Other . DataType ) ; /*&& (Subsystem == Other.Subsystem);*/
2020-09-08 14:13:51 -04:00
}
2021-10-12 21:21:22 -04:00
FORCEINLINE bool operator ! = ( const FSendAddress & Other ) const
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
return ! ( * this = = Other ) ;
2020-09-08 14:13:51 -04:00
}
2021-10-12 21:21:22 -04:00
const FName & GetDataType ( ) const
{
return DataType ;
}
const FName & GetChannelName ( ) const
{
return ChannelName ;
}
uint64 GetInstanceID ( ) const
{
return InstanceID ;
}
FString ToString ( ) const
{
return FString : : Format ( TEXT ( " SendAddress {0}:{1}[Type={2}] " ) , { ChannelName . ToString ( ) , InstanceID , DataType . ToString ( ) } ) ;
}
private :
FName ChannelName ;
FName DataType ;
// For instance-specific addresses, this uint64 is the instance of this metasound.
uint64 InstanceID = INDEX_NONE ;
2020-09-08 14:13:51 -04:00
} ;
// Base class that defines shared state and utilities for senders, receivers, and the shared channel state.
class METASOUNDFRONTEND_API IDataTransmissionBase
{
public :
IDataTransmissionBase ( ) = delete ;
IDataTransmissionBase ( FName InDataType )
: DataType ( InDataType )
{
}
virtual ~ IDataTransmissionBase ( ) = default ;
template < typename TSenderImplementation >
bool CheckType ( ) const
{
FName DestinationTypeName = TSenderImplementation : : GetDataTypeName ( ) ;
return ensureAlwaysMsgf ( DataType = = DestinationTypeName , TEXT ( " Tried to downcast type %s to %s! " ) , * DataType . ToString ( ) , * DestinationTypeName . ToString ( ) ) ;
}
template < typename TSenderImplementation >
const TSenderImplementation & GetAs ( ) const
{
check ( CheckType < TSenderImplementation > ( ) ) ;
return static_cast < TSenderImplementation & > ( * this ) ;
}
template < typename TSenderImplementation >
TSenderImplementation & GetAs ( )
{
check ( CheckType < TSenderImplementation > ( ) ) ;
return static_cast < TSenderImplementation & > ( * this ) ;
}
FName GetDataType ( ) const
{
return DataType ;
}
protected :
// The data type for this sender.
FName DataType ;
} ;
class IDataChannel ;
// This is a handle to something that will retrieve a datatype.
// Intentionally opaque- to use, one will need to call IReceiver::GetAs<TReceiver<YourDataType>>().
class METASOUNDFRONTEND_API IReceiver : public IDataTransmissionBase
{
public :
2021-03-05 20:06:54 -04:00
IReceiver ( TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel , FName DataType )
2020-09-08 14:13:51 -04:00
: IDataTransmissionBase ( DataType )
, DataChannel ( InDataChannel )
{ }
virtual ~ IReceiver ( ) ;
protected :
2021-03-05 20:06:54 -04:00
TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > DataChannel ;
2020-09-08 14:13:51 -04:00
} ;
// This is a handle to something that will retrieve a datatype.
// Intentionally opaque- to use, one will need to call ISender::GetAs<TSender<YourDataType>>().
class METASOUNDFRONTEND_API ISender : public IDataTransmissionBase
{
public :
2021-03-05 20:06:54 -04:00
ISender ( TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel , FName DataType )
2020-09-08 14:13:51 -04:00
: IDataTransmissionBase ( DataType )
, DataChannel ( InDataChannel )
{ }
virtual ~ ISender ( ) ;
2021-03-23 17:55:31 -04:00
virtual int64 GetPayloadID ( ) const = 0 ;
2021-03-05 20:06:54 -04:00
// Constructs the value with a literal and pushes to data channel.
virtual bool PushLiteral ( const FLiteral & InLiteral ) = 0 ;
2020-09-08 14:13:51 -04:00
protected :
2021-03-05 20:06:54 -04:00
TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > DataChannel ;
2020-09-08 14:13:51 -04:00
} ;
struct FSenderInitParams
{
FOperatorSettings OperatorSettings ;
float DelayTimeInSeconds ;
} ;
struct FReceiverInitParams
{
FOperatorSettings OperatorSettings ;
} ;
// This contains an intermediary buffer for use between the send and receive nodes.
// Typically only used by ISender and IReceiver implementations.
2021-03-05 20:06:54 -04:00
class METASOUNDFRONTEND_API IDataChannel : public IDataTransmissionBase , public TSharedFromThis < IDataChannel , ESPMode : : ThreadSafe >
2020-09-08 14:13:51 -04:00
{
public :
virtual ~ IDataChannel ( ) = default ;
IDataChannel ( FName DataType )
: IDataTransmissionBase ( DataType )
{ }
TUniquePtr < IReceiver > NewReceiver ( const FReceiverInitParams & InitParams )
{
NumAliveReceivers . Increment ( ) ;
return ConstructNewReceiverImplementation ( InitParams ) ;
}
TUniquePtr < ISender > NewSender ( const FSenderInitParams & InitParams )
{
NumAliveSenders . Increment ( ) ;
return ConstructNewSenderImplementation ( InitParams ) ;
}
uint32 GetNumActiveReceivers ( ) const
{
return NumAliveReceivers . GetValue ( ) ;
}
uint32 GetNumActiveSenders ( ) const
{
return NumAliveSenders . GetValue ( ) ;
}
2021-07-21 15:46:42 -04:00
virtual int64 GetPayloadID ( ) const { return INDEX_NONE ; }
virtual int64 CreateNewPayloadID ( ) = 0 ;
2020-09-08 14:13:51 -04:00
virtual bool PushOpaque ( ISender & InSender ) { return true ; } ;
virtual void PopOpaque ( IReceiver & InReceiver ) { } ;
virtual bool IsEmpty ( ) const { return true ; } ;
virtual FName GetDataType ( ) { return FName ( ) ; } ;
2021-01-13 10:48:59 -04:00
virtual bool PushLiteral ( const FLiteral & InParam ) { return false ; }
2020-09-08 14:13:51 -04:00
protected :
virtual TUniquePtr < IReceiver > ConstructNewReceiverImplementation ( const FReceiverInitParams & InitParams ) { return nullptr ; } ;
virtual TUniquePtr < ISender > ConstructNewSenderImplementation ( const FSenderInitParams & InitParams ) { return nullptr ; } ;
private :
void OnSenderDestroyed ( )
{
check ( NumAliveSenders . GetValue ( ) > 0 ) ;
NumAliveSenders . Decrement ( ) ;
}
void OnReceiverDestroyed ( )
{
check ( NumAliveReceivers . GetValue ( ) > 0 ) ;
NumAliveReceivers . Decrement ( ) ;
}
friend class ISender ;
friend class IReceiver ;
FThreadSafeCounter NumAliveReceivers ;
FThreadSafeCounter NumAliveSenders ;
} ;
// Generic templates:
template < typename TDataType >
class TNonOperationalSender : public ISender
{
public :
bool Push ( const TDataType & InValue ) { return true ; }
} ;
template < typename TDataType >
class TNonOperationalReceiver : public IReceiver
{
public :
bool Pop ( TDataType & OutValue ) { return true ; }
} ;
template < typename TDataType >
class TNonOperationalDataChannel : public IDataChannel
{
public :
TNonOperationalDataChannel ( const FOperatorSettings & InOperatorSettings )
: IDataChannel ( FName ( ) )
2021-07-21 15:46:42 -04:00
, PayloadIDCounter ( 0 )
2020-09-08 14:13:51 -04:00
{ }
virtual bool PushOpaque ( ISender & InSender ) override
{
return true ;
}
virtual void PopOpaque ( IReceiver & InReceiver ) override
{
}
2021-07-21 15:46:42 -04:00
virtual int64 CreateNewPayloadID ( ) override
{
// Atomic return and post increment.
return PayloadIDCounter + + ;
}
2020-09-08 14:13:51 -04:00
virtual bool IsEmpty ( ) const override
{
return true ;
}
virtual FName GetDataType ( ) override
{
return GetDataTypeName ( ) ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
protected :
virtual TUniquePtr < IReceiver > ConstructNewReceiverImplementation ( const FReceiverInitParams & InitParams ) override
{
return TUniquePtr < IReceiver > ( ) ;
}
virtual TUniquePtr < ISender > ConstructNewSenderImplementation ( const FSenderInitParams & InitParams ) override
{
return TUniquePtr < ISender > ( ) ;
}
2021-07-21 15:46:42 -04:00
private :
std : : atomic < int64 > PayloadIDCounter ;
2020-09-08 14:13:51 -04:00
} ;
// Specializations for trivially copyable types:
template < typename TDataType >
class TSender : public ISender
{
static_assert ( std : : is_copy_constructible < TDataType > : : value , " The generic TDataChannel requires the DataType it is specialized with to be copy assignable. " ) ;
// This can be changed to change when we reallocate the internal buffer for this sender.
static constexpr float BufferResizeThreshold = 2.0f ;
2021-08-30 14:08:45 -04:00
bool bMessageDataTypeError = true ;
2020-09-08 14:13:51 -04:00
public :
bool Push ( const TDataType & InElement )
{
SenderBuffer . Push ( InElement ) ;
if ( SenderBuffer . Num ( ) > = NumElementsToDelayBy )
{
return PushToDataChannel ( ) ;
}
else
{
return true ;
}
}
2021-03-05 20:06:54 -04:00
bool PushLiteral ( const FLiteral & InLiteral ) override
{
if ( ! TLiteralTraits < TDataType > : : IsParsable ( InLiteral ) )
{
2021-08-30 14:08:45 -04:00
if ( bMessageDataTypeError )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Failed to send data. Data type [TypeName:%s] cannot be parsed from literal [Literal:%s]. " ) , * GetMetasoundDataTypeString < TDataType > ( ) , * LexToString ( InLiteral ) ) ;
bMessageDataTypeError = false ;
}
2021-03-05 20:06:54 -04:00
return false ;
}
else
{
2021-04-06 01:41:29 -04:00
return TSender < TDataType > : : Push ( TDataTypeLiteralFactory < TDataType > : : CreateExplicitArgs ( Params . OperatorSettings , InLiteral ) ) ;
2021-03-05 20:06:54 -04:00
}
}
2020-09-08 14:13:51 -04:00
// Resets the delay for this specific sender. if this goes beyond the threshold set by BufferResizeThreshold, we reallocate.
bool SetDelay ( float InSeconds )
{
Params . DelayTimeInSeconds = InSeconds ;
2021-03-05 20:06:54 -04:00
// TODO: LOL. I was wondering why the having a delay didn't change the behavior.
// Probably for the best since it does not make much sense to delay
// sent values. The FMath::Min() results in it being either 1 or 0.
//NumElementsToDelayBy = FMath::Min(InSeconds * Params.OperatorSettings.GetActualBlockRate(), 1.f);
2020-09-08 14:13:51 -04:00
2021-03-05 20:06:54 -04:00
/*
2020-09-08 14:13:51 -04:00
if ( NumElementsToDelayBy > = SenderBuffer . GetCapacity ( ) )
{
SenderBuffer . SetCapacity ( NumElementsToDelayBy * BufferResizeThreshold ) ;
}
2021-03-05 20:06:54 -04:00
*/
SenderBuffer . SetCapacity ( 2 ) ;
2021-01-22 03:05:22 -04:00
return true ;
2020-09-08 14:13:51 -04:00
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
TSender ( const FSenderInitParams & InitParams , TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel )
2020-09-08 14:13:51 -04:00
: ISender ( InDataChannel , GetDataTypeName ( ) )
, Params ( InitParams )
{
2021-01-22 03:05:22 -04:00
SetDelay ( InitParams . DelayTimeInSeconds ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-23 17:55:31 -04:00
int64 GetPayloadID ( ) const override
{
return PayloadID ;
}
2020-09-08 14:13:51 -04:00
TDataType RetrievePayload ( )
{
check ( Payload . IsValid ( ) ) ;
return MoveTempIfPossible ( * Payload ) ;
}
// Push from the SenderBuffer to the data channel.
bool PushToDataChannel ( )
{
2021-07-21 15:46:42 -04:00
PayloadID = DataChannel - > CreateNewPayloadID ( ) ;
2020-09-08 14:13:51 -04:00
if ( ! Payload . IsValid ( ) )
{
2021-03-23 17:55:31 -04:00
Payload = MakeUnique < TDataType > ( SenderBuffer . Pop ( ) ) ;
2020-09-08 14:13:51 -04:00
}
else
{
* Payload = SenderBuffer . Pop ( ) ;
}
return DataChannel - > PushOpaque ( * this ) ;
}
private :
// This buffer acts as a delay line for this sender specifically.
Audio : : TCircularAudioBuffer < TDataType > SenderBuffer ;
2021-03-23 17:55:31 -04:00
int64 PayloadID = 0 ;
2020-09-08 14:13:51 -04:00
// lazily initialized holder for payload, allocated on first call to Push.
// Placed on the heap so that we don't have to assume we know how to construct TDataType.
TUniquePtr < TDataType > Payload ;
FSenderInitParams Params ;
// The amount of pop operations before elements before this sender pushed appear.
2021-03-10 18:29:21 -04:00
uint32 NumElementsToDelayBy = 0 ;
2020-09-08 14:13:51 -04:00
} ;
template < typename TDataType >
class TReceiver : public IReceiver
{
static_assert ( std : : is_copy_constructible < TDataType > : : value , " The generic TDataChannel requires the DataType it is specialized with to be copy constructible. " ) ;
public :
bool CanPop ( ) const
{
2021-04-06 01:41:29 -04:00
if ( DataChannel - > GetPayloadID ( ) = = LastPayloadID )
{
return false ;
}
if ( DataChannel - > IsEmpty ( ) )
{
return false ;
}
return true ;
2020-09-08 14:13:51 -04:00
}
// Pop the latest value from the data channel.
bool Pop ( TDataType & OutValue )
{
DataChannel - > PopOpaque ( * this ) ;
check ( Payload . IsValid ( ) ) ;
OutValue = * Payload ;
return true ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
TReceiver ( const FReceiverInitParams & InitParams , TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel )
2020-09-08 14:13:51 -04:00
: IReceiver ( InDataChannel , GetDataTypeName ( ) )
, OperatorSettings ( InitParams . OperatorSettings )
{
2021-04-06 01:41:29 -04:00
// Executable DataTypes (currently just triggers) do not
// sync to past state provided by the data channel,
// so mark them as up-to-date.
if ( TExecutableDataType < TDataType > : : bIsExecutable )
{
LastPayloadID = InDataChannel - > GetPayloadID ( ) ;
}
2020-09-08 14:13:51 -04:00
}
2021-03-23 17:55:31 -04:00
void PushPayload ( int64 InPayloadID , const TDataType & InDataPayload )
2020-09-08 14:13:51 -04:00
{
2021-03-23 17:55:31 -04:00
// Attempting to send the same payload again, so ignore.
if ( InPayloadID = = LastPayloadID )
{
return ;
}
LastPayloadID = InPayloadID ;
2020-09-08 14:13:51 -04:00
if ( ! Payload . IsValid ( ) )
{
2021-03-23 17:55:31 -04:00
Payload = MakeUnique < TDataType > ( InDataPayload ) ;
2020-09-08 14:13:51 -04:00
}
else
{
* Payload = InDataPayload ;
}
}
private :
2021-03-23 17:55:31 -04:00
int64 LastPayloadID = INDEX_NONE ;
2020-09-08 14:13:51 -04:00
TUniquePtr < TDataType > Payload ;
FOperatorSettings OperatorSettings ;
} ;
// Generic templated implementation of IDataChannel that can use for any copyable type.
template < typename TDataType >
class TCopyableDataChannel : public IDataChannel
{
static_assert ( std : : is_copy_constructible < TDataType > : : value , " The generic TDataChannel requires the DataType it is specialized with to be copy constructible. " ) ;
public :
TCopyableDataChannel ( const FOperatorSettings & InOperatorSettings )
: IDataChannel ( GetDataTypeName ( ) )
, OperatorSettings ( InOperatorSettings )
2021-07-21 15:46:42 -04:00
, PayloadIDCounter ( 0 )
2020-09-08 14:13:51 -04:00
{ }
2021-03-23 17:55:31 -04:00
virtual int64 GetPayloadID ( ) const override
{
return PayloadID ;
}
2021-07-21 15:46:42 -04:00
virtual int64 CreateNewPayloadID ( ) override
{
// Atomic return and post increment.
return PayloadIDCounter + + ;
}
2020-09-08 14:13:51 -04:00
virtual bool PushOpaque ( ISender & InSender ) override
{
TSender < TDataType > & CastSender = InSender . GetAs < TSender < TDataType > > ( ) ;
{
2021-03-23 17:55:31 -04:00
FScopeLock ScopeLock ( & AtomicDataLock ) ;
PayloadID = InSender . GetPayloadID ( ) ;
if ( ! AtomicData . IsValid ( ) )
{
AtomicData = MakeUnique < TDataType > ( CastSender . RetrievePayload ( ) ) ;
}
else
{
* AtomicData = CastSender . RetrievePayload ( ) ;
}
2020-09-08 14:13:51 -04:00
}
return true ;
}
virtual void PopOpaque ( IReceiver & InReceiver ) override
{
TReceiver < TDataType > & CastReceiver = InReceiver . GetAs < TReceiver < TDataType > > ( ) ;
FScopeLock ScopeLock ( & AtomicDataLock ) ;
if ( AtomicData . IsValid ( ) )
{
2021-03-23 17:55:31 -04:00
CastReceiver . PushPayload ( PayloadID , * AtomicData ) ;
2020-09-08 14:13:51 -04:00
}
}
virtual bool IsEmpty ( ) const override
{
return ! AtomicData . IsValid ( ) ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
virtual FName GetDataType ( ) override
{
return GetDataTypeName ( ) ;
}
2021-01-13 10:48:59 -04:00
virtual bool PushLiteral ( const FLiteral & InParam )
2020-09-08 14:13:51 -04:00
{
2020-11-04 14:26:37 -04:00
if ( TLiteralTraits < TDataType > : : IsParsable ( InParam ) )
2020-09-08 14:13:51 -04:00
{
2020-11-04 14:26:37 -04:00
TDataType DataToPush = TDataTypeLiteralFactory < TDataType > : : CreateExplicitArgs ( OperatorSettings , InParam ) ;
2020-09-08 14:13:51 -04:00
FScopeLock ScopeLock ( & AtomicDataLock ) ;
if ( ! AtomicData . IsValid ( ) )
{
2021-03-23 17:55:31 -04:00
AtomicData = MakeUnique < TDataType > ( DataToPush ) ;
2020-09-08 14:13:51 -04:00
}
else
{
* AtomicData = DataToPush ;
}
return true ;
}
else
{
return false ;
}
}
protected :
virtual TUniquePtr < IReceiver > ConstructNewReceiverImplementation ( const FReceiverInitParams & InitParams ) override
{
return TUniquePtr < IReceiver > ( new TReceiver < TDataType > ( InitParams , this - > AsShared ( ) ) ) ;
}
virtual TUniquePtr < ISender > ConstructNewSenderImplementation ( const FSenderInitParams & InitParams ) override
{
return TUniquePtr < ISender > ( new TSender < TDataType > ( InitParams , this - > AsShared ( ) ) ) ;
}
private :
// NOTE- In the future, this could be changed to use a TCircualrAudioBuffer or a TQueue for lockless operation.
// The primary challenge with this is handling multiple senders and receivers.
// In order to support multiple senders and receivers at scale, we'd need a good implementation of a bounded MPMC queue for arbitrary datatypes.
TUniquePtr < TDataType > AtomicData ;
FCriticalSection AtomicDataLock ;
2021-07-21 15:46:42 -04:00
int64 PayloadID = INDEX_NONE ;
2021-03-23 17:55:31 -04:00
2020-09-08 14:13:51 -04:00
FOperatorSettings OperatorSettings ;
2021-07-21 15:46:42 -04:00
std : : atomic < int64 > PayloadIDCounter ;
2020-09-08 14:13:51 -04:00
} ;
// AUDIO SPECIALIZATIONS:
template < typename TDataType >
class TAudioSender : public ISender
{
static constexpr float BufferResizeThreshold = 2.0f ;
static const int32 MaxChannels = 8 ;
public :
bool Push ( const TDataType & InElement )
{
const int32 NumChannelsToPush = InElement . GetNumChannels ( ) ;
check ( NumChannelsToPush < = MaxChannels ) ;
const TArrayView < const FAudioBufferReadRef > InputBuffers = InElement . GetBuffers ( ) ;
for ( int32 ChannelIndex = 0 ; ChannelIndex < NumChannelsToPush ; ChannelIndex + + )
{
const FAudioBuffer & ChannelBuffer = * InputBuffers [ ChannelIndex ] ;
DelayLines [ ChannelIndex ] . Push ( ChannelBuffer . GetData ( ) , ChannelBuffer . Num ( ) ) ;
if ( DelayLines [ ChannelIndex ] . Num ( ) > DelayTimeInFrames )
{
2021-05-04 16:05:46 -04:00
Audio : : FAlignedFloatBuffer & IntermediateBuffer = IntermediaryBuffers [ ChannelIndex ] ;
2020-09-08 14:13:51 -04:00
DelayLines [ ChannelIndex ] . Pop ( IntermediateBuffer . GetData ( ) , IntermediateBuffer . Num ( ) ) ;
DataChannelInputs [ ChannelIndex ] . PushAudio ( IntermediateBuffer . GetData ( ) , IntermediateBuffer . Num ( ) ) ;
}
}
return true ;
}
2021-03-23 17:55:31 -04:00
int64 GetPayloadID ( ) const override
{
2021-07-21 15:46:42 -04:00
return INDEX_NONE ;
2021-03-23 17:55:31 -04:00
}
2021-03-05 20:06:54 -04:00
bool PushLiteral ( const FLiteral & InLiteral ) override
{
ensureMsgf ( false , TEXT ( " Cannot push literal to audio format " ) ) ;
return false ;
}
2020-09-08 14:13:51 -04:00
// Resets the delay for this sender.
bool SetDelay ( float InSeconds )
{
Params . DelayTimeInSeconds = InSeconds ;
DelayTimeInFrames = FMath : : Min < int32 > ( Params . DelayTimeInSeconds * Params . OperatorSettings . GetSampleRate ( ) , Params . OperatorSettings . GetNumFramesPerBlock ( ) ) ;
if ( DelayTimeInFrames > = DelayLines [ 0 ] . Num ( ) )
{
int32 NumSamplesToBuffer = DelayTimeInFrames * BufferResizeThreshold ;
DelayLines . Reset ( ) ;
IntermediaryBuffers . Reset ( ) ;
// For now, all senders/receivers/data channels will just allocate 8 delay lines, but this can be changed in the future.
for ( int32 Index = 0 ; Index < MaxChannels ; Index + + )
{
DelayLines . Emplace ( NumSamplesToBuffer ) ;
2021-05-04 16:05:46 -04:00
Audio : : FAlignedFloatBuffer BufferForChannel ;
2020-09-08 14:13:51 -04:00
BufferForChannel . AddZeroed ( Params . OperatorSettings . GetNumFramesPerBlock ( ) ) ;
IntermediaryBuffers . Add ( MoveTemp ( BufferForChannel ) ) ;
}
}
return true ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
TAudioSender ( const FSenderInitParams & InitParams , TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel , TArray < Audio : : FPatchInput > & & Inputs )
2020-09-08 14:13:51 -04:00
: ISender ( InDataChannel , GetDataTypeName ( ) )
, DataChannelInputs ( MoveTemp ( Inputs ) )
, Params ( InitParams )
{
SetDelay ( InitParams . DelayTimeInSeconds ) ;
}
private :
// This buffer acts as a delay line for this sender specifically.
TArray < Audio : : TCircularAudioBuffer < float > > DelayLines ;
uint32 DelayTimeInFrames ;
TArray < Audio : : FPatchInput > DataChannelInputs ;
2021-05-04 16:05:46 -04:00
TArray < Audio : : FAlignedFloatBuffer > IntermediaryBuffers ;
2020-09-08 14:13:51 -04:00
FSenderInitParams Params ;
} ;
template < typename TDataType >
class TAudioReceiver : public IReceiver
{
static constexpr float BufferResizeThreshold = 2.0f ;
static const int32 MaxChannels = 8 ;
public :
bool CanPop ( ) const
{
// Audio receivers can and should always pop audio,
// because we zero out audio when a datachannel underruns.
return true ;
}
bool Pop ( TDataType & OutElement )
{
const int32 NumChannelsToPop = OutElement . GetNumChannels ( ) ;
check ( NumChannelsToPop < = MaxChannels ) ;
DataChannel - > PopOpaque ( * this ) ;
TArrayView < const FAudioBufferWriteRef > OutputBuffers = OutElement . GetBuffers ( ) ;
for ( int32 ChannelIndex = 0 ; ChannelIndex < NumChannelsToPop ; ChannelIndex + + )
{
FAudioBuffer & OutBuffer = * OutputBuffers [ ChannelIndex ] ;
DataChannelOutputs [ ChannelIndex ] - > PopAudio ( OutBuffer . GetData ( ) , OutBuffer . Num ( ) , false ) ;
}
return true ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
TAudioReceiver ( const FReceiverInitParams & InitParams , TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > InDataChannel , TArray < Audio : : FPatchOutputStrongPtr > & & Outputs )
2020-09-08 14:13:51 -04:00
: IReceiver ( InDataChannel , GetDataTypeName ( ) )
, DataChannelOutputs ( MoveTemp ( Outputs ) )
, Params ( InitParams )
{
}
private :
TArray < Audio : : FPatchOutputStrongPtr > DataChannelOutputs ;
FReceiverInitParams Params ;
} ;
template < typename TDataType >
class TAudioDataChannel : public IDataChannel
{
private :
static const int32 MaxChannels = 8 ;
public :
TAudioDataChannel ( const FOperatorSettings & InOperatorSettings )
: IDataChannel ( GetDataTypeName ( ) )
2021-07-21 15:46:42 -04:00
, PayloadIDCounter ( 0 )
2020-09-08 14:13:51 -04:00
{
AudioBuses . AddDefaulted ( MaxChannels ) ;
}
virtual bool PushOpaque ( ISender & InSender ) override
{
return true ;
}
virtual void PopOpaque ( IReceiver & InReceiver ) override
{
for ( Audio : : FPatchMixerSplitter & Bus : AudioBuses )
{
Bus . ProcessAudio ( ) ;
}
}
2021-07-21 15:46:42 -04:00
virtual int64 CreateNewPayloadID ( ) override
{
// Atomic return and post increment.
return PayloadIDCounter + + ;
}
2020-09-08 14:13:51 -04:00
virtual bool IsEmpty ( ) const override
{
return false ;
}
static FName GetDataTypeName ( )
{
2021-03-05 20:06:54 -04:00
return GetMetasoundDataTypeName < TDataType > ( ) ;
2020-09-08 14:13:51 -04:00
}
virtual FName GetDataType ( ) override
{
return GetDataTypeName ( ) ;
}
protected :
virtual TUniquePtr < IReceiver > ConstructNewReceiverImplementation ( const FReceiverInitParams & InitParams ) override
{
TArray < Audio : : FPatchOutputStrongPtr > Outputs ;
for ( Audio : : FPatchMixerSplitter & Bus : AudioBuses )
{
Outputs . Add ( Bus . AddNewOutput ( 8096 , 1.0f ) ) ;
}
return TUniquePtr < IReceiver > ( new TAudioReceiver < TDataType > ( InitParams , this - > AsShared ( ) , MoveTemp ( Outputs ) ) ) ;
}
virtual TUniquePtr < ISender > ConstructNewSenderImplementation ( const FSenderInitParams & InitParams ) override
{
TArray < Audio : : FPatchInput > Inputs ;
for ( Audio : : FPatchMixerSplitter & Bus : AudioBuses )
{
Inputs . Add ( Bus . AddNewInput ( 8096 , 1.0f ) ) ;
}
return TUniquePtr < ISender > ( new TAudioSender < TDataType > ( InitParams , this - > AsShared ( ) , MoveTemp ( Inputs ) ) ) ;
}
private :
TArray < Audio : : FPatchMixerSplitter > AudioBuses ;
2021-07-21 15:46:42 -04:00
std : : atomic < int64 > PayloadIDCounter ;
2020-09-08 14:13:51 -04:00
} ;
// Coalesce both our standard copying Sender/Receivers and our audio-specific sender/receiver implementations into TSenderPtr and TReceiverPtr:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename USenderType = typename std : : conditional < TIsDerivedFrom < TDataType , IAudioDataType > : : Value , TAudioSender < TDataType > , TSender < TDataType > > : : type >
2020-09-08 14:13:51 -04:00
using TSenderPtr = TUniquePtr < USenderType > ;
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename UReceiverType = typename std : : conditional < TIsDerivedFrom < TDataType , IAudioDataType > : : Value , TAudioReceiver < TDataType > , TReceiver < TDataType > > : : type >
2020-09-08 14:13:51 -04:00
using TReceiverPtr = TUniquePtr < UReceiverType > ;
// SFINAE for creating the correct data channel type for the given datatype:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2021-03-05 20:06:54 -04:00
TSharedRef < IDataChannel , ESPMode : : ThreadSafe > MakeDataChannel ( const FOperatorSettings & InSettings )
2020-09-08 14:13:51 -04:00
{
return MakeShareable ( new TCopyableDataChannel < TDataType > ( InSettings ) ) ;
}
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2021-03-05 20:06:54 -04:00
TSharedRef < IDataChannel , ESPMode : : ThreadSafe > MakeDataChannel ( const FOperatorSettings & InSettings )
2020-09-08 14:13:51 -04:00
{
return MakeShareable ( new TAudioDataChannel < TDataType > ( InSettings ) ) ;
}
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < ! std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2021-03-05 20:06:54 -04:00
TSharedRef < IDataChannel , ESPMode : : ThreadSafe > MakeDataChannel ( const FOperatorSettings & InSettings )
2020-09-08 14:13:51 -04:00
{
return MakeShareable ( new TNonOperationalDataChannel < TDataType > ( InSettings ) ) ;
}
// Utility function for properly downcasting to the correct type based on whether TDataType is an audio stream:
// For Audio Senders:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TSenderPtr < TDataType > Downcast ( TUniquePtr < ISender > & & InPtr )
{
checkf ( InPtr - > CheckType < TAudioSender < TDataType > > ( ) , TEXT ( " Tried to downcast an ISender of type %s to a TSender of type %s! " ) , * ( InPtr - > GetDataType ( ) . ToString ( ) ) , * ( TAudioSender < TDataType > : : GetDataTypeName ( ) . ToString ( ) ) ) ;
return TSenderPtr < TDataType > ( static_cast < TAudioSender < TDataType > * > ( InPtr . Release ( ) ) ) ;
}
// For generic senders for copyable types:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TSenderPtr < TDataType > Downcast ( TUniquePtr < ISender > & & InPtr )
{
checkf ( InPtr - > CheckType < TSender < TDataType > > ( ) , TEXT ( " Tried to downcast an ISender of type %s to a TSender of type %s! " ) , * ( InPtr - > GetDataType ( ) . ToString ( ) ) , * ( TSender < TDataType > : : GetDataTypeName ( ) . ToString ( ) ) ) ;
return TSenderPtr < TDataType > ( static_cast < TSender < TDataType > * > ( InPtr . Release ( ) ) ) ;
}
// For invalid types (non-copyable AND not an audio type):
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < ! std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TSenderPtr < TDataType > Downcast ( TUniquePtr < ISender > & & InPtr )
{
return nullptr ;
}
// For audio receivers:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TReceiverPtr < TDataType > Downcast ( TUniquePtr < IReceiver > & & InPtr )
{
checkf ( InPtr - > CheckType < TAudioReceiver < TDataType > > ( ) , TEXT ( " Tried to downcast an IReceiver of type %s to a TSender of type %s! " ) , * ( InPtr - > GetDataType ( ) . ToString ( ) ) , * ( TAudioReceiver < TDataType > : : GetDataTypeName ( ) . ToString ( ) ) ) ;
return TReceiverPtr < TDataType > ( static_cast < TAudioReceiver < TDataType > * > ( InPtr . Release ( ) ) ) ;
}
// For generic receivers for copyable types:
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TReceiverPtr < TDataType > Downcast ( TUniquePtr < IReceiver > & & InPtr )
{
checkf ( InPtr - > CheckType < TReceiver < TDataType > > ( ) , TEXT ( " Tried to downcast an IReceiver of type %s to a TSender of type %s! " ) , * ( InPtr - > GetDataType ( ) . ToString ( ) ) , * ( TReceiver < TDataType > : : GetDataTypeName ( ) . ToString ( ) ) ) ;
return TReceiverPtr < TDataType > ( static_cast < TReceiver < TDataType > * > ( InPtr . Release ( ) ) ) ;
}
// For invalid types (non-copyable AND not an audio type):
2020-11-04 14:26:37 -04:00
template < typename TDataType , typename TEnableIf < ! std : : is_copy_constructible < TDataType > : : value & & ! TIsDerivedFrom < TDataType , IAudioDataType > : : Value , bool > : : Type = true >
2020-09-08 14:13:51 -04:00
TReceiverPtr < TDataType > Downcast ( TUniquePtr < IReceiver > & & InPtr )
{
return nullptr ;
}
// Basic router that takes an FName address,
class METASOUNDFRONTEND_API FAddressRouter
{
2021-03-12 00:09:21 -04:00
2020-09-08 14:13:51 -04:00
public :
2021-10-12 21:21:22 -04:00
TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > FindDataChannel ( const FSendAddress & InAddress ) ;
TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > GetDataChannel ( const FSendAddress & InAddress , const FOperatorSettings & InOperatorSettings ) ;
2021-03-05 20:06:54 -04:00
2021-10-12 21:21:22 -04:00
TUniquePtr < ISender > RegisterNewSender ( const FSendAddress & InAddress , const FSenderInitParams & InitParams ) ;
TUniquePtr < IReceiver > RegisterNewReceiver ( const FSendAddress & InAddress , const FReceiverInitParams & InitParams ) ;
2021-03-05 20:06:54 -04:00
2020-09-08 14:13:51 -04:00
template < typename TDataType >
2021-10-12 21:21:22 -04:00
TReceiverPtr < TDataType > RegisterNewReceiver ( const FSendAddress & InAddress , const FReceiverInitParams & InitParams )
2020-09-08 14:13:51 -04:00
{
2021-03-05 20:06:54 -04:00
TReceiverPtr < TDataType > Receiver ;
2020-09-08 14:13:51 -04:00
2021-10-12 21:21:22 -04:00
const bool bIsMatchingDataType = GetMetasoundDataTypeName < TDataType > ( ) = = InAddress . GetDataType ( ) ;
if ( ensure ( bIsMatchingDataType ) )
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
TUniquePtr < IReceiver > ReceiverBase = RegisterNewReceiver ( InAddress , InitParams ) ;
if ( ReceiverBase . IsValid ( ) )
{
Receiver = Downcast < TDataType > ( MoveTemp ( ReceiverBase ) ) ;
}
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
return MoveTemp ( Receiver ) ;
2020-09-08 14:13:51 -04:00
}
template < typename TDataType >
2021-10-12 21:21:22 -04:00
TSenderPtr < TDataType > RegisterNewSender ( const FSendAddress & InAddress , const FSenderInitParams & InitParams )
2020-09-08 14:13:51 -04:00
{
2021-03-05 20:06:54 -04:00
TSenderPtr < TDataType > Sender ;
2020-09-08 14:13:51 -04:00
2021-10-12 21:21:22 -04:00
const bool bIsMatchingDataType = GetMetasoundDataTypeName < TDataType > ( ) = = InAddress . GetDataType ( ) ;
if ( ensure ( bIsMatchingDataType ) )
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
TUniquePtr < ISender > SenderBase = RegisterNewSender ( InAddress , InitParams ) ;
if ( SenderBase . IsValid ( ) )
{
Sender = Downcast < TDataType > ( MoveTemp ( SenderBase ) ) ;
}
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
return MoveTemp ( Sender ) ;
2020-09-08 14:13:51 -04:00
}
2021-03-12 00:09:21 -04:00
bool PushLiteral ( const FName & InDataTypeName , const FName & InChannelName , const FLiteral & InParam )
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
FSendAddress Address { InChannelName , InDataTypeName } ;
TSharedPtr < IDataChannel , ESPMode : : ThreadSafe > Channel = FindDataChannel ( Address ) ;
2021-03-12 00:09:21 -04:00
if ( Channel . IsValid ( ) )
2020-09-08 14:13:51 -04:00
{
2021-03-12 00:09:21 -04:00
return Channel - > PushLiteral ( InParam ) ;
2020-09-08 14:13:51 -04:00
}
else
{
return false ;
}
}
2021-10-12 21:21:22 -04:00
bool UnregisterDataChannel ( const FSendAddress & InAddress ) ;
bool UnregisterDataChannelIfUnconnected ( const FSendAddress & InAddress ) ;
2021-04-06 01:41:29 -04:00
2020-09-08 14:13:51 -04:00
FAddressRouter ( const FAddressRouter & Other )
: DataChannelMap ( Other . DataChannelMap )
{
}
FAddressRouter ( )
{ }
private :
2021-03-12 00:09:21 -04:00
2021-10-12 21:21:22 -04:00
TMap < FSendAddress , TSharedRef < IDataChannel , ESPMode : : ThreadSafe > > DataChannelMap ;
2020-09-08 14:13:51 -04:00
FCriticalSection DataChannelMapMutationLock ;
} ;
// Main entry point for all sender/receiver registration.
class METASOUNDFRONTEND_API FDataTransmissionCenter
{
public :
// Returns the universal router.
static FDataTransmissionCenter & Get ( ) ;
2021-03-05 20:06:54 -04:00
// Creates a new object to push data to an address.
// Returns a new sender, or nullptr if registration failed.
2021-10-12 21:21:22 -04:00
TUniquePtr < ISender > RegisterNewSender ( const FSendAddress & InAddress , const FSenderInitParams & InitParams ) ;
2021-03-05 20:06:54 -04:00
2020-09-08 14:13:51 -04:00
// Creates a new object to push data to an address.
// Returns a new sender, or nullptr if registration failed.
template < typename TDataType >
2021-03-05 20:06:54 -04:00
TSenderPtr < TDataType > RegisterNewSender ( const FSendAddress & InAddress , const FSenderInitParams & InitParams )
2020-09-08 14:13:51 -04:00
{
2021-03-05 20:06:54 -04:00
TSenderPtr < TDataType > Sender ;
2021-10-12 21:21:22 -04:00
const bool bIsMatchingDataType = GetMetasoundDataTypeName < TDataType > ( ) = = InAddress . GetDataType ( ) ;
if ( ensure ( bIsMatchingDataType ) )
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
TUniquePtr < ISender > SenderBase = RegisterNewSender ( InAddress , InitParams ) ;
if ( SenderBase . IsValid ( ) )
{
Sender = Downcast < TDataType > ( MoveTemp ( SenderBase ) ) ;
}
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
return Sender ;
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
// Registers a new object to poll data from an address.
// Returns a new receiver, or nullptr if registration failed.
2021-10-12 21:21:22 -04:00
TUniquePtr < IReceiver > RegisterNewReceiver ( const FSendAddress & InAddress , const FReceiverInitParams & InitParams ) ;
2021-03-05 20:06:54 -04:00
2020-09-08 14:13:51 -04:00
// Registers a new object to poll data from an address.
// Returns a new receiver, or nullptr if registration failed.
template < typename TDataType >
TReceiverPtr < TDataType > RegisterNewReceiver ( const FSendAddress & InAddress , const FReceiverInitParams & InitParams )
{
2021-03-05 20:06:54 -04:00
TReceiverPtr < TDataType > Receiver ;
2020-09-08 14:13:51 -04:00
2021-10-12 21:21:22 -04:00
const bool bIsMatchingDataType = GetMetasoundDataTypeName < TDataType > ( ) = = InAddress . GetDataType ( ) ;
if ( ensure ( bIsMatchingDataType ) )
2020-09-08 14:13:51 -04:00
{
2021-10-12 21:21:22 -04:00
TUniquePtr < IReceiver > ReceiverBase = RegisterNewReceiver ( InAddress , InitParams ) ;
if ( ReceiverBase . IsValid ( ) )
{
Receiver = Downcast < TDataType > ( MoveTemp ( ReceiverBase ) ) ;
}
2020-09-08 14:13:51 -04:00
}
2021-03-05 20:06:54 -04:00
return Receiver ;
2020-09-08 14:13:51 -04:00
}
2021-04-06 01:41:29 -04:00
// Unregisters DataChannel irrespective of number of receivers or senders still active.
2021-10-12 21:21:22 -04:00
bool UnregisterDataChannel ( const FSendAddress & InAddress ) ;
2021-04-07 22:12:28 -04:00
// Unregister a data channel if there are no senders or receivers
2021-10-12 21:21:22 -04:00
bool UnregisterDataChannelIfUnconnected ( const FSendAddress & InAddress ) ;
2021-04-06 01:41:29 -04:00
2020-09-08 14:13:51 -04:00
// Pushes a literal parameter to a specific data channel in the global router.
// returns false if the literal type isn't supported.
2021-03-12 00:09:21 -04:00
bool PushLiteral ( FName DataTypeName , FName GlobalChannelName , const FLiteral & InParam ) ;
2020-09-08 14:13:51 -04:00
private :
// Single map of FNames to IDataChannels
FAddressRouter GlobalRouter ;
2021-03-05 20:06:54 -04:00
FDataTransmissionCenter ( ) = default ;
2020-09-08 14:13:51 -04:00
} ;
DECLARE_METASOUND_DATA_REFERENCE_TYPES ( FSendAddress , METASOUNDFRONTEND_API , FSendAddressTypeInfo , FSendAddressReadRef , FSendAddressWriteRef )
}