2021-11-18 14:37:34 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DSP/LinkwitzRileyBandSplitter.h"
# include "Internationalization/Text.h"
# include "MetasoundAudioBuffer.h"
# include "MetasoundEnumRegistrationMacro.h"
# include "MetasoundEnvelopeFollowerTypes.h"
# include "MetasoundDataTypeRegistrationMacro.h"
# include "MetasoundNodeRegistrationMacro.h"
# include "MetasoundExecutableOperator.h"
# include "MetasoundParamHelper.h"
# include "MetasoundPrimitives.h"
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundStandardNodesNames.h"
# define LOCTEXT_NAMESPACE "MetasoundStandardNodes_Bandsplitter"
namespace Metasound
{
namespace BandSplitterNode
{
METASOUND_PARAM ( InputFilterOrder , " Filter Order " , " The steepness of the crossover filters. " ) ;
METASOUND_PARAM ( InputPhaseCompensate , " Phase Compensate " , " Whether or not to phase compensate each band so they can be summed back together correctly. " ) ;
}
enum class EBandSplitterFilterOrder : int32
{
TwoPole ,
FourPole ,
SixPole ,
EightPole
} ;
DECLARE_METASOUND_ENUM ( EBandSplitterFilterOrder , EBandSplitterFilterOrder : : FourPole , METASOUNDSTANDARDNODES_API ,
FEnumBandSplitterFilterOrder , FEnumBandSplitterFilterOrderInfo , FEnumBandSplitterFilterOrderReadRef , FEnumBandSplitterFilterOrderWriteRef ) ;
DEFINE_METASOUND_ENUM_BEGIN ( EBandSplitterFilterOrder , FEnumBandSplitterFilterOrder , " Filter Order " )
2022-02-10 18:36:47 -05:00
DEFINE_METASOUND_ENUM_ENTRY ( EBandSplitterFilterOrder : : TwoPole , " BandSplitterFilterOrderTwoPoleDescription " , " Two Pole " , " EnvelopePeakModeMSDescriptionTT " , " Envelope follows a running Mean Squared of the audio signal. " ) ,
DEFINE_METASOUND_ENUM_ENTRY ( EBandSplitterFilterOrder : : FourPole , " BandSplitterFilterOrderFourPoleDescription " , " Four Pole " , " EnvelopePeakModeRMSDescriptionTT " , " Envelope follows a running Root Mean Squared of the audio signal. " ) ,
DEFINE_METASOUND_ENUM_ENTRY ( EBandSplitterFilterOrder : : SixPole , " BandSplitterFilterOrderSixPoleDescription " , " Six Pole " , " EnvelopePeakModePeakDescriptionTT " , " Envelope follows the peaks in the audio signal. " ) ,
DEFINE_METASOUND_ENUM_ENTRY ( EBandSplitterFilterOrder : : EightPole , " BandSplitterFilterOrderEightPoleDescription " , " Eight Pole " , " EnvelopePeakModePeakDescriptionTT " , " Envelope follows the peaks in the audio signal. " ) ,
2021-11-18 14:37:34 -05:00
DEFINE_METASOUND_ENUM_END ( )
template < uint32 NumBands , uint32 NumChannels >
class METASOUNDSTANDARDNODES_API TBandSplitterOperator : public TExecutableOperator < TBandSplitterOperator < NumBands , NumChannels > >
{
public :
TBandSplitterOperator (
const FOperatorSettings & InSettings
, const TArray < FAudioBufferReadRef > & & InInputBuffers
, const TArray < FFloatReadRef > & & InCrossoverFrequencies
, const FEnumBandSplitterFilterOrderReadRef & InFilterOrder
, const FBoolReadRef & InPhaseCompensate )
: AudioInputs ( InInputBuffers )
, CrossoverFrequencies ( InCrossoverFrequencies )
, FilterOrder ( InFilterOrder )
, bPhaseCompensate ( InPhaseCompensate )
, Settings ( InSettings )
{
2023-03-02 14:40:35 -05:00
// Update internal crossover buffer from passed data references
2021-11-18 14:37:34 -05:00
Crossovers . Reset ( ) ;
Crossovers . AddUninitialized ( NumBands - 1 ) ;
2023-03-02 14:40:35 -05:00
CopyClampCrossovers ( ) ;
2021-11-18 14:37:34 -05:00
bPrevPhaseCompensate = * bPhaseCompensate ;
PrevFilterOrder = GetFilterOrder ( * InFilterOrder ) ;
for ( uint32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; + + ChannelIndex )
{
MultiBandBuffers . AddDefaulted ( ) ;
Filters . AddDefaulted ( ) ;
MultiBandBuffers [ ChannelIndex ] . SetBands ( NumBands ) ;
Filters [ ChannelIndex ] . Init ( 1 , InSettings . GetSampleRate ( ) , PrevFilterOrder , bPrevPhaseCompensate , Crossovers ) ;
for ( uint32 BandIndex = 0 ; BandIndex < NumBands ; + + BandIndex )
{
AudioOutputs . Add ( FAudioBufferWriteRef : : CreateNew ( InSettings ) ) ;
}
}
}
static const FNodeClassMetadata & GetNodeInfo ( )
{
// used if NumChannels == 1
auto CreateNodeClassMetadataMono = [ ] ( ) - > FNodeClassMetadata
{
FName OperatorName = * FString : : Printf ( TEXT ( " Band Splitter (Mono, %d) " ) , NumBands ) ;
2022-02-10 18:36:47 -05:00
FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT ( " BandSplitterDisplayNamePattern1 " , " Mono Band Splitter ({0}) " , NumBands ) ;
const FText NodeDescription = METASOUND_LOCTEXT ( " BandSplitterDescription1 " , " Will split incoming audio into separate frequency bands. " ) ;
2021-11-18 14:37:34 -05:00
FVertexInterface NodeInterface = GetDefaultInterface ( ) ;
return CreateNodeClassMetadata ( OperatorName , NodeDisplayName , NodeDescription , NodeInterface ) ;
} ;
// used if NumChannels == 2
auto CreateNodeClassMetadataStereo = [ ] ( ) - > FNodeClassMetadata
{
FName OperatorName = * FString : : Printf ( TEXT ( " Band Splitter (Stereo, %d) " ) , NumBands ) ;
2022-02-10 18:36:47 -05:00
FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT ( " BandSplitterDisplayNamePattern2 " , " Stereo Band Splitter ({0}) " , NumBands ) ;
const FText NodeDescription = METASOUND_LOCTEXT ( " BandSplitterDescription2 " , " Will split incoming audio into separate frequency bands. " ) ;
2021-11-18 14:37:34 -05:00
FVertexInterface NodeInterface = GetDefaultInterface ( ) ;
return CreateNodeClassMetadata ( OperatorName , NodeDisplayName , NodeDescription , NodeInterface ) ;
} ;
// used if NumChannels > 2
auto CreateNodeClassMetadataMultiChan = [ ] ( ) - > FNodeClassMetadata
{
FName OperatorName = * FString : : Printf ( TEXT ( " Band Splitter (%d-Channel, %d) " ) , NumChannels , NumBands ) ;
2022-02-10 18:36:47 -05:00
FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT ( " BandSplitterDisplayNamePattern3 " , " {0}-channel Band Splitter ({1}) " , NumChannels , NumBands ) ;
const FText NodeDescription = METASOUND_LOCTEXT ( " BandSplitterDescription3 " , " Will split incoming audio into separate frequency bands. " ) ;
2021-11-18 14:37:34 -05:00
FVertexInterface NodeInterface = GetDefaultInterface ( ) ;
return CreateNodeClassMetadata ( OperatorName , NodeDisplayName , NodeDescription , NodeInterface ) ;
} ;
static const FNodeClassMetadata Metadata = ( NumChannels = = 1 ) ? CreateNodeClassMetadataMono ( )
: ( NumChannels = = 2 ) ? CreateNodeClassMetadataStereo ( ) : CreateNodeClassMetadataMultiChan ( ) ;
return Metadata ;
}
2023-05-22 13:28:27 -04:00
virtual void BindInputs ( FInputVertexInterfaceData & InOutVertexData ) override
2021-11-18 14:37:34 -05:00
{
using namespace BandSplitterNode ;
for ( uint32 Chan = 0 ; Chan < NumChannels ; + + Chan )
{
2023-11-07 21:33:28 -05:00
InOutVertexData . BindReadVertex ( AudioInputNames [ Chan ] , AudioInputs [ Chan ] ) ;
2021-11-18 14:37:34 -05:00
}
2023-05-22 13:28:27 -04:00
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputFilterOrder ) , FilterOrder ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputPhaseCompensate ) , bPhaseCompensate ) ;
2021-11-18 14:37:34 -05:00
for ( uint32 BandIndex = 0 ; BandIndex < NumBands - 1 ; + + BandIndex )
{
2023-11-07 21:33:28 -05:00
InOutVertexData . BindReadVertex ( CrossoverInputNames [ BandIndex ] , CrossoverFrequencies [ BandIndex ] ) ;
2021-11-18 14:37:34 -05:00
}
}
2023-05-22 13:28:27 -04:00
virtual void BindOutputs ( FOutputVertexInterfaceData & InOutVertexData ) override
2021-11-18 14:37:34 -05:00
{
for ( uint32 BandIndex = 0 ; BandIndex < NumBands ; + + BandIndex )
{
for ( uint32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; + + ChannelIndex )
{
2023-05-22 13:28:27 -04:00
InOutVertexData . BindReadVertex ( GetAudioOutputName ( BandIndex , ChannelIndex ) , AudioOutputs [ BandIndex * NumChannels + ChannelIndex ] ) ;
2021-11-18 14:37:34 -05:00
}
}
2023-05-22 13:28:27 -04:00
}
2021-11-18 14:37:34 -05:00
2023-05-22 13:28:27 -04:00
virtual FDataReferenceCollection GetInputs ( ) const override
{
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
return { } ;
}
virtual FDataReferenceCollection GetOutputs ( ) const override
{
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
return { } ;
2021-11-18 14:37:34 -05:00
}
static const FVertexInterface & GetDefaultInterface ( )
{
using namespace BandSplitterNode ;
auto CreateDefaultInterface = [ ] ( ) - > FVertexInterface
{
// inputs
FInputVertexInterface InputInterface ;
// audio channels
for ( uint32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; ChannelIndex + + )
{
2022-10-20 16:38:58 -04:00
# if WITH_EDITOR
2022-03-17 13:14:50 -04:00
const FDataVertexMetadata AudioInputMetadata
{
GetAudioInputDescription ( ChannelIndex ) ,
GetAudioInputDisplayName ( ChannelIndex )
} ;
2022-10-20 16:38:58 -04:00
# else
const FDataVertexMetadata AudioInputMetadata ;
# endif // WITH_EDITOR
2022-03-17 13:14:50 -04:00
2023-11-07 21:33:28 -05:00
InputInterface . Add ( TInputDataVertex < FAudioBuffer > ( AudioInputNames [ ChannelIndex ] , AudioInputMetadata ) ) ;
2021-11-18 14:37:34 -05:00
}
2022-03-31 16:49:59 -04:00
InputInterface . Add ( TInputDataVertex < FEnumBandSplitterFilterOrder > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputFilterOrder ) ) ) ;
InputInterface . Add ( TInputDataVertex < bool > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputPhaseCompensate ) , true ) ) ;
2021-11-18 14:37:34 -05:00
// Crossover frequencies
for ( uint32 InputIndex = 0 ; InputIndex < NumBands - 1 ; InputIndex + + )
{
2022-10-20 16:38:58 -04:00
# if WITH_EDITOR
2022-03-17 13:14:50 -04:00
const FDataVertexMetadata CrossoverInputMetadata
{
GetCrossoverInputDescription ( InputIndex ) ,
GetCrossoverInputDisplayName ( InputIndex )
} ;
2022-10-20 16:38:58 -04:00
# else
const FDataVertexMetadata CrossoverInputMetadata ;
# endif // WITH_EDITOR
2022-03-17 13:14:50 -04:00
2023-11-07 21:33:28 -05:00
InputInterface . Add ( TInputDataVertex < float > ( CrossoverInputNames [ InputIndex ] , CrossoverInputMetadata , ( 1 + InputIndex ) * 500.0f ) ) ;
2021-11-18 14:37:34 -05:00
}
// outputs
FOutputVertexInterface OutputInterface ;
for ( uint32 OutputIndex = 0 ; OutputIndex < NumBands ; OutputIndex + + )
{
for ( uint32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; ChannelIndex + + )
{
2022-10-20 16:38:58 -04:00
# if WITH_EDITOR
2022-03-17 13:14:50 -04:00
const FDataVertexMetadata AudioOutputMetadata
{
GetAudioOutputDescription ( OutputIndex , ChannelIndex ) ,
GetAudioOutputDisplayName ( OutputIndex , ChannelIndex )
} ;
2022-10-20 16:38:58 -04:00
# else
const FDataVertexMetadata AudioOutputMetadata ;
# endif // WITH_EDITOR
2022-03-17 13:14:50 -04:00
2022-03-31 16:49:59 -04:00
OutputInterface . Add ( TOutputDataVertex < FAudioBuffer > ( GetAudioOutputName ( OutputIndex , ChannelIndex ) , AudioOutputMetadata ) ) ;
2021-11-18 14:37:34 -05:00
}
}
return FVertexInterface ( InputInterface , OutputInterface ) ;
} ; // end lambda: CreateDefaultInterface()
static const FVertexInterface DefaultInterface = CreateDefaultInterface ( ) ;
return DefaultInterface ;
}
2023-10-13 19:29:51 -04:00
static TUniquePtr < IOperator > CreateOperator ( const FBuildOperatorParams & InParams , FBuildResults & OutResults )
2021-11-18 14:37:34 -05:00
{
using namespace BandSplitterNode ;
2023-10-13 19:29:51 -04:00
const FInputVertexInterfaceData & InputData = InParams . InputData ;
2021-11-18 14:37:34 -05:00
TArray < FAudioBufferReadRef > InputBuffers ;
TArray < FFloatReadRef > InputCrossovers ;
2023-10-13 19:29:51 -04:00
FEnumBandSplitterFilterOrderReadRef FilterOrderIn = InputData . GetOrCreateDefaultDataReadReference < FEnumBandSplitterFilterOrder > ( METASOUND_GET_PARAM_NAME ( InputFilterOrder ) , InParams . OperatorSettings ) ;
FBoolReadRef bPhaseCompensateIn = InputData . GetOrCreateDefaultDataReadReference < bool > ( METASOUND_GET_PARAM_NAME ( InputPhaseCompensate ) , InParams . OperatorSettings ) ;
2021-11-18 14:37:34 -05:00
for ( uint32 Chan = 0 ; Chan < NumChannels ; Chan + + )
{
2023-11-07 21:33:28 -05:00
InputBuffers . Add ( InputData . GetOrConstructDataReadReference < FAudioBuffer > ( AudioInputNames [ Chan ] , InParams . OperatorSettings ) ) ;
2021-11-18 14:37:34 -05:00
}
for ( uint32 Band = 0 ; Band < NumBands - 1 ; Band + + )
{
2023-11-07 21:33:28 -05:00
InputCrossovers . Add ( InputData . GetOrCreateDefaultDataReadReference < float > ( CrossoverInputNames [ Band ] , InParams . OperatorSettings ) ) ;
2021-11-18 14:37:34 -05:00
}
return MakeUnique < TBandSplitterOperator < NumBands , NumChannels > > ( InParams . OperatorSettings , MoveTemp ( InputBuffers ) , MoveTemp ( InputCrossovers ) , FilterOrderIn , bPhaseCompensateIn ) ;
}
void UpdateFilterSettings ( )
{
2022-09-22 17:43:34 -04:00
bool bNeedsReinitFilters = false ;
2023-03-02 14:40:35 -05:00
bool bNeedsUpdateCrossovers = CopyClampCrossovers ( ) ;
2021-11-18 14:37:34 -05:00
if ( * bPhaseCompensate ! = bPrevPhaseCompensate )
{
2022-09-22 17:43:34 -04:00
bNeedsReinitFilters = true ;
2021-11-18 14:37:34 -05:00
bPrevPhaseCompensate = * bPhaseCompensate ;
}
if ( PrevFilterOrder ! = GetFilterOrder ( * FilterOrder ) )
{
2022-09-22 17:43:34 -04:00
bNeedsReinitFilters = true ;
2021-11-18 14:37:34 -05:00
PrevFilterOrder = GetFilterOrder ( * FilterOrder ) ;
}
2022-09-22 17:43:34 -04:00
if ( bNeedsReinitFilters )
2021-11-18 14:37:34 -05:00
{
for ( int32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; ChannelIndex + + )
{
Filters [ ChannelIndex ] . Init ( 1 , Settings . GetSampleRate ( ) , PrevFilterOrder , bPrevPhaseCompensate , Crossovers ) ;
}
}
2022-09-22 17:43:34 -04:00
else if ( bNeedsUpdateCrossovers )
{
for ( int32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; ChannelIndex + + )
{
Filters [ ChannelIndex ] . SetCrossovers ( Crossovers ) ;
}
}
2021-11-18 14:37:34 -05:00
}
2023-03-02 14:40:35 -05:00
void Reset ( const IOperator : : FResetParams & InParams )
{
CopyClampCrossovers ( ) ;
bPrevPhaseCompensate = * bPhaseCompensate ;
PrevFilterOrder = GetFilterOrder ( * FilterOrder ) ;
for ( uint32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; + + ChannelIndex )
{
MultiBandBuffers [ ChannelIndex ] . Reset ( ) ;
Filters [ ChannelIndex ] . Init ( 1 , InParams . OperatorSettings . GetSampleRate ( ) , PrevFilterOrder , bPrevPhaseCompensate , Crossovers ) ;
}
for ( const TDataWriteReference < FAudioBuffer > & AudioOutput : AudioOutputs )
{
AudioOutput - > Zero ( ) ;
}
}
2021-11-18 14:37:34 -05:00
void Execute ( )
{
UpdateFilterSettings ( ) ;
for ( int32 ChannelIndex = 0 ; ChannelIndex < NumChannels ; ChannelIndex + + )
{
const int32 NumFrames = AudioInputs [ ChannelIndex ] - > Num ( ) ;
MultiBandBuffers [ ChannelIndex ] . SetSamples ( NumFrames ) ;
Filters [ ChannelIndex ] . ProcessAudioBuffer ( AudioInputs [ ChannelIndex ] - > GetData ( ) , MultiBandBuffers [ ChannelIndex ] , NumFrames ) ;
for ( int32 BandIndex = 0 ; BandIndex < NumBands ; BandIndex + + )
{
FMemory : : Memcpy ( AudioOutputs [ BandIndex * NumChannels + ChannelIndex ] - > GetData ( ) , MultiBandBuffers [ ChannelIndex ] [ BandIndex ] , NumFrames * sizeof ( float ) ) ;
}
}
}
2023-03-02 14:40:35 -05:00
2021-11-18 14:37:34 -05:00
private :
2023-03-02 14:40:35 -05:00
bool CopyClampCrossovers ( )
{
check ( Crossovers . Num ( ) = = CrossoverFrequencies . Num ( ) ) ;
bool bDidUpdate = false ;
const float MaxFrequency = 0.95f * Settings . GetSampleRate ( ) / 2.f ;
const float MinFrequency = FMath : : Min ( 20.f , MaxFrequency ) ;
const TDataReadReference < float > * InputFreqs = CrossoverFrequencies . GetData ( ) ;
float * CrossoverData = Crossovers . GetData ( ) ;
for ( int32 i = 0 ; i < CrossoverFrequencies . Num ( ) ; + + i )
{
const float NewCrossover = FMath : : Clamp ( * InputFreqs [ i ] , MinFrequency , MaxFrequency ) ;
if ( NewCrossover ! = CrossoverData [ i ] )
{
bDidUpdate = true ;
CrossoverData [ i ] = NewCrossover ;
}
}
return bDidUpdate ;
}
2023-11-07 21:33:28 -05:00
static TArray < FVertexName > InitializeAudioInputNames ( )
{
TArray < FVertexName > Names ;
Names . AddUninitialized ( NumChannels ) ;
for ( int ChanIndex = 0 ; ChanIndex < NumChannels ; + + ChanIndex )
{
if ( NumChannels = = 1 )
{
Names [ ChanIndex ] = * FString : : Printf ( TEXT ( " In " ) ) ;
}
else if ( NumChannels = = 2 )
{
Names [ ChanIndex ] = * FString : : Printf ( TEXT ( " In %s " ) , ( ChanIndex = = 0 ) ? TEXT ( " L " ) : TEXT ( " R " ) ) ;
}
else
{
Names [ ChanIndex ] = * FString : : Printf ( TEXT ( " In %i " ) , ChanIndex ) ;
}
}
return Names ;
}
static TArray < FVertexName > InitializeCrossoverInputNames ( )
{
TArray < FVertexName > Names ;
Names . AddUninitialized ( NumBands ) ;
for ( int InputIndex = 0 ; InputIndex < NumBands ; + + InputIndex )
{
Names [ InputIndex ] = * FString : : Printf ( TEXT ( " Crossover %i " ) , InputIndex ) ;
}
return Names ;
}
static TArray < FVertexName > InitializeAudioOutputNames ( )
{
TArray < FVertexName > Names ;
Names . AddUninitialized ( NumBands * NumChannels ) ;
for ( int BandIndex = 0 ; BandIndex < NumBands ; + + BandIndex )
{
for ( int ChanIndex = 0 ; ChanIndex < NumChannels ; + + ChanIndex )
{
if ( NumChannels = = 1 )
{
Names [ BandIndex * NumChannels + ChanIndex ] = * FString : : Printf ( TEXT ( " Band %i Out " ) , BandIndex ) ;
}
else if ( NumChannels = = 2 )
{
Names [ BandIndex * NumChannels + ChanIndex ] = * FString : : Printf ( TEXT ( " Band %i %s " ) , BandIndex , ( ChanIndex = = 0 ) ? TEXT ( " L " ) : TEXT ( " R " ) ) ;
}
else
{
Names [ BandIndex * NumChannels + ChanIndex ] = * FString : : Printf ( TEXT ( " Band %i Out %i " ) , BandIndex , ChanIndex ) ;
}
}
}
return Names ;
}
2021-11-18 14:37:34 -05:00
TArray < FAudioBufferReadRef > AudioInputs ;
TArray < FFloatReadRef > CrossoverFrequencies ;
TArray < float > Crossovers ;
FEnumBandSplitterFilterOrderReadRef FilterOrder ;
FBoolReadRef bPhaseCompensate ;
bool bPrevPhaseCompensate ;
Audio : : EFilterOrder PrevFilterOrder ;
TArray < FAudioBufferWriteRef > AudioOutputs ;
TArray < Audio : : FLinkwitzRileyBandSplitter > Filters ;
TArray < Audio : : FMultibandBuffer > MultiBandBuffers ;
2023-11-07 21:33:28 -05:00
static const inline TArray < FVertexName > AudioInputNames = InitializeAudioInputNames ( ) ;
static const inline TArray < FVertexName > CrossoverInputNames = InitializeCrossoverInputNames ( ) ;
static const inline TArray < FVertexName > AudioOutputNames = InitializeAudioOutputNames ( ) ;
2021-11-18 14:37:34 -05:00
FOperatorSettings Settings ;
static FNodeClassMetadata CreateNodeClassMetadata ( const FName & InOperatorName , const FText & InDisplayName , const FText & InDescription , const FVertexInterface & InDefaultInterface )
{
FNodeClassMetadata Metadata
{
FNodeClassName { " BandSplitter " , InOperatorName , FName ( ) } ,
1 , // Major Version
0 , // Minor Version
InDisplayName ,
InDescription ,
PluginAuthor ,
PluginNodeMissingPrompt ,
InDefaultInterface ,
{ NodeCategories : : Filters } ,
{ } ,
FNodeDisplayStyle { }
} ;
return Metadata ;
}
2022-10-20 16:38:58 -04:00
static const FVertexName GetAudioOutputName ( uint32 OutputIndex , uint32 ChannelIndex )
{
2023-11-07 21:33:28 -05:00
return AudioOutputNames [ OutputIndex * NumChannels + ChannelIndex ] ;
2022-10-20 16:38:58 -04:00
}
# if WITH_EDITOR
2021-11-18 14:37:34 -05:00
static const FText GetAudioInputDescription ( uint32 ChannelIndex )
{
2022-02-10 18:36:47 -05:00
return METASOUND_LOCTEXT_FORMAT ( " BandSplitterAudioInputDescription " , " Audio Channel: {0} " , ChannelIndex ) ;
2021-11-18 14:37:34 -05:00
}
2022-03-17 13:14:50 -04:00
static const FText GetAudioInputDisplayName ( uint32 ChannelIndex )
{
if ( NumChannels = = 1 )
{
return METASOUND_LOCTEXT ( " BandsplitterAudioInputIn1 " , " In " ) ;
}
else if ( NumChannels = = 2 )
{
if ( ChannelIndex = = 0 )
{
return METASOUND_LOCTEXT ( " BandsplitterAudioInputIn2L " , " In L " ) ;
}
else
{
return METASOUND_LOCTEXT ( " BandsplitterAudioInputIn2R " , " In R " ) ;
}
}
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterAudioInputIn " , " In {0} " , ChannelIndex ) ;
}
static const FText GetCrossoverInputDisplayName ( uint32 InputIndex )
{
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterCrossoverInputDisplayName " , " Crossover {0} " , InputIndex ) ;
}
2021-11-18 14:37:34 -05:00
static const FText GetCrossoverInputDescription ( uint32 InputIndex )
{
2022-02-10 18:36:47 -05:00
return METASOUND_LOCTEXT_FORMAT ( " BandSplitterCrossoverInputDescription " , " Crossover Input #: {0} " , InputIndex ) ;
2021-11-18 14:37:34 -05:00
}
static const FText GetAudioOutputDescription ( uint32 OutputIndex , uint32 ChannelIndex )
{
2022-02-10 18:36:47 -05:00
return METASOUND_LOCTEXT_FORMAT ( " BandSplitterAudioOutputDescription " , " Channel {0} Output for Band {1} " , ChannelIndex , OutputIndex ) ;
2021-11-18 14:37:34 -05:00
}
2022-03-17 13:14:50 -04:00
static const FText GetAudioOutputDisplayName ( uint32 OutputIndex , uint32 ChannelIndex )
{
if ( NumChannels = = 1 )
{
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterAudioOutputOut1 " , " Band {0} Out " , OutputIndex ) ;
}
else if ( NumChannels = = 2 )
{
if ( ChannelIndex = = 0 )
{
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterAudioOutputOut2L " , " Band {0} L " , OutputIndex ) ;
}
else
{
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterAudioOutputOut2R " , " Band {0} R " , OutputIndex ) ;
}
}
return METASOUND_LOCTEXT_FORMAT ( " BandsplitterAudioOutputOut " , " Band {0} Out {1} " , OutputIndex , ChannelIndex ) ;
}
2022-10-20 16:38:58 -04:00
# endif // WITH_EDITOR
2022-03-17 13:14:50 -04:00
2021-11-18 14:37:34 -05:00
FORCEINLINE Audio : : EFilterOrder GetFilterOrder ( EBandSplitterFilterOrder InFilterOrder ) const
{
using namespace Audio ;
switch ( InFilterOrder )
{
case EBandSplitterFilterOrder : : TwoPole :
return EFilterOrder : : TwoPole ;
break ;
case EBandSplitterFilterOrder : : FourPole :
return EFilterOrder : : FourPole ;
break ;
case EBandSplitterFilterOrder : : SixPole :
return EFilterOrder : : SixPole ;
break ;
case EBandSplitterFilterOrder : : EightPole :
return EFilterOrder : : EightPole ;
break ;
default :
ensureMsgf ( false , TEXT ( " Unhandled case converting EBandSplitterFilterOrder! " ) ) ;
break ;
}
return EFilterOrder : : TwoPole ;
}
} ;
// Node Class
template < uint32 NumBands , uint32 NumChannels >
class METASOUNDSTANDARDNODES_API TBandSplitterNode : public FNodeFacade
{
public :
TBandSplitterNode ( const FNodeInitData & InitData )
: FNodeFacade ( InitData . InstanceName , InitData . InstanceID , TFacadeOperatorClass < TBandSplitterOperator < NumBands , NumChannels > > ( ) )
{
}
} ;
# define REGISTER_BANDSPLITTER_NODE(A, B) \
using FBandSplitterNode_ # # A # # _ # # B = TBandSplitterNode < A , B > ; \
METASOUND_REGISTER_NODE ( FBandSplitterNode_ # # A # # _ # # B ) \
// monos
REGISTER_BANDSPLITTER_NODE ( 2 , 1 )
REGISTER_BANDSPLITTER_NODE ( 3 , 1 )
REGISTER_BANDSPLITTER_NODE ( 4 , 1 )
REGISTER_BANDSPLITTER_NODE ( 5 , 1 )
// stereo
REGISTER_BANDSPLITTER_NODE ( 2 , 2 )
REGISTER_BANDSPLITTER_NODE ( 3 , 2 )
REGISTER_BANDSPLITTER_NODE ( 4 , 2 )
REGISTER_BANDSPLITTER_NODE ( 5 , 2 )
} // namespace Metasound
# undef LOCTEXT_NAMESPACE