2021-03-18 20:04:51 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Internationalization/Text.h"
2021-04-05 20:22:19 -04:00
# include "MetasoundEnumRegistrationMacro.h"
2021-03-18 20:04:51 -04:00
# include "MetasoundExecutableOperator.h"
# include "MetasoundNodeRegistrationMacro.h"
# include "MetasoundDataTypeRegistrationMacro.h"
# include "MetasoundPrimitives.h"
# include "MetasoundStandardNodesNames.h"
# include "MetasoundTrigger.h"
# include "MetasoundTime.h"
# include "MetasoundAudioBuffer.h"
# include "DSP/BufferVectorOperations.h"
2021-04-03 18:40:14 -04:00
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundFacade.h"
2021-09-13 14:14:37 -04:00
# include "MetasoundVertex.h"
2021-03-18 20:04:51 -04:00
2021-04-03 18:40:14 -04:00
# define LOCTEXT_NAMESPACE "MetasoundStandardNodes_StereoPanner"
2021-03-18 20:04:51 -04:00
namespace Metasound
{
namespace StereoPannerVertexNames
{
2021-09-13 14:14:37 -04:00
const FVertexName & GetInputAudioName ( )
2021-03-18 20:04:51 -04:00
{
2021-09-13 14:14:37 -04:00
static FVertexName Name = TEXT ( " In " ) ;
2021-03-18 20:04:51 -04:00
return Name ;
}
const FText & GetInputAudioDescription ( )
{
static FText Desc = LOCTEXT ( " StereoPannerNodeInDesc " , " The input audio to pan. " ) ;
return Desc ;
}
2021-09-13 14:14:37 -04:00
const FVertexName & GetInputPanAmountName ( )
2021-03-18 20:04:51 -04:00
{
2021-09-13 14:14:37 -04:00
static FVertexName Name = TEXT ( " Pan Amount " ) ;
2021-03-18 20:04:51 -04:00
return Name ;
}
const FText & GetInputPanAmountDescription ( )
{
static FText Desc = LOCTEXT ( " StereoPannerNodePanAmountDesc " , " The amount of pan. -1.0 is full left, 1.0 is full right. " ) ;
return Desc ;
}
2021-09-13 14:14:37 -04:00
const FVertexName & GetInputPanningLawName ( )
2021-03-18 20:04:51 -04:00
{
2021-09-13 14:14:37 -04:00
static FVertexName Name = TEXT ( " Panning Law " ) ;
2021-03-18 20:04:51 -04:00
return Name ;
}
const FText & GetInputPanningLawDescription ( )
{
static FText Desc = LOCTEXT ( " StereoPannerNodePanningLawDescription " , " Which panning law should be used for the stereo panner. " ) ;
return Desc ;
}
2021-09-13 14:14:37 -04:00
const FVertexName & GetOutputAudioLeftName ( )
2021-03-18 20:04:51 -04:00
{
2021-09-13 14:14:37 -04:00
static FVertexName Name = TEXT ( " Out Left " ) ;
2021-03-18 20:04:51 -04:00
return Name ;
}
const FText & GetOutputAudioLeftDescription ( )
{
static FText Desc = LOCTEXT ( " StereoPannerNodeOutputLeftDescription " , " Left channel audio output. " ) ;
return Desc ;
}
2021-09-13 14:14:37 -04:00
const FVertexName & GetOutputAudioRightName ( )
2021-03-18 20:04:51 -04:00
{
2021-09-13 14:14:37 -04:00
static FVertexName Name = TEXT ( " Out Right " ) ;
2021-03-18 20:04:51 -04:00
return Name ;
}
const FText & GetOutputAudioRightDescription ( )
{
static FText Desc = LOCTEXT ( " StereoPannerNodeOutputRightDescription " , " Right channel audio output. " ) ;
return Desc ;
}
}
enum class EPanningLaw
{
EqualPower = 0 ,
Linear
} ;
DECLARE_METASOUND_ENUM ( EPanningLaw , EPanningLaw : : EqualPower , METASOUNDSTANDARDNODES_API ,
FEnumPanningLaw , FEnumPanningLawInfo , FPanningLawReadRef , FPanningLawWriteRef ) ;
DEFINE_METASOUND_ENUM_BEGIN ( EPanningLaw , FEnumPanningLaw , " PanningLaw " )
DEFINE_METASOUND_ENUM_ENTRY ( EPanningLaw : : EqualPower , LOCTEXT ( " PanningLawEqualPowerName " , " Equal Power " ) , LOCTEXT ( " PanningLawEqualPowerTT " , " The power of the audio signal is constant while panning. " ) ) ,
DEFINE_METASOUND_ENUM_ENTRY ( EPanningLaw : : Linear , LOCTEXT ( " PanningLawLinearName " , " Linear " ) , LOCTEXT ( " PanningLawLinearTT " , " The amplitude of the audio signal is constant while panning. " ) ) ,
DEFINE_METASOUND_ENUM_END ( )
class FStereoPannerOperator : public TExecutableOperator < FStereoPannerOperator >
{
public :
static const FNodeClassMetadata & GetNodeInfo ( ) ;
static const FVertexInterface & GetVertexInterface ( ) ;
static TUniquePtr < IOperator > CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors ) ;
FStereoPannerOperator ( const FOperatorSettings & InSettings ,
const FAudioBufferReadRef & InAudioInput ,
const FFloatReadRef & InPanningAmount ,
const FPanningLawReadRef & InPanningLaw ) ;
virtual FDataReferenceCollection GetInputs ( ) const override ;
virtual FDataReferenceCollection GetOutputs ( ) const override ;
void Execute ( ) ;
private :
float GetInputDelayTimeMsec ( ) const ;
void ComputePanGains ( float InPanningAmmount , float & OutLeftGain , float & OutRightGain ) const ;
// The input audio buffer
FAudioBufferReadRef AudioInput ;
// The amount of delay time
FFloatReadRef PanningAmount ;
// The the dry level
FPanningLawReadRef PanningLaw ;
// The audio output
FAudioBufferWriteRef AudioLeftOutput ;
FAudioBufferWriteRef AudioRightOutput ;
float PrevPanningAmount = 0.0f ;
float PrevLeftPan = 0.0f ;
float PrevRightPan = 0.0f ;
} ;
FStereoPannerOperator : : FStereoPannerOperator ( const FOperatorSettings & InSettings ,
const FAudioBufferReadRef & InAudioInput ,
const FFloatReadRef & InPanningAmount ,
const FPanningLawReadRef & InPanningLaw )
: AudioInput ( InAudioInput )
, PanningAmount ( InPanningAmount )
, PanningLaw ( InPanningLaw )
, AudioLeftOutput ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
, AudioRightOutput ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
{
2021-03-24 16:29:25 -04:00
PrevPanningAmount = FMath : : Clamp ( * PanningAmount , - 1.0f , 1.0f ) ;
2021-03-18 20:04:51 -04:00
ComputePanGains ( PrevPanningAmount , PrevLeftPan , PrevRightPan ) ;
}
FDataReferenceCollection FStereoPannerOperator : : GetInputs ( ) const
{
using namespace StereoPannerVertexNames ;
FDataReferenceCollection InputDataReferences ;
InputDataReferences . AddDataReadReference ( GetInputAudioName ( ) , AudioInput ) ;
InputDataReferences . AddDataReadReference ( GetInputPanAmountName ( ) , PanningAmount ) ;
InputDataReferences . AddDataReadReference ( GetInputPanningLawName ( ) , PanningLaw ) ;
return InputDataReferences ;
}
FDataReferenceCollection FStereoPannerOperator : : GetOutputs ( ) const
{
using namespace StereoPannerVertexNames ;
FDataReferenceCollection OutputDataReferences ;
OutputDataReferences . AddDataReadReference ( GetOutputAudioLeftName ( ) , AudioLeftOutput ) ;
OutputDataReferences . AddDataReadReference ( GetOutputAudioRightName ( ) , AudioRightOutput ) ;
return OutputDataReferences ;
}
void FStereoPannerOperator : : ComputePanGains ( float InPanningAmmount , float & OutLeftGain , float & OutRightGain ) const
{
// Convert [-1.0, 1.0] to [0.0, 1.0]
float Fraction = 0.5f * ( InPanningAmmount + 1.0f ) ;
if ( * PanningLaw = = EPanningLaw : : EqualPower )
{
// Compute the left and right amount with one math call
FMath : : SinCos ( & OutRightGain , & OutLeftGain , 0.5f * PI * Fraction ) ;
}
else
{
OutLeftGain = Fraction ;
OutRightGain = 1.0f - Fraction ;
}
}
void FStereoPannerOperator : : Execute ( )
{
float CurrentPanningAmount = FMath : : Clamp ( * PanningAmount , - 1.0f , 1.0f ) ;
const float * InputBufferPtr = AudioInput - > GetData ( ) ;
int32 InputSampleCount = AudioInput - > Num ( ) ;
float * OutputLeftBufferPtr = AudioLeftOutput - > GetData ( ) ;
float * OutputRightBufferPtr = AudioRightOutput - > GetData ( ) ;
if ( FMath : : IsNearlyEqual ( PrevPanningAmount , CurrentPanningAmount ) )
{
Audio : : BufferMultiplyByConstant ( InputBufferPtr , PrevLeftPan , OutputLeftBufferPtr , InputSampleCount ) ;
Audio : : BufferMultiplyByConstant ( InputBufferPtr , PrevRightPan , OutputRightBufferPtr , InputSampleCount ) ;
}
else
{
// The pan amount has changed so recompute it
float CurrentLeftPan ;
float CurrentRightPan ;
ComputePanGains ( CurrentPanningAmount , CurrentLeftPan , CurrentRightPan ) ;
// Copy the input to the output buffers
FMemory : : Memcpy ( OutputLeftBufferPtr , InputBufferPtr , InputSampleCount * sizeof ( float ) ) ;
FMemory : : Memcpy ( OutputRightBufferPtr , InputBufferPtr , InputSampleCount * sizeof ( float ) ) ;
// Do a fast fade on the buffers from the prev left/right gains to current left/right gains
Audio : : FadeBufferFast ( OutputLeftBufferPtr , InputSampleCount , PrevLeftPan , CurrentLeftPan ) ;
Audio : : FadeBufferFast ( OutputRightBufferPtr , InputSampleCount , PrevRightPan , CurrentRightPan ) ;
// lerp through the buffer to the target panning amount
PrevPanningAmount = * PanningAmount ;
PrevLeftPan = CurrentLeftPan ;
PrevRightPan = CurrentRightPan ;
}
}
const FVertexInterface & FStereoPannerOperator : : GetVertexInterface ( )
{
using namespace StereoPannerVertexNames ;
static const FVertexInterface Interface (
FInputVertexInterface (
TInputDataVertexModel < FAudioBuffer > ( GetInputAudioName ( ) , GetInputAudioDescription ( ) ) ,
TInputDataVertexModel < float > ( GetInputPanAmountName ( ) , GetInputPanAmountDescription ( ) , 0.0f ) ,
TInputDataVertexModel < FEnumPanningLaw > ( GetInputPanningLawName ( ) , GetInputPanningLawDescription ( ) )
) ,
FOutputVertexInterface (
TOutputDataVertexModel < FAudioBuffer > ( GetOutputAudioLeftName ( ) , GetOutputAudioLeftDescription ( ) ) ,
TOutputDataVertexModel < FAudioBuffer > ( GetOutputAudioRightName ( ) , GetOutputAudioRightDescription ( ) )
)
) ;
return Interface ;
}
const FNodeClassMetadata & FStereoPannerOperator : : GetNodeInfo ( )
{
auto InitNodeInfo = [ ] ( ) - > FNodeClassMetadata
{
FNodeClassMetadata Info ;
2021-08-09 15:13:40 -04:00
Info . ClassName = { StandardNodes : : Namespace , TEXT ( " Stereo Panner " ) , TEXT ( " " ) } ;
2021-03-18 20:04:51 -04:00
Info . MajorVersion = 1 ;
Info . MinorVersion = 0 ;
Info . DisplayName = LOCTEXT ( " Metasound_StereoPannerDisplayName " , " Stereo Panner " ) ;
Info . Description = LOCTEXT ( " Metasound_StereoPannerNodeDescription " , " Pans an input audio signal to left and right outputs. " ) ;
Info . Author = PluginAuthor ;
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = GetVertexInterface ( ) ;
2021-08-09 15:13:40 -04:00
Info . CategoryHierarchy . Emplace ( NodeCategories : : Spatialization ) ;
2021-03-18 20:04:51 -04:00
return Info ;
} ;
static const FNodeClassMetadata Info = InitNodeInfo ( ) ;
return Info ;
}
TUniquePtr < IOperator > FStereoPannerOperator : : CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors )
{
const FDataReferenceCollection & InputCollection = InParams . InputDataReferences ;
const FInputVertexInterface & InputInterface = GetVertexInterface ( ) . GetInputInterface ( ) ;
using namespace StereoPannerVertexNames ;
FAudioBufferReadRef AudioIn = InputCollection . GetDataReadReferenceOrConstruct < FAudioBuffer > ( GetInputAudioName ( ) , InParams . OperatorSettings ) ;
2021-03-23 22:43:28 -04:00
FFloatReadRef PanningAmount = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < float > ( InputInterface , GetInputPanAmountName ( ) , InParams . OperatorSettings ) ;
2021-03-18 20:04:51 -04:00
FPanningLawReadRef PanningLaw = InputCollection . GetDataReadReferenceOrConstruct < FEnumPanningLaw > ( GetInputPanningLawName ( ) ) ;
return MakeUnique < FStereoPannerOperator > ( InParams . OperatorSettings , AudioIn , PanningAmount , PanningLaw ) ;
}
2021-04-03 18:40:14 -04:00
class FStereoPannerNode : public FNodeFacade
2021-03-18 20:04:51 -04:00
{
2021-04-03 18:40:14 -04:00
public :
/**
* Constructor used by the Metasound Frontend .
*/
FStereoPannerNode ( const FNodeInitData & InitData )
: FNodeFacade ( InitData . InstanceName , InitData . InstanceID , TFacadeOperatorClass < FStereoPannerOperator > ( ) )
{
}
} ;
2021-03-18 20:04:51 -04:00
METASOUND_REGISTER_NODE ( FStereoPannerNode )
}
# undef LOCTEXT_NAMESPACE