279 lines
9.3 KiB
C++
279 lines
9.3 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MetasoundCatBreakNode.h"
|
|
|
|
#include "MetasoundChannelAgnosticType.h"
|
|
#include "MetasoundExecutableOperator.h"
|
|
#include "MetasoundFacade.h"
|
|
#include "MetasoundNodeRegistrationMacro.h"
|
|
#include "MetasoundVertex.h"
|
|
#include "DSP/MultiMono.h"
|
|
#include "TypeFamily/ChannelTypeFamily.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "MetasoundStandardNodes_CatBreakNode"
|
|
|
|
namespace Metasound
|
|
{
|
|
class FChannelAgnosticType;
|
|
struct FBuildOperatorParams;
|
|
class FNodeFacade;
|
|
|
|
namespace CatBreakNodePrivate
|
|
{
|
|
METASOUND_PARAM(InputFromCat, "Input", "CAT to Cast");
|
|
METASOUND_PARAM(OutputToCat, "Output", "CAT Result");
|
|
|
|
const FLazyName InputBaseName{"In"};
|
|
|
|
FName MakeOutputVertexName(const int32 InIndex)
|
|
{
|
|
FName Name = InputBaseName;
|
|
Name.SetNumber(InIndex);
|
|
return Name;
|
|
}
|
|
|
|
TOutputDataVertex<FAudioBuffer> MakeOutputDataVertex(const int32 InIndex, const FName InName, const FString& InFriendlyName)
|
|
{
|
|
const FName Name = MakeOutputVertexName(InIndex);
|
|
|
|
const FText DisplayName = METASOUND_LOCTEXT_FORMAT("In_DisplayName", "{0}", FText::FromString(InName.ToString()));
|
|
|
|
return TOutputDataVertex<FAudioBuffer>{Name, FDataVertexMetadata{FText::FromString(*InFriendlyName), DisplayName}};
|
|
}
|
|
|
|
FVertexInterface MakeClassInterface(const FName Format)
|
|
{
|
|
const Audio::FChannelTypeFamily* Found = Audio::GetChannelRegistry().FindConcreteChannel(Format);
|
|
if (!Found)
|
|
{
|
|
return {};
|
|
}
|
|
const int32 NumChannels = Found->NumChannels();
|
|
|
|
FInputVertexInterface InputInterface;
|
|
InputInterface.Add(TInputDataVertex<FChannelAgnosticType>(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputToCat)));
|
|
|
|
FOutputVertexInterface OutputInterface;
|
|
for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ++ChannelIndex )
|
|
{
|
|
if (TOptional<Audio::FChannelTypeFamily::FChannelName> Name = Found->GetChannelName(ChannelIndex); Name.IsSet())
|
|
{
|
|
OutputInterface.Add(MakeOutputDataVertex(ChannelIndex, Name->Name, Name->FriendlyName));
|
|
}
|
|
else
|
|
{
|
|
OutputInterface.Add(MakeOutputDataVertex(ChannelIndex, TEXT("Output"), FString::FromInt(ChannelIndex) ));
|
|
}
|
|
}
|
|
return{
|
|
MoveTemp(InputInterface),
|
|
MoveTemp(OutputInterface)
|
|
};
|
|
}
|
|
|
|
class FCatBreakOperatorData final : public TOperatorData<FCatBreakOperatorData>
|
|
{
|
|
public:
|
|
// The OperatorDataTypeName is used when downcasting an IOperatorData to ensure
|
|
// that the downcast is valid.
|
|
static const FLazyName OperatorDataTypeName;
|
|
|
|
explicit FCatBreakOperatorData(const FName& InToTypeName, const Audio::EChannelTranscodeMethod InMethod, const Audio::EChannelMapMonoUpmixMethod InMixMethod)
|
|
: ToTypeName(InToTypeName)
|
|
, TranscodeMethod(InMethod)
|
|
{}
|
|
const FName& GetToType() const
|
|
{
|
|
return ToTypeName;
|
|
}
|
|
Audio::EChannelTranscodeMethod GetTranscodeMethod() const
|
|
{
|
|
return TranscodeMethod;
|
|
}
|
|
Audio::EChannelMapMonoUpmixMethod GetMixMethod() const
|
|
{
|
|
return MixMethod;
|
|
}
|
|
private:
|
|
FName ToTypeName;
|
|
Audio::EChannelTranscodeMethod TranscodeMethod = Audio::EChannelTranscodeMethod::ChannelDrop;
|
|
Audio::EChannelMapMonoUpmixMethod MixMethod = Audio::EChannelMapMonoUpmixMethod::EqualPower;
|
|
};
|
|
|
|
// Linkage.
|
|
const FLazyName FCatBreakOperatorData::OperatorDataTypeName = TEXT("FCatBreakOperatorData");
|
|
|
|
// Helper to create array of multi-mono channel pointers from a CAT.
|
|
Audio::TStackArrayOfPointers<float> MakeMultiMonoPointersFromBufferArray(const TArray<TDataWriteReference<FAudioBuffer>>& InArrayOfCat)
|
|
{
|
|
Audio::TStackArrayOfPointers<float> Result;
|
|
Result.SetNum(InArrayOfCat.Num());
|
|
for (int32 i = 0; i < InArrayOfCat.Num(); ++i)
|
|
{
|
|
Result[i] = InArrayOfCat[i]->GetData();
|
|
}
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
class FCatBreakOperator final : public TExecutableOperator<FCatBreakOperator>
|
|
{
|
|
public:
|
|
using FTranscoder = Audio::FChannelTypeFamily::FTranscoder;
|
|
FCatBreakOperator(const FBuildOperatorParams& InParams, FChannelAgnosticTypeReadRef&& InInputCat, TArray<TDataWriteReference<FAudioBuffer>>&& InOutputVerticies,
|
|
FTranscoder&& InTranscoder, const FName InFormat)
|
|
: InputCat(MoveTemp(InInputCat))
|
|
, OutputAudioVertices(MoveTemp(InOutputVerticies))
|
|
, Settings(InParams.OperatorSettings)
|
|
, Transcoder(InTranscoder)
|
|
, Format(InFormat)
|
|
{}
|
|
virtual ~FCatBreakOperator() override = default;
|
|
|
|
static const FVertexInterface& GetDefaultInterface()
|
|
{
|
|
using namespace CatBreakNodePrivate ;
|
|
auto CreateDefaultInterface = []()-> FVertexInterface
|
|
{
|
|
// inputs
|
|
FInputVertexInterface InputInterface;
|
|
InputInterface.Add(TInputDataVertex<FChannelAgnosticType>(METASOUND_GET_PARAM_NAME_AND_METADATA(InputFromCat)));
|
|
|
|
// outputs
|
|
FOutputVertexInterface OutputInterface;
|
|
|
|
return FVertexInterface(MoveTemp(InputInterface), MoveTemp(OutputInterface));
|
|
}; // end lambda: CreateDefaultInterface()
|
|
|
|
static const FVertexInterface DefaultInterface = CreateDefaultInterface();
|
|
return DefaultInterface;
|
|
}
|
|
|
|
static TUniquePtr<IOperator> CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults)
|
|
{
|
|
using namespace CatBreakNodePrivate;
|
|
|
|
const FCatBreakOperatorData* CatBreakData = CastOperatorData<const FCatBreakOperatorData>(InParams.Node.GetOperatorData().Get());
|
|
if (!CatBreakData)
|
|
{
|
|
return MakeUnique<FNoOpOperator>();
|
|
}
|
|
|
|
const FName OutputFormat = CatBreakData->GetToType();
|
|
const FInputVertexInterfaceData& InputData = InParams.InputData;
|
|
const Audio::FChannelTypeFamily* ConcreteToType = Audio::GetChannelRegistry().FindConcreteChannel(OutputFormat);
|
|
if (!ConcreteToType)
|
|
{
|
|
// Throw editor error, we don't know the format.
|
|
return MakeUnique<FNoOpOperator>();
|
|
}
|
|
|
|
// Create the input pin.
|
|
TDataReadReference<FChannelAgnosticType> InputPin = InputData.GetOrCreateDefaultDataReadReference<FChannelAgnosticType>(
|
|
METASOUND_GET_PARAM_NAME(OutputToCat), InParams.OperatorSettings);
|
|
|
|
// Create output based on format.
|
|
TArray<TDataWriteReference<FAudioBuffer>> OutputAudioVertices;
|
|
for (int32 i = 0; i < ConcreteToType->NumChannels(); ++i)
|
|
{
|
|
OutputAudioVertices.Emplace(FAudioBufferWriteRef::CreateNew(InParams.OperatorSettings));
|
|
}
|
|
|
|
// Always ask for the transcoder
|
|
// In the trivial case where we are the same format, this will be just a memcpy.
|
|
FTranscoder Transcoder = InputPin->GetType().GetTranscoder(
|
|
{
|
|
.ToType = *ConcreteToType,
|
|
.TranscodeMethod = CatBreakData->GetTranscodeMethod(),
|
|
.MixMethod = CatBreakData->GetMixMethod(),
|
|
});
|
|
|
|
return MakeUnique<FCatBreakOperator>(
|
|
InParams,
|
|
MoveTemp(InputPin),
|
|
MoveTemp(OutputAudioVertices),
|
|
MoveTemp(Transcoder),
|
|
ConcreteToType->GetName()
|
|
);
|
|
}
|
|
|
|
virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override
|
|
{
|
|
using namespace CatBreakNodePrivate;
|
|
InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputToCat),InputCat);
|
|
}
|
|
virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override
|
|
{
|
|
using namespace CatBreakNodePrivate;
|
|
for (int32 i = 0; i < OutputAudioVertices.Num(); i++)
|
|
{
|
|
InOutVertexData.BindWriteVertex(MakeOutputVertexName(i), OutputAudioVertices[i]);
|
|
}
|
|
}
|
|
|
|
void Reset(const FResetParams& InParams)
|
|
{
|
|
Execute();
|
|
}
|
|
|
|
void Execute()
|
|
{
|
|
using namespace Audio;
|
|
using namespace CatBreakNodePrivate;
|
|
if (Transcoder)
|
|
{
|
|
TStackArrayOfPointers<const float> Src = MakeMultiMonoPointersFromView(InputCat->GetRawMultiMono(), Settings.GetNumFramesPerBlock(), InputCat->NumChannels());
|
|
TStackArrayOfPointers<float> Dst = MakeMultiMonoPointersFromBufferArray(OutputAudioVertices);
|
|
Transcoder(Src, Dst, Settings.GetNumFramesPerBlock() );
|
|
}
|
|
}
|
|
|
|
static FNodeClassMetadata GetNodeInfo()
|
|
{
|
|
using namespace CatBreakNodePrivate;
|
|
return FNodeClassMetadata
|
|
{
|
|
FNodeClassName{ "Experimental", "CatBreakOperator", "" },
|
|
1, // Major version
|
|
0, // Minor version
|
|
METASOUND_LOCTEXT("CatBreakNodeName", "CAT Break Node"),
|
|
METASOUND_LOCTEXT("CatBreakNodeNameDescription", "A Node that Breaks CATs"),
|
|
TEXT("UE"), // Author
|
|
METASOUND_LOCTEXT("ExampleConfigurablePromptIfMissing", "Enable the MetaSoundExperimental Plugin"), // Prompt if missing
|
|
GetDefaultInterface(),
|
|
{}
|
|
};
|
|
}
|
|
private:
|
|
FChannelAgnosticTypeReadRef InputCat;
|
|
TArray<TDataWriteReference<FAudioBuffer>> OutputAudioVertices;
|
|
FOperatorSettings Settings;
|
|
FTranscoder Transcoder;
|
|
FName Format;
|
|
}; // class FCatCastingOperator
|
|
|
|
using FCatBreakNode = TNodeFacade<FCatBreakOperator>;
|
|
|
|
// register node config.
|
|
METASOUND_REGISTER_NODE_AND_CONFIGURATION(FCatBreakNode, FMetaSoundCatBreakNodeConfiguration);
|
|
} // namespace Metasound
|
|
|
|
|
|
TInstancedStruct<FMetasoundFrontendClassInterface> FMetaSoundCatBreakNodeConfiguration::OverrideDefaultInterface(
|
|
const FMetasoundFrontendClass& InNodeClass) const
|
|
{
|
|
using namespace Metasound::CatBreakNodePrivate;
|
|
return TInstancedStruct<FMetasoundFrontendClassInterface>::Make(FMetasoundFrontendClassInterface::GenerateClassInterface(MakeClassInterface(Format)));
|
|
}
|
|
|
|
TSharedPtr<const Metasound::IOperatorData> FMetaSoundCatBreakNodeConfiguration::GetOperatorData() const
|
|
{
|
|
return MakeShared<Metasound::CatBreakNodePrivate::FCatBreakOperatorData>(
|
|
Format,
|
|
static_cast<Audio::EChannelTranscodeMethod>(TranscodeMethod),
|
|
static_cast<Audio::EChannelMapMonoUpmixMethod>(MixMethod)
|
|
);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE //
|