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"
2022-03-17 13:14:50 -04:00
# include "MetasoundParamHelper.h"
2021-03-18 20:04:51 -04:00
# include "MetasoundPrimitives.h"
# include "MetasoundStandardNodesNames.h"
# include "MetasoundTrigger.h"
# include "MetasoundTime.h"
# include "MetasoundAudioBuffer.h"
# include "DSP/BufferVectorOperations.h"
2022-06-15 01:43:38 -04:00
# include "DSP/FloatArrayMath.h"
2021-04-03 18:40:14 -04:00
# include "MetasoundStandardNodesCategories.h"
# include "MetasoundFacade.h"
2021-09-13 14:13:39 -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
{
2022-03-17 13:14:50 -04:00
METASOUND_PARAM ( InputAudio , " In " , " The input audio to pan. " )
METASOUND_PARAM ( InputPanAmount , " Pan Amount " , " The amount of pan. -1.0 is full left, 1.0 is full right. " )
METASOUND_PARAM ( InputPanningLaw , " Panning Law " , " Which panning law should be used for the stereo panner. " )
METASOUND_PARAM ( OutputAudioLeft , " Out Left " , " Left channel audio output. " )
METASOUND_PARAM ( OutputAudioRight , " Out Right " , " Right channel audio output. " )
2021-03-18 20:04:51 -04:00
}
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 " )
2022-02-10 18:36:47 -05:00
DEFINE_METASOUND_ENUM_ENTRY ( EPanningLaw : : EqualPower , " PanningLawEqualPowerName " , " Equal Power " , " PanningLawEqualPowerTT " , " The power of the audio signal is constant while panning. " ) ,
DEFINE_METASOUND_ENUM_ENTRY ( EPanningLaw : : Linear , " PanningLawLinearName " , " Linear " , " PanningLawLinearTT " , " The amplitude of the audio signal is constant while panning. " ) ,
2021-03-18 20:04:51 -04:00
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 ;
2022-03-17 13:14:50 -04:00
InputDataReferences . AddDataReadReference ( METASOUND_GET_PARAM_NAME ( InputAudio ) , AudioInput ) ;
InputDataReferences . AddDataReadReference ( METASOUND_GET_PARAM_NAME ( InputPanAmount ) , PanningAmount ) ;
InputDataReferences . AddDataReadReference ( METASOUND_GET_PARAM_NAME ( InputPanningLaw ) , PanningLaw ) ;
2021-03-18 20:04:51 -04:00
return InputDataReferences ;
}
FDataReferenceCollection FStereoPannerOperator : : GetOutputs ( ) const
{
using namespace StereoPannerVertexNames ;
FDataReferenceCollection OutputDataReferences ;
2022-03-17 13:14:50 -04:00
OutputDataReferences . AddDataReadReference ( METASOUND_GET_PARAM_NAME ( OutputAudioLeft ) , AudioLeftOutput ) ;
OutputDataReferences . AddDataReadReference ( METASOUND_GET_PARAM_NAME ( OutputAudioRight ) , AudioRightOutput ) ;
2021-03-18 20:04:51 -04:00
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 ( ) ;
2022-04-04 13:09:43 -04:00
TArrayView < const float > InputBufferView ( AudioInput - > GetData ( ) , InputSampleCount ) ;
TArrayView < float > OutputLeftBufferView ( AudioLeftOutput - > GetData ( ) , InputSampleCount ) ;
TArrayView < float > OutputRightBufferView ( AudioRightOutput - > GetData ( ) , InputSampleCount ) ;
2021-03-18 20:04:51 -04:00
if ( FMath : : IsNearlyEqual ( PrevPanningAmount , CurrentPanningAmount ) )
{
2022-04-04 13:09:43 -04:00
Audio : : ArrayMultiplyByConstant ( InputBufferView , PrevLeftPan , OutputLeftBufferView ) ;
Audio : : ArrayMultiplyByConstant ( InputBufferView , PrevRightPan , OutputRightBufferView ) ;
2021-03-18 20:04:51 -04:00
}
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
2022-04-04 13:09:43 -04:00
Audio : : ArrayFade ( OutputLeftBufferView , PrevLeftPan , CurrentLeftPan ) ;
Audio : : ArrayFade ( OutputRightBufferView , PrevRightPan , CurrentRightPan ) ;
2021-03-18 20:04:51 -04:00
// 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 (
2022-03-31 16:49:59 -04:00
TInputDataVertex < FAudioBuffer > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputAudio ) ) ,
TInputDataVertex < float > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputPanAmount ) , 0.0f ) ,
TInputDataVertex < FEnumPanningLaw > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputPanningLaw ) )
2021-03-18 20:04:51 -04:00
) ,
FOutputVertexInterface (
2022-03-31 16:49:59 -04:00
TOutputDataVertex < FAudioBuffer > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputAudioLeft ) ) ,
TOutputDataVertex < FAudioBuffer > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputAudioRight ) )
2021-03-18 20:04:51 -04:00
)
) ;
return Interface ;
}
const FNodeClassMetadata & FStereoPannerOperator : : GetNodeInfo ( )
{
auto InitNodeInfo = [ ] ( ) - > FNodeClassMetadata
{
FNodeClassMetadata Info ;
2021-08-09 15:08:37 -04:00
Info . ClassName = { StandardNodes : : Namespace , TEXT ( " Stereo Panner " ) , TEXT ( " " ) } ;
2021-03-18 20:04:51 -04:00
Info . MajorVersion = 1 ;
Info . MinorVersion = 0 ;
2022-02-10 18:36:47 -05:00
Info . DisplayName = METASOUND_LOCTEXT ( " Metasound_StereoPannerDisplayName " , " Stereo Panner " ) ;
Info . Description = METASOUND_LOCTEXT ( " Metasound_StereoPannerNodeDescription " , " Pans an input audio signal to left and right outputs. " ) ;
2021-03-18 20:04:51 -04:00
Info . Author = PluginAuthor ;
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = GetVertexInterface ( ) ;
2021-08-09 15:08:37 -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 ;
2022-03-17 13:14:50 -04:00
FAudioBufferReadRef AudioIn = InputCollection . GetDataReadReferenceOrConstruct < FAudioBuffer > ( METASOUND_GET_PARAM_NAME ( InputAudio ) , InParams . OperatorSettings ) ;
FFloatReadRef PanningAmount = InputCollection . GetDataReadReferenceOrConstructWithVertexDefault < float > ( InputInterface , METASOUND_GET_PARAM_NAME ( InputPanAmount ) , InParams . OperatorSettings ) ;
FPanningLawReadRef PanningLaw = InputCollection . GetDataReadReferenceOrConstruct < FEnumPanningLaw > ( METASOUND_GET_PARAM_NAME ( InputPanningLaw ) ) ;
2021-03-18 20:04:51 -04:00
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