296 lines
8.2 KiB
C++
296 lines
8.2 KiB
C++
#include "MixerList.h"
|
|
|
|
#include "Mixer/MixerTrack.h"
|
|
#include "PortAudioAPI.h"
|
|
#include "Thread/MainThreadEventList.h"
|
|
#include "TaskGraphInterfaces.h"
|
|
#include "AudioBuffer/ChannelNode.h"
|
|
#include "PluginHost/PluginHost.h"
|
|
#include "PluginHostList.h"
|
|
|
|
DECLARE_THREAD_MESSAGE(FMainThreadEventList, OnMixerTrackCreated,
|
|
FMixerTrack* MixerTrack;
|
|
)
|
|
{
|
|
FMixerList::Get().OnMixerTrackCreated.Broadcast(Args.MixerTrack);
|
|
}
|
|
|
|
DECLARE_THREAD_MESSAGE(FMainThreadEventList, OnMixerTrackRemoved,
|
|
FMixerTrack* MixerTrack;
|
|
)
|
|
{
|
|
FMixerList::Get().OnMixerTrackRemoved.Broadcast(Args.MixerTrack);
|
|
delete Args.MixerTrack;
|
|
}
|
|
|
|
DECLARE_THREAD_MESSAGE(FPortAudioAPI, RegisterMixerTrack,
|
|
FMixerTrack* MixerTrack;
|
|
)
|
|
{
|
|
FMixerList::Get().Add(Args.MixerTrack);
|
|
FMixerList::Get().BuildProcessNode();
|
|
PUSH_THREAD_EVENT(OnMixerTrackCreated, Args.MixerTrack);
|
|
}
|
|
|
|
DECLARE_THREAD_MESSAGE(FPortAudioAPI, RemoveMixerTrack,
|
|
FMixerTrack* MixerTrack;
|
|
)
|
|
{
|
|
FMixerList::Get().RemoveTrack_AudioThread(Args.MixerTrack);
|
|
}
|
|
|
|
DECLARE_THREAD_MESSAGE(FPortAudioAPI, BuildProcessNode,)
|
|
{
|
|
FMixerList::Get().BuildProcessNode();
|
|
}
|
|
|
|
struct FMixerTrackEffectProcessTask
|
|
{
|
|
FMixerTrackEffectProcessTask(FMixerTrack* InTrack, int32 InNumSamples) : Track(InTrack), NumSamples(InNumSamples)
|
|
{ }
|
|
|
|
FMixerTrack* Track;
|
|
int32 NumSamples;
|
|
|
|
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& CompletionGraphEvent)
|
|
{
|
|
Track->Process(NumSamples);
|
|
}
|
|
|
|
FORCEINLINE TStatId GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FGenerateWaveformRangeTask, STATGROUP_ThreadPoolAsyncTasks);
|
|
}
|
|
|
|
static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyThread; }
|
|
|
|
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
|
|
};
|
|
|
|
|
|
void FMixerList::Init()
|
|
{
|
|
ZeroTrack = new FDummyMixerTrack();
|
|
ZeroTrack->Rename("Zero");
|
|
ZeroTrack->Init();
|
|
|
|
FDummyMixerTrack* MixerTrack = new FDummyMixerTrack();
|
|
MixerTrack->Rename("Master");
|
|
MixerTrack->Init();
|
|
Add(MixerTrack); // Master
|
|
}
|
|
|
|
void FMixerList::Release()
|
|
{
|
|
for (FMixerTrack*& Track : *this)
|
|
{
|
|
OnMixerTrackRemoved.Broadcast(Track);
|
|
delete Track;
|
|
}
|
|
Empty();
|
|
delete ZeroTrack;
|
|
}
|
|
|
|
FDummyMixerTrack* FMixerList::CreateDummyTrack(const FString& Name)
|
|
{
|
|
FDummyMixerTrack* Track = new FDummyMixerTrack();
|
|
Track->Init();
|
|
Track->Rename(Name);
|
|
FMixerTrackLink& Link = GetMaster()->Children.AddDefaulted_GetRef();
|
|
Link.Track = Track;
|
|
Link.Gain = 1.f;
|
|
// Add(Track);
|
|
PUSH_THREAD_EVENT(RegisterMixerTrack, Track);
|
|
return Track;
|
|
}
|
|
|
|
FInstrumentMixerTrack* FMixerList::CreateInstrumentTrack(FPluginHost* Host)
|
|
{
|
|
FInstrumentMixerTrack* Track = new FInstrumentMixerTrack(Host);
|
|
Track->Init();
|
|
FMixerTrackLink& Link = GetMaster()->Children.AddDefaulted_GetRef();
|
|
Link.Track = Track;
|
|
Link.Gain = 1.f;
|
|
// Add(Track);
|
|
PUSH_THREAD_EVENT(RegisterMixerTrack, Track);
|
|
return Track;
|
|
}
|
|
|
|
void FMixerList::RemoveTrack(FMixerTrack* Track)
|
|
{
|
|
PUSH_THREAD_EVENT(RemoveMixerTrack, Track);
|
|
}
|
|
|
|
void FMixerList::RemoveTrack(FPluginHost* Instrument)
|
|
{
|
|
for (FMixerTrack* Track : *this)
|
|
{
|
|
if (Track->Type == EMixerTrackType::Instrument)
|
|
{
|
|
FInstrumentMixerTrack* InstrumentTrack = static_cast<FInstrumentMixerTrack*>(Track);
|
|
if (InstrumentTrack->GetHost() == Instrument)
|
|
{
|
|
RemoveTrack(Track);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMixerList::RemoveTrack_AudioThread(FMixerTrack* InTrack)
|
|
{
|
|
Remove(InTrack);
|
|
for (FMixerTrack* Track : FMixerList::Get())
|
|
{
|
|
Track->RemoveChild(Track);
|
|
}
|
|
GetMaster()->RemoveChild(InTrack);
|
|
|
|
for (const FPluginHost* Plugin : FPluginHostList::Get())
|
|
{
|
|
Plugin->ChannelInterface->RemoveTrack(InTrack);
|
|
}
|
|
BuildProcessNode();
|
|
PUSH_THREAD_EVENT(OnMixerTrackRemoved, InTrack);
|
|
}
|
|
|
|
void FMixerList::Process(int32 NumSamples)
|
|
{
|
|
for (const auto& Layer : LayerTracks)
|
|
{
|
|
FGraphEventArray Futures;
|
|
for (FMixerTrack* Track : Layer.Value)
|
|
{
|
|
FGraphEventRef BuildTask = TGraphTask<FMixerTrackEffectProcessTask>::CreateTask(nullptr).ConstructAndDispatchWhenReady(Track, NumSamples);
|
|
Futures.Add(BuildTask);
|
|
}
|
|
const FGraphEventRef NullTask = TGraphTask<FNullGraphTask>::CreateTask(&Futures).ConstructAndDispatchWhenReady(TStatId(), ENamedThreads::Type::AnyThread);
|
|
FTaskGraphInterface::Get().WaitUntilTaskCompletes(NullTask);
|
|
}
|
|
|
|
FMixerTrack* Master = GetMaster();
|
|
TArray<FMixerTrack*> ProcessedTracks;
|
|
ProcessInternal(Master, NumSamples, ProcessedTracks);
|
|
Master->Buffer.Multiple(Master->Gain);
|
|
}
|
|
|
|
void FMixerList::ResetAllTracks()
|
|
{
|
|
for (FMixerTrack* Track : *this)
|
|
Track->Buffer.ZeroBuffer();
|
|
}
|
|
|
|
void FMixerList::BuildProcessNode()
|
|
{
|
|
check(FPortAudioAPI::IsInAudioThread());
|
|
if (Num() == 0) // 没有任何轨道
|
|
return;
|
|
|
|
TMap<FMixerTrack*, int32> ProcessedTracks;
|
|
FMixerTrack* Master = GetMaster();
|
|
BuildProcessNodeInternal(Master, ProcessedTracks, 0);
|
|
for (FPluginHost* PluginHost : FPluginHostList::Get().Instruments)
|
|
{
|
|
BuildInstrumentProcessNode(PluginHost, ProcessedTracks);
|
|
}
|
|
|
|
LayerTracks.Reset();
|
|
for (const auto& Pair : ProcessedTracks)
|
|
{
|
|
LayerTracks.FindOrAdd(Pair.Value).Add(Pair.Key);
|
|
}
|
|
|
|
// 较大的层级在前面
|
|
LayerTracks.KeySort([](int32 A, int32 B)
|
|
{
|
|
return A > B;
|
|
});
|
|
|
|
// 绘制层级
|
|
for (const auto& Pair : LayerTracks)
|
|
{
|
|
const int32& Layer = Pair.Key;
|
|
const TArray<FMixerTrack*>& Tracks = Pair.Value;
|
|
FString LayerString;
|
|
for (FMixerTrack* T : Tracks)
|
|
{
|
|
LayerString += T->GetName() + FString(" | ");
|
|
}
|
|
UE_LOG(LogTemp, Log, TEXT("Layer %d:| %s"), Layer, *LayerString);
|
|
}
|
|
}
|
|
|
|
void FMixerList::RequestBuildProcessNode()
|
|
{
|
|
PUSH_THREAD_EVENT(BuildProcessNode);
|
|
}
|
|
|
|
void FMixerList::ProcessInternal(FMixerTrack* Track, int32 NumSamples, TArray<FMixerTrack*>& ProcessedTracks)
|
|
{
|
|
if (ProcessedTracks.Contains(Track))
|
|
return;
|
|
|
|
for (const auto& Link : Track->Children)
|
|
{
|
|
ProcessInternal(Link.Track, NumSamples, ProcessedTracks);
|
|
Track->Buffer.AddBuffer(Link.Track->Buffer, Link.Gain);
|
|
}
|
|
|
|
Track->Buffer.CalculatePeak();
|
|
ProcessedTracks.Add(Track);
|
|
}
|
|
|
|
int32 FMixerList::BuildProcessNodeInternal(FMixerTrack* Track, TMap<FMixerTrack*, int32>& ProcessedTracks, int32 Layer)
|
|
{
|
|
int32& TrackCurrentLayer = ProcessedTracks.FindOrAdd(Track);
|
|
TrackCurrentLayer = FMath::Max(TrackCurrentLayer, Layer);
|
|
for (const FMixerTrackLink& ChildLink : Track->Children)
|
|
{
|
|
FMixerTrack* ChildTrack = ChildLink.Track;
|
|
BuildProcessNodeInternal(ChildTrack, ProcessedTracks, Layer + 1);
|
|
for (const FPluginHost* Effect : ChildTrack->Effects)
|
|
{
|
|
BuildEffectChannelInterface(ChildTrack, Effect->ChannelInterface, ProcessedTracks);
|
|
}
|
|
}
|
|
return ++Layer;
|
|
}
|
|
|
|
void FMixerList::BuildInstrumentProcessNode(FPluginHost* PluginHost, TMap<FMixerTrack*, int32>& ProcessedTracks)
|
|
{
|
|
for (FMixerTrack* Track : PluginHost->OwnerTracks)
|
|
{
|
|
BuildEffectChannelInterface(Track, PluginHost->ChannelInterface, ProcessedTracks);
|
|
}
|
|
}
|
|
|
|
void FMixerList::BuildEffectChannelInterface(FMixerTrack* Track, FChannelInterface* ChannelInterface, TMap<FMixerTrack*, int32>& ProcessedTracks)
|
|
{
|
|
int32& TrackCurrentLayer = ProcessedTracks.FindOrAdd(Track);
|
|
TArray<FChannelNode*>& InputChannelNodes = ChannelInterface->InputChannelNodes;
|
|
TArray<FChannelNode*>& OutputChannelNodes = ChannelInterface->OutputChannelNodes;
|
|
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
|
|
for (int i = 1; i < InputChannelNodes.Num(); ++i)
|
|
{
|
|
FChannelNode* ChannelNode = InputChannelNodes[i];
|
|
if (ChannelNode->NodeType != EChannelNodeType::MixerNode)
|
|
continue;
|
|
const FMixerChannelNode* MixerChannelNode = static_cast<FMixerChannelNode*>(ChannelNode);
|
|
FMixerTrack* InputTrack = MixerChannelNode->GetTrack();
|
|
int32& TargetMixerCurrentLayer = ProcessedTracks.FindOrAdd(InputTrack);
|
|
TargetMixerCurrentLayer = FMath::Max(TargetMixerCurrentLayer + 1, TrackCurrentLayer + 1);
|
|
}
|
|
|
|
// 如果这个效果器需要输出到其他轨道,那么这个轨道的深度就是目标轨道的深度+1
|
|
for (int i = 1; i < OutputChannelNodes.Num(); ++i)
|
|
{
|
|
FChannelNode* ChannelNode = OutputChannelNodes[i];
|
|
if (ChannelNode->NodeType != EChannelNodeType::MixerNode)
|
|
continue;
|
|
const FMixerChannelNode* MixerChannelNode = static_cast<FMixerChannelNode*>(ChannelNode);
|
|
FMixerTrack* MixerTrack = MixerChannelNode->GetTrack();
|
|
const int32& TargetMixerCurrentLayer = ProcessedTracks.FindOrAdd(MixerTrack);
|
|
TrackCurrentLayer = FMath::Max(TrackCurrentLayer, TargetMixerCurrentLayer + 1);
|
|
}
|
|
}
|