// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundWavePlayerNode.h" #include "MetasoundExecutableOperator.h" #include "MetasoundDataReferenceTypes.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundPrimitives.h" #include "MetasoundWave.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundBuildError.h" #include "IAudioCodecRegistry.h" #include "IAudioCodec.h" #define LOCTEXT_NAMESPACE "MetasoundWaveNode" namespace Metasound { METASOUND_REGISTER_NODE(FWavePlayerNode) // WavePlayer custom error class FWavePlayerError : public FBuildErrorBase { public: FWavePlayerError(FName InName, FText InErrorDescription) : FBuildErrorBase(InName, InErrorDescription) {} virtual ~FWavePlayerError() = default; }; class FWavePlayerOperator : public TExecutableOperator { public: // Silent setup FWavePlayerOperator( const FOperatorSettings& InSettings, const FWaveAssetReadRef& InWave ) : OperatorSettings(InSettings) , Wave(InWave) , AudioBuffer(FAudioBufferWriteRef::CreateNew(InSettings)) { check(AudioBuffer->Num() == InSettings.GetNumFramesPerBlock()); OutputDataReferences.AddDataReadReference(TEXT("Audio"), FAudioBufferReadRef(AudioBuffer)); } FWavePlayerOperator( const FOperatorSettings& InSettings, const FWaveAssetReadRef& InWave, FWaveAsset::FDecoderInputPtr&& InDecoderInput, TUniquePtr&& InDecoderOutput, TUniquePtr&& InDecoder ) : OperatorSettings(InSettings) , Wave(InWave) , AudioBuffer(FAudioBufferWriteRef::CreateNew(InSettings)) , Decoder(MoveTemp(InDecoder)) , DecoderInput(MoveTemp(InDecoderInput)) , DecoderOutput(MoveTemp(InDecoderOutput)) { check(AudioBuffer->Num() == InSettings.GetNumFramesPerBlock()); OutputDataReferences.AddDataReadReference(TEXT("Audio"), FAudioBufferReadRef(AudioBuffer)); } virtual const FDataReferenceCollection& GetInputs() const override { return InputDataReferences; } virtual const FDataReferenceCollection& GetOutputs() const override { return OutputDataReferences; } // Scheduler/decoder holds weakref, and pins the sharedptr to the output when it writes. // FBufferedDecoderOutput // 1 write [... .. .. ] // n free // m queued // 1 read [...........] // ApuMemory 2kb compress -> 512 frames // TCircularAudioBuffer //consider for Output object. void Execute() { int32 NumPopped = 0; float* Dst = AudioBuffer->GetData(); // If we don't have a valid state, just output silence. if (Decoder && DecoderOutput && DecoderInput ) { // V1. Do the decode inline, this will sound bad. Decoder->Decode(); Audio::IDecoderOutput::FPushedAudioDetails Details; NumPopped = DecoderOutput->PopAudio(MakeArrayView(Dst, AudioBuffer->Num()), Details); } // Pad with Silence if we didn't pop enough for ( int32 i = NumPopped; i < OperatorSettings.GetNumFramesPerBlock(); ++i) { Dst[i] = 0.0f; } } private: const FOperatorSettings OperatorSettings; FWaveAssetReadRef Wave; FAudioBufferWriteRef AudioBuffer; FDataReferenceCollection InputDataReferences; FDataReferenceCollection OutputDataReferences; // Decoder/IO. Audio::ICodec::FDecoderPtr Decoder; FWaveAsset::FDecoderInputPtr DecoderInput; TUniquePtr DecoderOutput; }; const FNodeInfo FWavePlayerNode::Info = FNodeInfo( { FName(TEXT("Wave Player")), LOCTEXT("Metasound_WavePlayerNodeDescription", "Plays a supplied Wave"), PluginAuthor, PluginNodeMissingPrompt }); TUniquePtr FWavePlayerNode::FOperatorFactory::CreateOperator( const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors) { using namespace Audio; const FWavePlayerNode& WaveNode = static_cast(InParams.Node); const FDataReferenceCollection& InputCol = InParams.InputDataReferences; FWaveAssetReadRef Wave = InputCol.GetDataReadReferenceOrConstruct(TEXT("Wave")); FWaveAsset::FDecoderInputPtr Input = FWaveAsset::CreateDecoderInput(Wave); if (Input) { ICodecRegistry::FCodecPtr Codec = ICodecRegistry::Get().FindCodecByParsingInput(Input.Get()); if (Codec) { IDecoderOutput::FRequirements Reqs { Float32_Interleaved, InParams.OperatorSettings.GetNumFramesPerBlock(), static_cast(InParams.OperatorSettings.GetSampleRate()) }; TUniquePtr Output = IDecoderOutput::Create(Reqs); TUniquePtr Decoder = Codec->CreateDecoder(Input.Get(), Output.Get()); return MakeUnique( InParams.OperatorSettings, Wave, MoveTemp(Input), MoveTemp(Output), MoveTemp(Decoder) ); } else { AddBuildError(OutErrors, TEXT("FailedToFindCodec"), LOCTEXT("FailedToFindCodec", "Failed to find codec for opening the supplied Wave")); } } else { AddBuildError(OutErrors, TEXT("FailedToParseInput"), LOCTEXT("FailedToParseInput", "Failed to parse the compressed data")); } // Create the player without any inputs, will just produce silence. return MakeUnique(InParams.OperatorSettings, Wave); } FWavePlayerNode::FWavePlayerNode(const FString& InName) : FNode(InName, FWavePlayerNode::Info) , Factory(MakeOperatorFactoryRef()) { Interface.GetInputInterface().Add(TInputDataVertexModel(TEXT("Wave"), LOCTEXT("WaveTooltip", "The Wave to be decoded"))); Interface.GetOutputInterface().Add(TOutputDataVertexModel(TEXT("Audio"), LOCTEXT("AudioTooltip", "The output audio"))); } FWavePlayerNode::FWavePlayerNode(const FNodeInitData& InInitData) : FWavePlayerNode(InInitData.InstanceName) { } FOperatorFactorySharedRef FWavePlayerNode::GetDefaultOperatorFactory() const { return Factory; } const FVertexInterface& FWavePlayerNode::GetVertexInterface() const { return Interface; } const FVertexInterface& FWavePlayerNode::GetDefaultVertexInterface() const { return Interface; } bool FWavePlayerNode::SetVertexInterface(const FVertexInterface& InInterface) { return InInterface == Interface; } bool FWavePlayerNode::IsVertexInterfaceSupported(const FVertexInterface& InInterface) const { return InInterface == Interface; } } #undef LOCTEXT_NAMESPACE //MetasoundWaveNode