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 ,
const FTriggerReadRef & InTrigger ,
2021-01-25 06:10:37 -04:00
const FFloatReadRef InPitchShiftCents )
2020-07-30 19:14:41 -04:00
: OperatorSettings ( InSettings )
2021-01-24 16:12:59 -04:00
, TrigIn ( InTrigger )
2020-07-30 19:14:41 -04:00
, Wave ( InWave )
2021-01-25 06:10:37 -04:00
, PitchShiftCents ( InPitchShiftCents )
2021-01-24 16:12:59 -04:00
, AudioBufferL ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
, AudioBufferR ( FAudioBufferWriteRef : : CreateNew ( InSettings ) )
, 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 ) ;
if ( Wave - > SoundWaveProxy . IsValid ( ) )
{
CurrentSoundWaveName = Wave - > SoundWaveProxy - > GetFName ( ) ;
}
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-01-27 15:54:01 -04:00
InputDataReferences . AddDataReadReference ( TEXT ( " TrigIn " ) , FTriggerReadRef ( TrigIn ) ) ;
2021-01-25 06:10:37 -04:00
InputDataReferences . AddDataReadReference ( TEXT ( " PitchShiftCents " ) , FFloatReadRef ( PitchShiftCents ) ) ;
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 ) ) ;
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 ( ) ;
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-01-24 16:12:59 -04:00
NewSoundWaveName = Wave - > SoundWaveProxy - > 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 ) ) ;
TrigIn - > ExecuteBlock (
2021-01-27 15:54:01 -04:00
// OnPreTrigger
2021-01-24 16:12:59 -04:00
[ & ] ( int32 StartFrame , int32 EndFrame )
{
if ( bIsPlaying )
{
ExecuteInternal ( StartFrame , EndFrame ) ;
}
} ,
2021-01-27 15:54:01 -04:00
// OnTrigger
2021-01-24 16:12:59 -04:00
[ & ] ( int32 StartFrame , int32 EndFrame )
{
2021-02-01 21:03:37 -04:00
ResetDecoder ( ) ;
2021-01-24 16:12:59 -04:00
if ( ! bIsPlaying )
{
2021-01-25 06:10:37 -04:00
bIsPlaying = Decoder . CanGenerateAudio ( ) ;
2021-01-24 16:12:59 -04:00
}
ExecuteInternal ( StartFrame , EndFrame ) ;
}
) ;
}
2021-01-25 06:10:37 -04:00
bool ResetDecoder ( )
{
Audio : : FSimpleDecoderWrapper : : InitParams Params ;
Params . OutputBlockSizeInFrames = OutputBlockSizeInFrames ;
Params . OutputSampleRate = OutputSampleRate ;
Params . MaxPitchShiftMagnitudeAllowedInOctaves = 4.f ;
if ( false = = Wave - > IsSoundWaveValid ( ) )
{
return false ;
}
2021-02-26 02:30:51 -04:00
return Decoder . Initialize ( Params , Wave - > SoundWaveProxy ) ;
2021-01-25 06:10:37 -04:00
}
2021-01-24 16:12:59 -04:00
void ExecuteInternal ( int32 StartFrame , int32 EndFrame )
{
2021-02-10 21:43:31 -04:00
bool bCanDecodeWave = Wave - > IsSoundWaveValid ( ) & & ( Wave - > SoundWaveProxy - > 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-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-02-10 21:43:31 -04:00
const int32 NumInputChannels = Wave - > SoundWaveProxy - > GetNumChannels ( ) ;
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 ( ) ;
int32 NumFramesDecoded = Decoder . GenerateAudio ( PostSrcBufferPtr , NumOutputFrames , * PitchShiftCents ) ; // TODO: pitch shift
// TODO: handle decoder having completed during it's decode
if ( ! Decoder . CanGenerateAudio ( ) | | ( NumFramesDecoded < NumOutputFrames ) )
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-02-10 21:43:31 -04:00
if ( bNeedsUpmix )
{
// TODO: attenuate by -3 dB?
// copy to Left & Right output buffers
FMemory : : Memcpy ( FinalOutputLeft , PostSrcBufferPtr , sizeof ( float ) * NumOutputFrames ) ;
FMemory : : Memcpy ( FinalOutputRight , PostSrcBufferPtr , sizeof ( float ) * NumOutputFrames ) ;
}
else if ( bNeedsDeinterleave )
{
for ( int32 i = 0 ; i < NumOutputFrames ; + + i )
{
// 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
}
}
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
FTriggerReadRef TrigIn ;
2020-09-08 18:12:55 -04:00
FWaveAssetReadRef Wave ;
2021-01-25 06:10:37 -04:00
FFloatReadRef PitchShiftCents ;
2021-01-24 16:12:59 -04:00
FAudioBufferWriteRef AudioBufferL ;
FAudioBufferWriteRef AudioBufferR ;
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-01-24 16:12:59 -04:00
FTriggerReadRef TriggerPlay = InputDataRefs . GetDataReadReferenceOrConstruct < FTrigger > ( TEXT ( " TrigIn " ) , InParams . OperatorSettings ) ;
FWaveAssetReadRef Wave = InputDataRefs . GetDataReadReferenceOrConstruct < FWaveAsset > ( TEXT ( " Wave " ) ) ;
2021-01-25 06:10:37 -04:00
FFloatReadRef PitchShiftCents = InputDataRefs . GetDataReadReferenceOrConstruct < float > ( TEXT ( " PitchShiftCents " ) ) ;
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
}
else if ( Wave - > SoundWaveProxy - > GetNumChannels ( ) ! = 1 )
{
AddBuildError < FWavePlayerError > ( OutErrors , WaveNode , LOCTEXT ( " WavePlayerCurrentlyOnlySuportsMonoAssets " , " Wave Player Currently Only Supports Mono 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-01-25 06:10:37 -04:00
, PitchShiftCents
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-01-25 06:10:37 -04:00
TInputDataVertexModel < FTrigger > ( TEXT ( " TrigIn " ) , LOCTEXT ( " TrigInTooltip " , " Trigger the playing of the input wave. " ) ) ,
TInputDataVertexModel < float > ( TEXT ( " PitchShiftCents " ) , LOCTEXT ( " PitchShiftCentsTooltip " , " Pitch Shift in cents. " ) )
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 " ) ) ,
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-02-01 21:03:37 -04:00
Info . DisplayName = LOCTEXT ( " Metasound_WavePlayerNodeDisplayName " , " Wave Player Node " ) ;
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