2020-07-28 11:45:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundWavePlayerNode.h"
2021-01-27 15:54:01 -04:00
# include "AudioResampler.h"
# include "MetasoundBuildError.h"
2020-07-28 11:45:27 -04:00
# include "MetasoundExecutableOperator.h"
2020-08-11 01:36:57 -04:00
# include "MetasoundNodeRegistrationMacro.h"
2020-07-28 11:45:27 -04:00
# include "MetasoundPrimitives.h"
# include "MetasoundWave.h"
2021-01-27 15:54:01 -04:00
# include "MetasoundTrigger.h"
2021-02-01 21:03:37 -04:00
# include "MetasoundEngineNodesNames.h"
2021-01-27 15:54:01 -04:00
2020-07-28 11:45:27 -04:00
# define LOCTEXT_NAMESPACE "MetasoundWaveNode"
2021-01-24 20:17:50 -04:00
// static const int32 SMOOTH = -1;
// static int32 NoDiscontinuities(const float* start, int32 numframes, const float thresh = 0.3f)
// {
// for (int32 i = 0; i < numframes - 1; ++i)
// {
// float delta = FMath::Abs(start[i] - start[i + 1]);
// if (delta > thresh)
// {
// return i;
// }
// }
2021-02-01 21:03:37 -04:00
// return SMOOTH; // all good!
2021-01-24 20:17:50 -04:00
// }
2020-07-28 11:45:27 -04:00
namespace Metasound
{
2020-07-30 19:14:41 -04:00
// WavePlayer custom error
class FWavePlayerError : public FBuildErrorBase
{
public :
2020-11-04 14:26:37 -04:00
FWavePlayerError ( const FWavePlayerNode & InNode , FText InErrorDescription )
: FBuildErrorBase ( ErrorType , InErrorDescription )
{
AddNode ( InNode ) ;
}
2020-07-30 19:14:41 -04:00
virtual ~ FWavePlayerError ( ) = default ;
2020-11-04 14:26:37 -04:00
static const FName ErrorType ;
2020-07-30 19:14:41 -04:00
} ;
2020-11-04 14:26:37 -04:00
const FName FWavePlayerError : : ErrorType = FName ( TEXT ( " WavePlayerError " ) ) ;
2020-07-30 19:14:41 -04:00
2020-07-28 11:45:27 -04:00
class FWavePlayerOperator : public TExecutableOperator < FWavePlayerOperator >
{
2020-07-30 19:14:41 -04:00
public :
FWavePlayerOperator (
const FOperatorSettings & InSettings ,
2021-01-24 16:12:59 -04:00
const FWaveAssetReadRef & InWave ,
2021-03-15 22:10:18 -04:00
const FTriggerReadRef & InPlayTrigger ,
const FTriggerReadRef & InStopTrigger ,
const FTriggerReadRef & InSeekTrigger ,
const FFloatReadRef & InSeekTimeSeconds ,
const FFloatReadRef & InPitchShiftSemiTones ,
const FBoolReadRef & InLoop
)
2020-07-30 19:14:41 -04:00
: OperatorSettings ( InSettings )
2021-03-15 22:10:18 -04:00
, PlayTrig ( InPlayTrigger )
, StopTrig ( InStopTrigger )
, SeekTrig ( InSeekTrigger )
2020-07-30 19:14:41 -04:00
, Wave ( InWave )
2021-03-15 22:10:18 -04:00
, SeekTimeSeconds ( InSeekTimeSeconds )
, PitchShiftSemiTones ( InPitchShiftSemiTones )
, IsLooping ( InLoop )
2021-01-24 16:12:59 -04:00
, AudioBufferL ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
, AudioBufferR ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
2021-03-15 22:10:18 -04:00
, TrigggerOnLooped ( FTriggerWriteRef : : CreateNew ( InSettings ) )
2021-01-24 16:12:59 -04:00
, TrigggerOnDone ( FTriggerWriteRef : : CreateNew ( InSettings ) )
2021-01-25 06:10:37 -04:00
, OutputSampleRate ( InSettings . GetSampleRate ( ) )
2021-01-24 16:12:59 -04:00
, OutputBlockSizeInFrames ( InSettings . GetNumFramesPerBlock ( ) )
2020-07-30 19:14:41 -04:00
{
2021-01-25 06:10:37 -04:00
check ( OutputSampleRate ) ;
2021-01-24 16:12:59 -04:00
check ( AudioBufferL - > Num ( ) = = OutputBlockSizeInFrames & & AudioBufferR - > Num ( ) = = OutputBlockSizeInFrames ) ;
2021-03-09 21:30:41 -04:00
if ( Wave - > IsSoundWaveValid ( ) )
2021-01-24 16:12:59 -04:00
{
2021-03-09 21:30:41 -04:00
CurrentSoundWaveName = ( * Wave ) - > GetFName ( ) ;
2021-01-24 16:12:59 -04:00
}
2020-07-30 19:14:41 -04:00
}
2020-07-28 11:45:27 -04:00
2020-12-08 19:34:18 -04:00
virtual FDataReferenceCollection GetInputs ( ) const override
2020-07-30 19:14:41 -04:00
{
2020-12-08 19:34:18 -04:00
FDataReferenceCollection InputDataReferences ;
InputDataReferences . AddDataReadReference ( TEXT ( " Audio " ) , FWaveAssetReadRef ( Wave ) ) ;
2021-03-15 22:10:18 -04:00
InputDataReferences . AddDataReadReference ( TEXT ( " Play " ) , FTriggerReadRef ( PlayTrig ) ) ;
InputDataReferences . AddDataReadReference ( TEXT ( " Stop " ) , FTriggerReadRef ( StopTrig ) ) ;
InputDataReferences . AddDataReadReference ( TEXT ( " Seek " ) , FTriggerReadRef ( SeekTrig ) ) ;
InputDataReferences . AddDataReadReference ( TEXT ( " SeekTime " ) , FFloatReadRef ( SeekTimeSeconds ) ) ;
InputDataReferences . AddDataReadReference ( TEXT ( " PitchShift " ) , FFloatReadRef ( PitchShiftSemiTones ) ) ;
InputDataReferences . AddDataReadReference ( TEXT ( " Loop " ) , FBoolReadRef ( IsLooping ) ) ;
2020-07-30 19:14:41 -04:00
return InputDataReferences ;
}
2020-07-28 11:45:27 -04:00
2020-12-08 19:34:18 -04:00
virtual FDataReferenceCollection GetOutputs ( ) const override
2020-07-30 19:14:41 -04:00
{
2020-12-08 19:34:18 -04:00
FDataReferenceCollection OutputDataReferences ;
2021-01-24 16:12:59 -04:00
OutputDataReferences . AddDataReadReference ( TEXT ( " AudioLeft " ) , FAudioBufferReadRef ( AudioBufferL ) ) ;
OutputDataReferences . AddDataReadReference ( TEXT ( " AudioRight " ) , FAudioBufferReadRef ( AudioBufferR ) ) ;
2021-03-15 22:10:18 -04:00
OutputDataReferences . AddDataReadReference ( TEXT ( " Looped " ) , FTriggerReadRef ( TrigggerOnLooped ) ) ;
2021-01-24 16:12:59 -04:00
OutputDataReferences . AddDataReadReference ( TEXT ( " Done " ) , FTriggerReadRef ( TrigggerOnDone ) ) ;
2020-07-30 19:14:41 -04:00
return OutputDataReferences ;
}
2020-07-28 11:45:27 -04:00
2020-07-30 19:14:41 -04:00
void Execute ( )
{
2021-01-24 16:12:59 -04:00
TrigggerOnDone - > AdvanceBlock ( ) ;
2021-03-15 22:10:18 -04:00
TrigggerOnLooped - > AdvanceBlock ( ) ;
2020-07-30 19:14:41 -04:00
2021-01-24 16:12:59 -04:00
// see if we have a new soundwave input
FName NewSoundWaveName = FName ( ) ;
if ( Wave - > IsSoundWaveValid ( ) )
2020-07-28 11:45:27 -04:00
{
2021-03-09 21:30:41 -04:00
NewSoundWaveName = ( * Wave ) - > GetFName ( ) ;
2020-07-28 11:45:27 -04:00
}
2020-08-04 19:08:01 -04:00
2021-01-24 16:12:59 -04:00
if ( NewSoundWaveName ! = CurrentSoundWaveName )
2020-07-30 19:14:41 -04:00
{
2021-01-25 06:10:37 -04:00
ResetDecoder ( ) ;
2021-01-24 16:12:59 -04:00
CurrentSoundWaveName = NewSoundWaveName ;
}
// zero output buffers
FMemory : : Memzero ( AudioBufferL - > GetData ( ) , OutputBlockSizeInFrames * sizeof ( float ) ) ;
FMemory : : Memzero ( AudioBufferR - > GetData ( ) , OutputBlockSizeInFrames * sizeof ( float ) ) ;
2021-03-15 22:10:18 -04:00
// Parse triggers and render audio
int32 PlayTrigIndex = 0 ;
int32 NextPlayFrame = 0 ;
const int32 NumPlayTrigs = PlayTrig - > NumTriggeredInBlock ( ) ;
2021-02-01 21:03:37 -04:00
2021-03-15 22:10:18 -04:00
int32 StopTrigIndex = 0 ;
int32 NextStopFrame = 0 ;
const int32 NumStopTrigs = StopTrig - > NumTriggeredInBlock ( ) ;
2021-01-24 16:12:59 -04:00
2021-03-15 22:10:18 -04:00
int32 SeekTrigIndex = 0 ;
int32 NextSeekFrame = 0 ;
const int32 NumSeekTrigs = SeekTrig - > NumTriggeredInBlock ( ) ;
int32 CurrAudioFrame = 0 ;
int32 NextAudioFrame = 0 ;
while ( NextAudioFrame < ( OutputBlockSizeInFrames - 1 ) )
{
const int32 NoTrigger = ( OutputBlockSizeInFrames < < 1 ) ;
// get the next Play and Seek indicies
// (play)
if ( PlayTrigIndex < NumPlayTrigs )
{
NextPlayFrame = ( * PlayTrig ) [ PlayTrigIndex ] ;
2021-01-24 16:12:59 -04:00
}
2021-03-15 22:10:18 -04:00
else
{
NextPlayFrame = NoTrigger ;
}
// (stop)
if ( StopTrigIndex < NumStopTrigs )
{
NextStopFrame = ( * StopTrig ) [ StopTrigIndex ] ;
}
else
{
NextStopFrame = NoTrigger ;
}
// (seek)
if ( SeekTrigIndex < NumSeekTrigs )
{
NextSeekFrame = ( * SeekTrig ) [ SeekTrigIndex ] ;
}
else
{
NextSeekFrame = NoTrigger ;
}
// determine the next audio frame we are going to render up to
NextAudioFrame = FMath : : Min ( NextPlayFrame , NextStopFrame ) ;
NextAudioFrame = FMath : : Min ( NextAudioFrame , NextSeekFrame ) ;
// no more triggers, rendering to the end of the block
if ( NextAudioFrame = = NoTrigger )
{
NextAudioFrame = OutputBlockSizeInFrames ;
}
// render audio (while loop handles looping audio)
while ( CurrAudioFrame ! = NextAudioFrame )
{
if ( bIsPlaying )
{
CurrAudioFrame + = ExecuteInternal ( CurrAudioFrame , NextAudioFrame ) ;
}
else
{
CurrAudioFrame = NextAudioFrame ;
}
}
// execute the next trigger
if ( CurrAudioFrame = = NextSeekFrame )
{
ExecuteSeekRequest ( ) ;
+ + SeekTrigIndex ;
}
if ( CurrAudioFrame = = NextPlayFrame )
{
StartPlaying ( ) ;
+ + PlayTrigIndex ;
}
if ( CurrAudioFrame = = NextStopFrame )
{
bIsPlaying = false ;
ResetDecoder ( ) ;
2021-03-24 16:42:09 -04:00
TrigggerOnDone - > TriggerFrame ( CurrAudioFrame ) ;
2021-03-15 22:10:18 -04:00
+ + StopTrigIndex ;
}
}
}
void StartPlaying ( )
{
ResetDecoder ( ) ;
if ( ! bIsPlaying )
{
bIsPlaying = Decoder . CanGenerateAudio ( ) ;
}
}
void ExecuteSeekRequest ( )
{
// TODO: get this to work w/o full decoder reset
// Decoder.SeekToTime(FMath::Max(0.f, *SeekTimeSeconds));
// in the mean time, using this instead:
if ( ! bIsPlaying )
{
StartPlaying ( ) ;
}
2021-01-24 16:12:59 -04:00
}
2021-01-25 06:10:37 -04:00
bool ResetDecoder ( )
{
Audio : : FSimpleDecoderWrapper : : InitParams Params ;
Params . OutputBlockSizeInFrames = OutputBlockSizeInFrames ;
Params . OutputSampleRate = OutputSampleRate ;
2021-03-24 16:42:09 -04:00
Params . MaxPitchShiftMagnitudeAllowedInOctaves = 6.f ;
Params . InitialPitchShiftSemitones = * PitchShiftSemiTones ;
2021-03-15 22:10:18 -04:00
Params . StartTimeSeconds = FMath : : Max ( 0.f , * SeekTimeSeconds ) ;
2021-01-25 06:10:37 -04:00
if ( false = = Wave - > IsSoundWaveValid ( ) )
{
return false ;
}
2021-03-09 21:30:41 -04:00
return Decoder . Initialize ( Params , Wave - > GetSoundWaveProxy ( ) ) ;
2021-01-25 06:10:37 -04:00
}
2021-01-24 16:12:59 -04:00
2021-03-15 22:10:18 -04:00
int32 ExecuteInternal ( int32 StartFrame , int32 EndFrame )
2021-01-24 16:12:59 -04:00
{
2021-03-09 21:30:41 -04:00
bool bCanDecodeWave = Wave - > IsSoundWaveValid ( ) & & ( ( * Wave ) - > GetNumChannels ( ) < = 2 ) ; // only support mono or stereo inputs
2021-01-25 06:10:37 -04:00
// note: output is hard-coded to stereo (dual-mono)
2021-01-24 16:12:59 -04:00
float * FinalOutputLeft = AudioBufferL - > GetData ( ) + StartFrame ;
float * FinalOutputRight = AudioBufferR - > GetData ( ) + StartFrame ;
2021-01-25 06:10:37 -04:00
const int32 NumOutputFrames = ( EndFrame - StartFrame ) ;
2021-03-15 22:10:18 -04:00
int32 NumFramesDecoded = 0 ;
2021-01-24 16:12:59 -04:00
2021-02-10 21:43:31 -04:00
if ( bCanDecodeWave )
2021-01-24 16:12:59 -04:00
{
2021-02-10 21:43:31 -04:00
ensure ( Decoder . CanGenerateAudio ( ) ) ;
2021-01-24 16:12:59 -04:00
2021-03-09 21:30:41 -04:00
const int32 NumInputChannels = ( * Wave ) - > GetNumChannels ( ) ;
2021-02-10 21:43:31 -04:00
const bool bNeedsUpmix = ( NumInputChannels = = 1 ) ;
const bool bNeedsDeinterleave = ! bNeedsUpmix ;
const int32 NumSamplesToGenerate = NumOutputFrames * NumInputChannels ; // (stereo output)
PostSrcBuffer . Reset ( NumSamplesToGenerate ) ;
PostSrcBuffer . AddZeroed ( NumSamplesToGenerate ) ;
float * PostSrcBufferPtr = PostSrcBuffer . GetData ( ) ;
2021-03-15 22:10:18 -04:00
bool bIsLooping = * IsLooping ;
NumFramesDecoded = Decoder . GenerateAudio ( PostSrcBufferPtr , NumOutputFrames , ( * PitchShiftSemiTones * 100.f ) , bIsLooping ) / NumInputChannels ;
2021-02-10 21:43:31 -04:00
2021-03-10 17:57:58 -04:00
// handle decoder having completed during it's decode
2021-03-15 22:10:18 -04:00
const bool bReachedEOF = ! Decoder . CanGenerateAudio ( ) | | ( NumFramesDecoded < NumOutputFrames ) ;
if ( bReachedEOF & & ! bIsLooping )
2021-01-24 16:12:59 -04:00
{
2021-02-10 21:43:31 -04:00
bIsPlaying = false ;
TrigggerOnDone - > TriggerFrame ( StartFrame + NumFramesDecoded ) ;
2021-01-24 16:12:59 -04:00
}
2021-03-15 22:10:18 -04:00
else if ( bReachedEOF & & bIsLooping )
{
TrigggerOnLooped - > TriggerFrame ( StartFrame + NumFramesDecoded ) ;
}
2021-02-10 21:43:31 -04:00
if ( bNeedsUpmix )
{
// TODO: attenuate by -3 dB?
// copy to Left & Right output buffers
2021-03-01 20:25:28 -04:00
FMemory : : Memcpy ( FinalOutputLeft , PostSrcBufferPtr , sizeof ( float ) * NumFramesDecoded ) ;
FMemory : : Memcpy ( FinalOutputRight , PostSrcBufferPtr , sizeof ( float ) * NumFramesDecoded ) ;
2021-02-10 21:43:31 -04:00
}
else if ( bNeedsDeinterleave )
{
2021-03-01 20:25:28 -04:00
for ( int32 i = 0 ; i < NumFramesDecoded ; + + i )
2021-02-10 21:43:31 -04:00
{
// de-interleave each stereo frame into output buffers
FinalOutputLeft [ i ] = PostSrcBufferPtr [ ( i < < 1 ) ] ;
FinalOutputRight [ i ] = PostSrcBufferPtr [ ( i < < 1 ) + 1 ] ;
}
}
}
else
{
FMemory : : Memzero ( FinalOutputLeft , sizeof ( float ) * NumOutputFrames ) ;
FMemory : : Memzero ( FinalOutputRight , sizeof ( float ) * NumOutputFrames ) ;
2020-07-30 19:14:41 -04:00
}
2021-03-15 22:10:18 -04:00
return NumFramesDecoded ;
2020-07-30 19:14:41 -04:00
}
2020-07-28 11:45:27 -04:00
2020-07-30 19:14:41 -04:00
private :
const FOperatorSettings OperatorSettings ;
2020-07-28 11:45:27 -04:00
2021-01-24 16:12:59 -04:00
// i/o
2021-03-15 22:10:18 -04:00
FTriggerReadRef PlayTrig ;
FTriggerReadRef StopTrig ;
FTriggerReadRef SeekTrig ;
2020-09-08 18:12:55 -04:00
FWaveAssetReadRef Wave ;
2021-03-15 22:10:18 -04:00
FFloatReadRef SeekTimeSeconds ;
FFloatReadRef PitchShiftSemiTones ;
FBoolReadRef IsLooping ;
2021-01-24 16:12:59 -04:00
FAudioBufferWriteRef AudioBufferL ;
FAudioBufferWriteRef AudioBufferR ;
2021-03-15 22:10:18 -04:00
FTriggerWriteRef TrigggerOnLooped ;
2021-01-24 16:12:59 -04:00
FTriggerWriteRef TrigggerOnDone ;
2021-01-25 06:10:37 -04:00
// source decode
TArray < float > PostSrcBuffer ;
Audio : : FSimpleDecoderWrapper Decoder ;
2021-01-24 16:12:59 -04:00
FName CurrentSoundWaveName { } ;
2021-01-25 06:10:37 -04:00
2021-01-24 16:12:59 -04:00
const float OutputSampleRate { 0.f } ;
const int32 OutputBlockSizeInFrames { 0 } ;
bool bIsPlaying { false } ;
} ;
2020-07-28 11:45:27 -04:00
TUniquePtr < IOperator > FWavePlayerNode : : FOperatorFactory : : CreateOperator (
2020-08-24 10:57:03 -04:00
const FCreateOperatorParams & InParams ,
FBuildErrorArray & OutErrors )
2020-07-28 11:45:27 -04:00
{
using namespace Audio ;
2020-08-24 10:57:03 -04:00
const FWavePlayerNode & WaveNode = static_cast < const FWavePlayerNode & > ( InParams . Node ) ;
2020-07-28 11:45:27 -04:00
2021-01-24 16:12:59 -04:00
const FDataReferenceCollection & InputDataRefs = InParams . InputDataReferences ;
2020-08-24 10:57:03 -04:00
2021-03-15 22:10:18 -04:00
FTriggerReadRef TriggerPlay = InputDataRefs . GetDataReadReferenceOrConstruct < FTrigger > ( TEXT ( " Play " ) , InParams . OperatorSettings ) ;
FTriggerReadRef TriggerStop = InputDataRefs . GetDataReadReferenceOrConstruct < FTrigger > ( TEXT ( " Stop " ) , InParams . OperatorSettings ) ;
FTriggerReadRef TriggerSeek = InputDataRefs . GetDataReadReferenceOrConstruct < FTrigger > ( TEXT ( " Seek " ) , InParams . OperatorSettings ) ;
2021-01-24 16:12:59 -04:00
FWaveAssetReadRef Wave = InputDataRefs . GetDataReadReferenceOrConstruct < FWaveAsset > ( TEXT ( " Wave " ) ) ;
2021-03-15 22:10:18 -04:00
FFloatReadRef SeekTimeSeconds = InputDataRefs . GetDataReadReferenceOrConstruct < float > ( TEXT ( " SeekTime " ) , 0.f ) ;
FFloatReadRef PitchShiftSemiTones = InputDataRefs . GetDataReadReferenceOrConstruct < float > ( TEXT ( " PitchShift " ) ) ;
FBoolReadRef IsLooping = InputDataRefs . GetDataReadReferenceOrConstruct < bool > ( TEXT ( " Loop " ) , false ) ;
2021-01-24 16:12:59 -04:00
if ( ! Wave - > IsSoundWaveValid ( ) )
2020-11-04 14:26:37 -04:00
{
AddBuildError < FWavePlayerError > ( OutErrors , WaveNode , LOCTEXT ( " NoSoundWave " , " No Sound Wave " ) ) ;
2021-01-24 16:12:59 -04:00
}
2021-03-09 21:30:41 -04:00
else if ( ( * Wave ) - > GetNumChannels ( ) > 2 )
2021-01-24 16:12:59 -04:00
{
2021-03-15 22:10:18 -04:00
AddBuildError < FWavePlayerError > ( OutErrors , WaveNode , LOCTEXT ( " WavePlayerCurrentlyOnlySuportsMonoOrStereoAssets " , " Wave Player Currently Only Supports Mono Or Stereo Assets " ) ) ;
2020-11-04 14:26:37 -04:00
}
2020-07-28 11:45:27 -04:00
2021-01-24 16:12:59 -04:00
return MakeUnique < FWavePlayerOperator > (
InParams . OperatorSettings
, Wave
, TriggerPlay
2021-03-15 22:10:18 -04:00
, TriggerStop
, TriggerSeek
, SeekTimeSeconds
, PitchShiftSemiTones
, IsLooping
2021-01-24 16:12:59 -04:00
) ;
2020-07-28 11:45:27 -04:00
}
2020-11-24 15:24:29 -04:00
FVertexInterface FWavePlayerNode : : DeclareVertexInterface ( )
{
return FVertexInterface (
FInputVertexInterface (
2021-01-24 16:12:59 -04:00
TInputDataVertexModel < FWaveAsset > ( TEXT ( " Wave " ) , LOCTEXT ( " WaveTooltip " , " The Wave to be decoded " ) ) ,
2021-03-15 22:10:18 -04:00
TInputDataVertexModel < FTrigger > ( TEXT ( " Play " ) , LOCTEXT ( " PlayTooltip " , " Play the input wave. " ) ) ,
TInputDataVertexModel < FTrigger > ( TEXT ( " Stop " ) , LOCTEXT ( " StopTooltip " , " Stop the input wave. " ) ) ,
TInputDataVertexModel < FTrigger > ( TEXT ( " Seek " ) , LOCTEXT ( " PlayTooltip " , " Trigger the playing of the input wave. " ) ) ,
TInputDataVertexModel < float > ( TEXT ( " SeekTime " ) , LOCTEXT ( " SeekTimeTooltip " , " Seek time in seconds. " ) ) ,
TInputDataVertexModel < float > ( TEXT ( " PitchShift " ) , LOCTEXT ( " PitchShiftTooltip " , " Pitch Shift in semi-tones. " ) ) ,
TInputDataVertexModel < bool > ( TEXT ( " Loop " ) , LOCTEXT ( " LoopTooltip " , " Wave will loop if true " ) )
2020-11-24 15:24:29 -04:00
) ,
FOutputVertexInterface (
2021-01-24 16:12:59 -04:00
TOutputDataVertexModel < FAudioBuffer > ( TEXT ( " AudioLeft " ) , LOCTEXT ( " AudioTooltip " , " The output audio " ) ) ,
TOutputDataVertexModel < FAudioBuffer > ( TEXT ( " AudioRight " ) , LOCTEXT ( " AudioTooltip " , " The output audio " ) ) ,
2021-03-15 22:10:18 -04:00
TOutputDataVertexModel < FTrigger > ( TEXT ( " Looped " ) , LOCTEXT ( " TriggerToolTip " , " Trigger that notifies when the sound is has looped " ) ) ,
2021-01-24 16:12:59 -04:00
TOutputDataVertexModel < FTrigger > ( TEXT ( " Done " ) , LOCTEXT ( " TriggerToolTip " , " Trigger that notifies when the sound is done playing " ) )
2020-11-24 15:24:29 -04:00
)
) ;
}
2021-01-28 19:02:51 -04:00
const FNodeClassMetadata & FWavePlayerNode : : GetNodeInfo ( )
2020-11-24 15:24:29 -04:00
{
2021-01-28 19:02:51 -04:00
auto InitNodeInfo = [ ] ( ) - > FNodeClassMetadata
2020-11-24 15:24:29 -04:00
{
2021-01-28 19:02:51 -04:00
FNodeClassMetadata Info ;
2021-02-01 21:03:37 -04:00
Info . ClassName = { Metasound : : EngineNodes : : Namespace , TEXT ( " Wave Player " ) , Metasound : : EngineNodes : : StereoVariant } ;
2020-11-24 15:24:29 -04:00
Info . MajorVersion = 1 ;
Info . MinorVersion = 0 ;
2021-03-22 11:02:28 -04:00
Info . DisplayName = LOCTEXT ( " Metasound_WavePlayerNodeDisplayName " , " Wave Player " ) ;
2020-11-24 15:24:29 -04:00
Info . Description = LOCTEXT ( " Metasound_WavePlayerNodeDescription " , " Plays a supplied Wave " ) ;
Info . Author = PluginAuthor ;
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = DeclareVertexInterface ( ) ;
return Info ;
} ;
2021-01-28 19:02:51 -04:00
static const FNodeClassMetadata Info = InitNodeInfo ( ) ;
2020-11-24 15:24:29 -04:00
return Info ;
}
2021-01-28 19:02:51 -04:00
FWavePlayerNode : : FWavePlayerNode ( const FString & InName , const FGuid & InInstanceID )
2021-02-01 21:03:37 -04:00
: FNode ( InName , InInstanceID , GetNodeInfo ( ) )
, Factory ( MakeOperatorFactoryRef < FWavePlayerNode : : FOperatorFactory > ( ) )
, Interface ( DeclareVertexInterface ( ) )
2020-07-28 11:45:27 -04:00
{
}
FWavePlayerNode : : FWavePlayerNode ( const FNodeInitData & InInitData )
2021-02-01 21:03:37 -04:00
: FWavePlayerNode ( InInitData . InstanceName , InInitData . InstanceID )
2020-07-28 11:45:27 -04:00
{
}
2020-11-24 15:24:29 -04:00
FOperatorFactorySharedRef FWavePlayerNode : : GetDefaultOperatorFactory ( ) const
2020-07-28 11:45:27 -04:00
{
return Factory ;
}
2020-08-24 10:57:03 -04:00
2020-11-24 15:24:29 -04:00
const FVertexInterface & FWavePlayerNode : : GetVertexInterface ( ) const
2020-08-24 10:57:03 -04:00
{
return Interface ;
}
bool FWavePlayerNode : : SetVertexInterface ( const FVertexInterface & InInterface )
{
return InInterface = = Interface ;
}
bool FWavePlayerNode : : IsVertexInterfaceSupported ( const FVertexInterface & InInterface ) const
{
return InInterface = = Interface ;
}
2020-11-18 17:34:04 -04:00
METASOUND_REGISTER_NODE ( FWavePlayerNode )
2021-01-24 16:12:59 -04:00
2020-11-18 17:34:04 -04:00
} // namespace Metasound
# undef LOCTEXT_NAMESPACE // MetasoundWaveNode