很早之前的修改,我也忘了改什么了
This commit is contained in:
parent
55d6cdaade
commit
77e386c0e5
@ -1,6 +1,6 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "{15DECA3A-4D95-597C-AAD4-38A8F659756E}",
|
||||
"EngineAssociation": "{35222BC2-4318-BEC6-472A-AAA5F298656C}",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Modules": [
|
||||
|
@ -1,2 +1,6 @@
|
||||
[Skin]
|
||||
Skin=Default
|
||||
Skin=Default
|
||||
|
||||
[Sample]
|
||||
Path="F:\Sample"
|
||||
Path="F:\Vocaloid DB\Miku V4x\MikuV4x\BCNFCY43LB2LZCD4"
|
||||
|
@ -10,7 +10,7 @@ public class AronaTarget : TargetRules
|
||||
public AronaTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Program;
|
||||
LinkType = TargetLinkType.Default;
|
||||
LinkType = TargetLinkType.Monolithic;
|
||||
LaunchModuleName = "Arona";
|
||||
|
||||
// Arona.exe has no exports, so no need to verify that a .lib and .exp file was emitted by
|
||||
|
@ -21,7 +21,8 @@ public class Arona : ModuleRules
|
||||
"StandaloneRenderer",
|
||||
"DesktopPlatform",
|
||||
"AronaCore",
|
||||
"SignalProcessing"
|
||||
"SignalProcessing",
|
||||
"Json"
|
||||
}
|
||||
);
|
||||
|
||||
@ -32,24 +33,5 @@ public class Arona : ModuleRules
|
||||
);
|
||||
|
||||
PrivateIncludePaths.Add(Path.Combine(EngineDirectory, "Source", "Runtime/Launch/Private")); // For LaunchEngineLoop.cpp include
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.IOS || Target.Platform == UnrealTargetPlatform.TVOS)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string [] {
|
||||
"NetworkFile",
|
||||
"StreamingFile"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (Target.IsInPlatformGroup(UnrealPlatformGroup.Linux))
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[] {
|
||||
"UnixCommonStartup"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ IMPLEMENT_APPLICATION(Arona, "Arona");
|
||||
int32 Init(const TCHAR* CmdLine)
|
||||
{
|
||||
FCommandLine::Set(CmdLine);
|
||||
FGenericPlatformProcess::SetShaderDir(*FPaths::Combine(FPaths::ProjectContentDir(), TEXT("Shader")));
|
||||
// FGenericPlatformProcess::SetShaderDir(*FPaths::Combine(FPaths::ProjectContentDir(), TEXT("Shader")));
|
||||
FGenericPlatformProcess::SetShaderDir(TEXT("E:\\Projects\\AronaSlate\\Content\\Shader"));
|
||||
GThreadPool = new FQueuedLowLevelThreadPool();
|
||||
InitLog();
|
||||
// BUG: Memoty leak
|
||||
|
@ -1,9 +1,12 @@
|
||||
#include "PeakFile.h"
|
||||
|
||||
#include "Async.h"
|
||||
#include "PluginHost/Sampler.h"
|
||||
|
||||
#define MID_TO_LOW (16 * 16)
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogPeakFile);
|
||||
|
||||
TArray<TArray<FSamplePeak>> GenPeakData(const TArray<TArray64<float>>& Data, int32 BlockSize)
|
||||
{
|
||||
TArray<TArray<FSamplePeak>> Out;
|
||||
@ -61,6 +64,20 @@ FWaveform::~FWaveform()
|
||||
|
||||
void FWaveform::UpdatePeak(const TArray<TArray64<float>>& SampleBuffer)
|
||||
{
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
RenderData[i]->Generate(SampleBuffer);
|
||||
Processing = true;
|
||||
Async(EAsyncExecution::TaskGraph, [this, &SampleBuffer]()
|
||||
{
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
RenderData[i]->Generate(SampleBuffer);
|
||||
|
||||
const float MemSize = GetMemSize();
|
||||
UE_LOG(LogPeakFile, Log, TEXT("Waveform MemSize: %f MB"), MemSize / 1024 / 1024);
|
||||
|
||||
// 创建一个主线程TaskGraph任务, 用于通知UI线程更新
|
||||
Async(EAsyncExecution::TaskGraphMainThread, [this]()
|
||||
{
|
||||
OnPostUpdatePeak.Broadcast();
|
||||
});
|
||||
Processing = false;
|
||||
});
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
class FSampler;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogPeakFile, Log, All);
|
||||
|
||||
struct FSamplePeak
|
||||
{
|
||||
float Min = 0;
|
||||
@ -73,6 +75,7 @@ public:
|
||||
const int32 BlockSize; // 每一个块包含多少个Frame
|
||||
};
|
||||
|
||||
// 最好由一个管理器统一管理
|
||||
class FWaveform
|
||||
{
|
||||
public:
|
||||
@ -90,6 +93,8 @@ public:
|
||||
|
||||
TArrayView<const FSamplePeak> GetPeakFromRange(const uint16 Channel, const int32 Width, const uint64 StartFrame, const uint64 EndFrame) const
|
||||
{
|
||||
if (Processing)
|
||||
return {};
|
||||
const uint64 FrameCount = EndFrame - StartFrame;
|
||||
const int32 BlockSize = FrameCount / Width;
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
@ -98,4 +103,8 @@ public:
|
||||
return RenderData.Last()->GetPeakFromRange(Channel, StartFrame, EndFrame);
|
||||
}
|
||||
TArray<FWaveformRenderData*> RenderData;
|
||||
std::atomic_bool Processing = false;
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE(FOnPostUpdatePeak)
|
||||
FOnPostUpdatePeak OnPostUpdatePeak;
|
||||
};
|
||||
|
56
Source/Arona/PeakFile/PeakFileManager.cpp
Normal file
56
Source/Arona/PeakFile/PeakFileManager.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "PeakFileManager.h"
|
||||
|
||||
#include "Async.h"
|
||||
#include "FileHelper.h"
|
||||
|
||||
void FPeakFileManager::Init()
|
||||
{
|
||||
// 开一个线程加载PeakFile
|
||||
}
|
||||
|
||||
void FPeakFileManager::LoadPeakFile(const FString& Path)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FPeakFileManager::LoadCache()
|
||||
{
|
||||
FString FilePath = GetCacheDirMapFilePath();
|
||||
if (!FPaths::FileExists(FilePath))
|
||||
{
|
||||
// 建立新的缓存
|
||||
GenerateCache();
|
||||
}
|
||||
}
|
||||
|
||||
void FPeakFileManager::GenerateCache()
|
||||
{
|
||||
// const FString& FileName = GetCacheDirMapFilePath();
|
||||
|
||||
// Async(EAsyncExecution::Thread, [this]()
|
||||
// {
|
||||
// Processing = true;
|
||||
// GenerateCacheTask();
|
||||
// Processing = false;
|
||||
// });
|
||||
|
||||
// FFileHelper::SaveStringToFile(, *FileName);
|
||||
}
|
||||
|
||||
FString FPeakFileManager::GetCacheDirPath()
|
||||
{
|
||||
return FPaths::ProjectContentDir() / TEXT("Cache") / TEXT("PeakFile");
|
||||
}
|
||||
|
||||
FString FPeakFileManager::GetCacheDirMapFilePath()
|
||||
{
|
||||
return GetCacheDirPath() / TEXT("Map.json");
|
||||
}
|
||||
|
||||
void FPeakFileManager::GenerateCacheTask()
|
||||
{
|
||||
while (!Processing)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
24
Source/Arona/PeakFile/PeakFileManager.h
Normal file
24
Source/Arona/PeakFile/PeakFileManager.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "Singleton/Singleton.h"
|
||||
|
||||
class FWaveform;
|
||||
|
||||
class FPeakFileManager : public TSingleton<FPeakFileManager>
|
||||
{
|
||||
public:
|
||||
virtual void Init() override;
|
||||
|
||||
|
||||
void LoadPeakFile(const FString& Path);
|
||||
void LoadCache();
|
||||
|
||||
void GenerateCache();
|
||||
|
||||
static FString GetCacheDirPath();
|
||||
static FString GetCacheDirMapFilePath();
|
||||
private:
|
||||
void GenerateCacheTask();
|
||||
std::atomic_bool Processing = false;
|
||||
|
||||
TMap<uint64, FWaveform*> WaveformMap;
|
||||
};
|
@ -1,11 +1,13 @@
|
||||
#include "SingletonManager.h"
|
||||
|
||||
#include "Misc/AronaConfig.h"
|
||||
#include "PeakFile/PeakFileManager.h"
|
||||
#include "Singleton/CallRateLimiterManager.h"
|
||||
#include "Singleton/MidiSequencer.h"
|
||||
#include "Singleton/MixerList.h"
|
||||
#include "Singleton/PluginHostList.h"
|
||||
#include "Singleton/PortAudioAPI.h"
|
||||
#include "Singleton/SampleManager.h"
|
||||
#include "UI/Widget/WindowManager.h"
|
||||
|
||||
#define REGISTER_MANAGER(ManagerClass) RegisterManager(&ManagerClass::Get());
|
||||
@ -14,11 +16,13 @@ void FSingletonManager::Init()
|
||||
{
|
||||
REGISTER_MANAGER(FCallRateLimiterManager)
|
||||
REGISTER_MANAGER(FAronaConfig)
|
||||
REGISTER_MANAGER(FSampleManager)
|
||||
REGISTER_MANAGER(FMixerList)
|
||||
REGISTER_MANAGER(FPortAudioAPI)
|
||||
REGISTER_MANAGER(FPluginHostList)
|
||||
REGISTER_MANAGER(FMidiSequencer)
|
||||
REGISTER_MANAGER(FWindowManager)
|
||||
REGISTER_MANAGER(FPeakFileManager)
|
||||
|
||||
for (ISingleton* Manager : Managers)
|
||||
{
|
||||
|
@ -17,5 +17,5 @@ public:
|
||||
void RegisterManager(ISingleton* Manager);
|
||||
private:
|
||||
FSingletonManager();
|
||||
TArray<ISingleton*> Managers;
|
||||
TArray<ISingleton*> Managers;
|
||||
};
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Constructs this widget with InArgs */
|
||||
void Construct(const FArguments& InArgs);
|
||||
virtual void Construct(const FArguments& InArgs);
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
@ -4,35 +4,65 @@
|
||||
#include "UI/Widget/Thumbnail.h"
|
||||
#include "UI/Widget/WaveformViewer.h"
|
||||
|
||||
FSamplePatternInstanceWaveformHandle::FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance):
|
||||
FSamplePatternInstanceWaveformHandle::FSamplePatternInstanceWaveformHandle(TSharedRef<SSamplePatternInstance> InPatternInstance):
|
||||
PatternInstanceWidget(InPatternInstance)
|
||||
{
|
||||
}
|
||||
|
||||
TArrayView<const FSamplePeak> FSamplePatternInstanceWaveformHandle::GetWaveform(int32 SizeX)
|
||||
{
|
||||
FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget->GetPatternInstance();
|
||||
TRange<AudioFrame> FrameViewRange = PatternInstanceWidget->GetFrameViewRange();
|
||||
if (!PatternInstanceWidget.IsValid())
|
||||
return TArrayView<const FSamplePeak>();
|
||||
const FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget.Pin()->GetPatternInstance();
|
||||
const FSampler* Sampler = Instance->GetInstanceOwner()->GetSampler();
|
||||
const TRange<AudioFrame>& TimeRange = Instance->TimeRange;
|
||||
|
||||
|
||||
if (!RenderData)
|
||||
{
|
||||
RenderData = new FWaveform(7, 256 * 128);
|
||||
RenderData->OnPostUpdatePeak.AddLambda([this]() { OnRequestUpdate.Broadcast(); });
|
||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
||||
const float MemSize = RenderData->GetMemSize();
|
||||
UE_LOG(LogTemp, Warning, TEXT("Waveform MemSize: %f MB"), MemSize / 1024 / 1024);
|
||||
}
|
||||
LastRenderRange = GetRenderRange();
|
||||
return RenderData->GetPeakFromRange(0, SizeX, LastRenderRange.GetLowerBoundValue(), LastRenderRange.GetUpperBoundValue());
|
||||
}
|
||||
|
||||
bool FSamplePatternInstanceWaveformHandle::NeedUpdate() const
|
||||
{
|
||||
const TRange<AudioFrame>& RenderRange = GetRenderRange();
|
||||
const bool Lower = FMath::IsNearlyEqual(LastRenderRange.GetLowerBoundValue(), RenderRange.GetLowerBoundValue());
|
||||
const bool Upper = FMath::IsNearlyEqual(LastRenderRange.GetUpperBoundValue(), RenderRange.GetUpperBoundValue());
|
||||
return !(Lower && Upper);
|
||||
}
|
||||
|
||||
TRange<AudioFrame> FSamplePatternInstanceWaveformHandle::GetRenderRange() const
|
||||
{
|
||||
if (!PatternInstanceWidget.IsValid())
|
||||
return TRange<AudioFrame>(0, 0);
|
||||
const TSharedPtr<SSamplePatternInstance> SamplePatternInstance = PatternInstanceWidget.Pin();
|
||||
FSamplePatternInstance* Instance = static_cast<FSamplePatternInstance*>(SamplePatternInstance->GetPatternInstance());
|
||||
const TRange<AudioFrame>& TimeRange = Instance->TimeRange;
|
||||
TRange<AudioFrame> FrameViewRange = SamplePatternInstance->GetFrameViewRange();
|
||||
|
||||
const AudioFrame InstancePos = Instance->GetMidiPos();
|
||||
FrameViewRange = TRange<AudioFrame>(FrameViewRange.GetLowerBoundValue() - InstancePos, FrameViewRange.GetUpperBoundValue() - InstancePos);
|
||||
const AudioFrame BeginFrame = FMath::Max(TimeRange.GetLowerBoundValue(), FrameViewRange.GetLowerBoundValue());
|
||||
const AudioFrame EndFrame = FMath::Min(TimeRange.GetUpperBoundValue(), FrameViewRange.GetUpperBoundValue());
|
||||
return RenderData->GetPeakFromRange(0, SizeX, BeginFrame, EndFrame);
|
||||
|
||||
return TRange<AudioFrame>(BeginFrame, EndFrame);
|
||||
}
|
||||
|
||||
SSamplePatternInstance::SSamplePatternInstance()
|
||||
{
|
||||
WaveformHandle = MakeShared<FSamplePatternInstanceWaveformHandle>(this);
|
||||
}
|
||||
|
||||
void SSamplePatternInstance::Construct(const FArguments& InArgs)
|
||||
{
|
||||
WaveformHandle = MakeShared<FSamplePatternInstanceWaveformHandle>(SharedThis(this));
|
||||
WaveformHandle->OnRequestUpdate.AddLambda([this]()
|
||||
{
|
||||
Invalidate(EInvalidateWidgetReason::Paint);
|
||||
});
|
||||
SPatternInstance::Construct(InArgs);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SSamplePatternInstance::GetViewWidget()
|
||||
@ -43,6 +73,8 @@ TSharedRef<SWidget> SSamplePatternInstance::GetViewWidget()
|
||||
|
||||
void SSamplePatternInstance::RequestUpdate()
|
||||
{
|
||||
if (WaveformHandle.IsValid() && WaveformHandle->NeedUpdate())
|
||||
Invalidate(EInvalidateWidgetReason::Paint);
|
||||
}
|
||||
|
||||
FReply SSamplePatternInstance::OpenPatternMenu()
|
||||
|
@ -8,16 +8,22 @@ class SSamplePatternInstance;
|
||||
class FSamplePatternInstanceWaveformHandle : public IWaveformHandle
|
||||
{
|
||||
public:
|
||||
FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance);
|
||||
SSamplePatternInstance* PatternInstanceWidget;
|
||||
FSamplePatternInstanceWaveformHandle(TSharedRef<SSamplePatternInstance> InPatternInstance);
|
||||
virtual ~FSamplePatternInstanceWaveformHandle() override { delete RenderData; }
|
||||
TWeakPtr<SSamplePatternInstance> PatternInstanceWidget;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
||||
virtual bool NeedUpdate() const override;
|
||||
|
||||
TRange<AudioFrame> GetRenderRange() const;
|
||||
FWaveform* RenderData = nullptr;
|
||||
TRange<AudioFrame> LastRenderRange;
|
||||
};
|
||||
|
||||
class ARONA_API SSamplePatternInstance : public SPatternInstance
|
||||
{
|
||||
public:
|
||||
SSamplePatternInstance();
|
||||
virtual void Construct(const FArguments& InArgs) override;
|
||||
virtual TSharedRef<SWidget> GetViewWidget() override;
|
||||
virtual void RequestUpdate() override;
|
||||
protected:
|
||||
|
@ -14,14 +14,24 @@ TArrayView<const FSamplePeak> FSamplePatternWaveformHandle::GetWaveform(int32 Si
|
||||
if (!RenderData)
|
||||
{
|
||||
RenderData = new FWaveform(1, 1024);
|
||||
RenderData->OnPostUpdatePeak.AddLambda([this]()
|
||||
{
|
||||
OnRequestUpdate.Broadcast();
|
||||
});
|
||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
||||
}
|
||||
return RenderData->GetPeakFromRange(0, SizeX, 0, Sampler->GetFrameCount());
|
||||
}
|
||||
|
||||
bool FSamplePatternWaveformHandle::NeedUpdate() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSamplePatternThumbnail::Construct(const FArguments& InArgs, FSampler* InSampler)
|
||||
{
|
||||
WaveformHandle = MakeShared<FSamplePatternWaveformHandle>(InSampler);
|
||||
WaveformHandle->OnRequestUpdate.AddLambda([this](){ Invalidate(EInvalidateWidgetReason::Paint); });
|
||||
ChildSlot
|
||||
[
|
||||
SAssignNew(WaveformViewer, SWaveformViewer)
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
FSampler* Sampler = nullptr;
|
||||
FWaveform* RenderData = nullptr;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
||||
virtual bool NeedUpdate() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -240,10 +240,10 @@ void SPlayListPanel::OnCreatePatternInstance(FPatternInstance* PatternInstance)
|
||||
.Alignment(FVector2D(0, 0))
|
||||
.Offset(Offset)
|
||||
[
|
||||
// SNew(SInvalidationPanel)
|
||||
// [
|
||||
SNew(SInvalidationPanel)
|
||||
[
|
||||
PlayListEdit->CreatePatternInstanceWidget(PatternInstance)
|
||||
// ]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@ -253,11 +253,12 @@ void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance)
|
||||
for (int i = 0; i < Children->Num(); ++i)
|
||||
{
|
||||
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
||||
// TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||
// InvalidationPanel->SetCanCache(false);
|
||||
// TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||
// InvalidationPanel->SetCanCache(true);
|
||||
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(Widget);
|
||||
const TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||
// 设置CanCache为false, 因为SInvalidationPanel重写了GetChildren;
|
||||
InvalidationPanel->SetCanCache(false);
|
||||
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||
InvalidationPanel->SetCanCache(true);
|
||||
const TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
||||
{
|
||||
Children->RemoveAt(i);
|
||||
@ -272,13 +273,12 @@ void SPlayListPanel::UpdateAllPatternInstance()
|
||||
for (int i = 0; i < Children->Num(); ++i)
|
||||
{
|
||||
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
||||
TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||
const TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||
InvalidationPanel->SetCanCache(false);
|
||||
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||
InvalidationPanel->SetCanCache(true);
|
||||
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||
const TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||
InstanceWidget->RequestUpdate();
|
||||
// InstanceWidget->Invalidate(EInvalidateWidgetReason::Paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "SPlayList.h"
|
||||
|
||||
#include "SInvalidationPanel.h"
|
||||
#include "SlateOptMacros.h"
|
||||
#include "SPlayListTimeLine.h"
|
||||
#include "PlayListPanel/SPlayListPanel.h"
|
||||
@ -43,8 +44,14 @@ void SPlayList::Construct(const FArguments& InArgs)
|
||||
void SPlayList::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
const bool LowerEqual = !FMath::IsNearlyEqualByULP(FrameRange.GetLowerBoundValue(), TargetFrameRange.GetLowerBoundValue(), 1);
|
||||
const bool UpperEqual = !FMath::IsNearlyEqualByULP(FrameRange.GetUpperBoundValue(), TargetFrameRange.GetUpperBoundValue(), 1);
|
||||
if (!LowerEqual && !UpperEqual)
|
||||
return;
|
||||
FrameRange.SetLowerBoundValue(FMath::FInterpTo<double>(FrameRange.GetLowerBoundValue(), TargetFrameRange.GetLowerBoundValue(), InDeltaTime, 20.f));
|
||||
FrameRange.SetUpperBoundValue(FMath::FInterpTo<double>(FrameRange.GetUpperBoundValue(), TargetFrameRange.GetUpperBoundValue(), InDeltaTime, 20.f));
|
||||
PlayListPanel->UpdateAllPatternInstance();
|
||||
}
|
||||
|
||||
FReply SPlayList::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
||||
|
@ -1,13 +1,6 @@
|
||||
#include "WaveformViewer.h"
|
||||
|
||||
#include "Async.h"
|
||||
#include "ExecutionTime.h"
|
||||
#include "MaxElement.h"
|
||||
#include "SlateComputeShader.h"
|
||||
#include "SlatePixelShader.h"
|
||||
#include "Thumbnail.h"
|
||||
#include "Pattern/SamplePattern.h"
|
||||
#include "Pattern/SamplePatternInstance.h"
|
||||
|
||||
SWaveformViewer::SWaveformViewer()
|
||||
{
|
||||
@ -53,8 +46,6 @@ void SWaveformViewer::Construct(const FArguments& InArgs)
|
||||
void SWaveformViewer::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
if (UpdateQueue.Peek())
|
||||
Invalidate(EInvalidateWidgetReason::Paint);
|
||||
}
|
||||
|
||||
TArray<FSamplePeak> ScaleWaveform(const TArrayView<const FSamplePeak>& Waveform, const int32 SizeX)
|
||||
@ -81,6 +72,7 @@ int32 SWaveformViewer::OnPaint(const FPaintArgs& Args, const FGeometry& Allotted
|
||||
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId,
|
||||
const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
if (WaveformHandle.IsValid())
|
||||
{
|
||||
const FIntPoint& Size = FIntPoint(AllottedGeometry.Size.X, AllottedGeometry.Size.Y);
|
||||
if (Size.X <= 0 || Size.Y <= 0) return LayerId;
|
||||
|
@ -9,7 +9,10 @@ class IWaveformHandle
|
||||
{
|
||||
public:
|
||||
virtual ~IWaveformHandle() = default;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) = 0;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) = 0;
|
||||
virtual bool NeedUpdate() const { return false; }
|
||||
DECLARE_MULTICAST_DELEGATE(FRequestUpdate)
|
||||
FRequestUpdate OnRequestUpdate;
|
||||
};
|
||||
|
||||
class ARONA_API SWaveformViewer : public SLeafWidget
|
||||
@ -34,14 +37,6 @@ private:
|
||||
TSharedPtr<FSlateShaderParam> Params;
|
||||
TSharedPtr<IComputeShaderSlateElement> ComputeElement;
|
||||
|
||||
struct UpdateTask
|
||||
{
|
||||
TArray<float> Array;
|
||||
FIntPoint Size;
|
||||
float LineUV;
|
||||
};
|
||||
mutable TQueue<UpdateTask, EQueueMode::Mpsc> UpdateQueue;
|
||||
|
||||
struct FCSParam
|
||||
{
|
||||
FLinearColor WaveColor;
|
||||
|
@ -7,17 +7,29 @@ class MidiTick;
|
||||
class AudioFrame
|
||||
{
|
||||
public:
|
||||
typedef double ValueType;
|
||||
AudioFrame() : Pos(0) {}
|
||||
AudioFrame(const double InFrame) : Pos(InFrame) {}
|
||||
AudioFrame(const MidiTick& InMidiTicks);
|
||||
|
||||
AudioFrame operator=(const double& InPos) { Pos = InPos; return *this; }
|
||||
AudioFrame operator+(const double& InFrame) const { return Pos + InFrame; }
|
||||
AudioFrame operator-(const double& InFrame) const { return Pos - InFrame; }
|
||||
AudioFrame operator*(const double& InFrame) const { return Pos * InFrame; }
|
||||
AudioFrame operator/(const double& InFrame) const { return Pos / InFrame; }
|
||||
AudioFrame& operator+=(const double& InFrame) { Pos += InFrame; return *this; }
|
||||
AudioFrame& operator-=(const double& InFrame) { Pos -= InFrame; return *this; }
|
||||
template<typename T>
|
||||
AudioFrame operator=(const T& InPos) { Pos = InPos; return *this; }
|
||||
template<typename T>
|
||||
AudioFrame operator+(const T& InFrame) const { return Pos + InFrame; }
|
||||
template<typename T>
|
||||
AudioFrame operator-(const T& InFrame) const { return Pos - InFrame; }
|
||||
template<typename T>
|
||||
AudioFrame operator*(const T& InFrame) const { return Pos * InFrame; }
|
||||
template<typename T>
|
||||
AudioFrame operator/(const T& InFrame) const { return Pos / InFrame; }
|
||||
template<typename T>
|
||||
AudioFrame& operator+=(const T& InFrame) { Pos += InFrame; return *this; }
|
||||
template<typename T>
|
||||
AudioFrame& operator-=(const T& InFrame) { Pos -= InFrame; return *this; }
|
||||
template<typename T>
|
||||
AudioFrame& operator*=(const T& InFrame) { Pos *= InFrame; return *this; }
|
||||
template<typename T>
|
||||
AudioFrame& operator/=(const T& InFrame) { Pos /= InFrame; return *this; }
|
||||
|
||||
bool operator==(const AudioFrame& InFrame) const { return Pos == InFrame.Pos; }
|
||||
bool operator!=(const AudioFrame& InFrame) const { return Pos != InFrame.Pos; }
|
||||
@ -26,7 +38,7 @@ public:
|
||||
bool operator<=(const AudioFrame& InFrame) const { return Pos <= InFrame.Pos; }
|
||||
bool operator>=(const AudioFrame& InFrame) const { return Pos >= InFrame.Pos; }
|
||||
|
||||
operator double() const { return Pos; }
|
||||
operator ValueType() const { return Pos; }
|
||||
|
||||
void SetTick(MidiTick InMidiTicks);
|
||||
MidiTick Ticks() const;
|
||||
@ -36,7 +48,7 @@ public:
|
||||
static AudioFrame FromFrames(double InFrames, float InFramesPerTick);
|
||||
static double FrameToMilliseconds(double InFrame);
|
||||
|
||||
double Pos;
|
||||
ValueType Pos;
|
||||
};
|
||||
|
||||
class MidiTick
|
||||
|
@ -31,20 +31,7 @@ public:
|
||||
static bool ParseString(const FString& String, T& Out);
|
||||
|
||||
template<typename T>
|
||||
static int32 GetValueArray(const FString& Section, const FString& Key, TArray<T>& Out)
|
||||
{
|
||||
TArray<FString> Temp;
|
||||
const int32 Count = Get().Config.GetArray(*Section, *Key, Temp);
|
||||
for (FString String : Temp)
|
||||
{
|
||||
T Value;
|
||||
if (ParseString(String, Value))
|
||||
{
|
||||
Out.Add(Value);
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
static int32 GetValueArray(const FString& Section, const FString& Key, TArray<T>& Out);
|
||||
|
||||
FConfigFile Config;
|
||||
};
|
||||
@ -72,5 +59,27 @@ ARONA_CONFIG_PARSE_STRING_FUNC(FRotator3d, ParseRotator);
|
||||
ARONA_CONFIG_PARSE_STRING_FUNC(FLinearColor, ParseLinearColor);
|
||||
ARONA_CONFIG_PARSE_STRING_FUNC(FColor, ParseColor);
|
||||
|
||||
template <typename T>
|
||||
int32 FAronaConfig::GetValueArray(const FString& Section, const FString& Key, TArray<T>& Out)
|
||||
{
|
||||
TArray<FString> Temp;
|
||||
const int32 Count = Get().Config.GetArray(*Section, *Key, Temp);
|
||||
for (FString String : Temp)
|
||||
{
|
||||
T Value;
|
||||
if (ParseString(String, Value))
|
||||
{
|
||||
Out.Add(Value);
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32 FAronaConfig::GetValueArray(const FString& Section, const FString& Key, TArray<FString>& Out)
|
||||
{
|
||||
return Get().Config.GetArray(*Section, *Key, Out);
|
||||
}
|
||||
|
||||
#undef ARONA_CONFIG_PARSE_STRING_FUNC
|
||||
#undef ARONA_CONFIG_GET_VALUE_FUNC
|
@ -28,7 +28,7 @@ FMidiPattern::~FMidiPattern()
|
||||
void FMidiPattern::Process(AudioFrame PatternPos, uint32 InLength)
|
||||
{
|
||||
UE::TScopeLock SpinLock(SequenceLock);
|
||||
AudioFrame PatternEndFrame = PatternPos + (double)InLength;
|
||||
AudioFrame PatternEndFrame = PatternPos + InLength;
|
||||
const MidiTick PatternEndPos = PatternEndFrame.Ticks();
|
||||
const MidiTick PatternBeginPos = PatternPos.Ticks();
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogVST2PluginHost);
|
||||
|
||||
ARONACORE_API static TMap<FString, TSharedPtr<FVST2PluginHandle>> PluginHandles;
|
||||
static TMap<FString, TSharedPtr<FVST2PluginHandle>> PluginHandles;
|
||||
|
||||
TSharedPtr<FVST2PluginHandle> TryLoadHandle(FString Path)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ FPortAudioAPI::FPortAudioAPI() : InputParameters(), OutputParameters()
|
||||
void FPortAudioAPI::Init()
|
||||
{
|
||||
Pa_Initialize();
|
||||
OpenStream(0, 20);
|
||||
OpenStream(0, Pa_GetDefaultOutputDevice());
|
||||
// OpenStream(0, 1);
|
||||
LogAllDevices();
|
||||
UE_LOG(PortAudioAPILog, Log, TEXT("PortAudioAPI Initialized"));
|
||||
@ -47,7 +47,7 @@ void FPortAudioAPI::Release()
|
||||
|
||||
void FPortAudioAPI::OpenStream(int32 InputDevice, int32 OutputDevice)
|
||||
{
|
||||
PaStreamParameters* Input = nullptr;
|
||||
const PaStreamParameters* Input = nullptr;
|
||||
if (InputDevice > 0)
|
||||
{
|
||||
InputParameters.device = InputDevice;
|
||||
|
203
Source/AronaCore/Singleton/SampleManager.cpp
Normal file
203
Source/AronaCore/Singleton/SampleManager.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include "SampleManager.h"
|
||||
|
||||
#include "ExecutionTime.h"
|
||||
#include "Misc/AronaConfig.h"
|
||||
|
||||
FArchive& operator<<(FArchive& Ar, FSamplePathNode* In)
|
||||
{
|
||||
return *In << Ar;
|
||||
}
|
||||
|
||||
FSamplePathNode* FSamplePathNode::Create(const FString& Path)
|
||||
{
|
||||
if (!FPaths::DirectoryExists(Path))
|
||||
return nullptr;
|
||||
|
||||
FSamplePathNode* Root = new FSamplePathNode();
|
||||
Root->Value = Path;
|
||||
Root->NodeType = Type::Directory;
|
||||
Root->Parent = nullptr;
|
||||
|
||||
class FSamplePathVisitor : public IPlatformFile::FDirectoryStatVisitor
|
||||
{
|
||||
public:
|
||||
FSamplePathVisitor(FSamplePathNode* InRoot) : Root(InRoot)
|
||||
{
|
||||
|
||||
}
|
||||
virtual bool Visit(const TCHAR* FilenameOrDirectory, const FFileStatData& StatData) override
|
||||
{
|
||||
if (!StatData.bIsDirectory)
|
||||
{
|
||||
const FString& FilePath = FPaths::GetPath(FilenameOrDirectory);
|
||||
auto* PathNode = Root->GetNodeByPath(FilePath);
|
||||
if (!PathNode)
|
||||
{
|
||||
PathNode = Root->CreateNodeByPath(FilePath);
|
||||
}
|
||||
FSamplePathNode* SamplePathNode = PathNode->AddChild();
|
||||
SamplePathNode->NodeType = Type::File;
|
||||
SamplePathNode->Value = FPaths::GetCleanFilename(FilenameOrDirectory);
|
||||
return true;
|
||||
}
|
||||
Root->CreateNodeByPath(FilenameOrDirectory);
|
||||
return true;
|
||||
}
|
||||
FSamplePathNode* Root;
|
||||
} Visitor(Root);
|
||||
|
||||
IPlatformFile::GetPlatformPhysical().IterateDirectoryStatRecursively(*Path, Visitor);
|
||||
return Root;
|
||||
}
|
||||
|
||||
FSamplePathNode* FSamplePathNode::AddChild()
|
||||
{
|
||||
FSamplePathNode* ChildNode = new FSamplePathNode();
|
||||
ChildNode->Parent = this;
|
||||
Child.Add(ChildNode);
|
||||
return ChildNode;
|
||||
}
|
||||
|
||||
FSamplePathNode* FSamplePathNode::GetNodeByPath(const FString& Path)
|
||||
{
|
||||
if (FPaths::IsSamePath(Path, GetFullPath()))
|
||||
return this;
|
||||
if (FSamplePathNode** Node = GetRootNode()->ChildMap.Find(Path))
|
||||
return *Node;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FSamplePathNode* FSamplePathNode::CreateNodeByPath(FString Path)
|
||||
{
|
||||
const FString& FullPath = GetFullPath();
|
||||
if (FullPath == Path)
|
||||
return this;
|
||||
TArray<FString> Paths;
|
||||
FString Leaf;
|
||||
do
|
||||
{
|
||||
FString Temp = FPaths::GetPathLeaf(Path);
|
||||
if (Temp == Leaf)
|
||||
break;
|
||||
Leaf = Temp;
|
||||
Paths.Add(Path);
|
||||
Path = FPaths::GetPath(Path);
|
||||
if (Path == FullPath)
|
||||
break;
|
||||
}
|
||||
while (true);
|
||||
Algo::Reverse(Paths);
|
||||
return CreateNodeByPathInternal(Paths);
|
||||
}
|
||||
|
||||
FSamplePathNode* FSamplePathNode::CreateNodeByPathInternal(const TArray<FString>& Paths)
|
||||
{
|
||||
FSamplePathNode* RootNode = GetRootNode();
|
||||
for (auto Str : Paths)
|
||||
{
|
||||
FSamplePathNode* PathNode = GetNodeByPath(Str);
|
||||
if (!PathNode)
|
||||
{
|
||||
PathNode = AddChild();
|
||||
PathNode->Value = FPaths::GetPathLeaf(Str);
|
||||
PathNode->NodeType = Directory;
|
||||
RootNode->ChildMap.Add(Str, PathNode);
|
||||
}
|
||||
}
|
||||
return GetNodeByPath(Paths[0]);
|
||||
}
|
||||
|
||||
bool FSampleDirectoryCache::NeedRefresh()
|
||||
{
|
||||
if (!FPaths::FileExists(*GetCachePath()))
|
||||
return true;
|
||||
TArray<FString> PathsStr;
|
||||
FAronaConfig::GetValueArray("Sample", "Path", PathsStr);
|
||||
if (PathsStr.Num() != SamplePathNodes.Num())
|
||||
return true;
|
||||
// TODO: 比较路径是否相同
|
||||
return false;
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::Init()
|
||||
{
|
||||
Reload();
|
||||
if (NeedRefresh())
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::Reload()
|
||||
{
|
||||
Clear();
|
||||
TArray<uint8> Data;
|
||||
if (!FFileHelper::LoadFileToArray(Data, *GetCachePath()))
|
||||
return;
|
||||
FMemoryReader MemoryReader(Data);
|
||||
Serialize(MemoryReader);
|
||||
Save();
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::Refresh()
|
||||
{
|
||||
if (Loading || !NeedRefresh())
|
||||
return;
|
||||
|
||||
Async(EAsyncExecution::Thread, [this]()
|
||||
{
|
||||
Loading = true;
|
||||
RegenCache();
|
||||
Loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::RegenCache()
|
||||
{
|
||||
DeleteCache();
|
||||
|
||||
FAronaConfig::GetValueArray("Sample", "Path", SamplePaths);
|
||||
for (auto Str : SamplePaths)
|
||||
{
|
||||
if (FSamplePathNode* SamplePathNode = FSamplePathNode::Create(Str))
|
||||
SamplePathNodes.Add(SamplePathNode);
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
bool FSampleDirectoryCache::Save()
|
||||
{
|
||||
TArray<uint8> Data;
|
||||
FMemoryWriter MemoryWriter(Data);
|
||||
Serialize(MemoryWriter);
|
||||
return FFileHelper::SaveArrayToFile(Data, *GetCachePath());
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::Clear()
|
||||
{
|
||||
for (const auto PathNode : SamplePathNodes)
|
||||
{
|
||||
delete PathNode;
|
||||
}
|
||||
SamplePathNodes.Empty();
|
||||
}
|
||||
|
||||
void FSampleManager::Init()
|
||||
{
|
||||
Cache.Init();
|
||||
}
|
||||
|
||||
void FSampleManager::Refresh()
|
||||
{
|
||||
Cache.Refresh();
|
||||
}
|
||||
|
||||
void FSampleDirectoryCache::DeleteCache()
|
||||
{
|
||||
IPlatformFile::GetPlatformPhysical().DeleteFile(*GetCachePath());
|
||||
}
|
||||
|
||||
FString FSampleDirectoryCache::GetCachePath()
|
||||
{
|
||||
return FPaths::ProjectSavedDir() / TEXT("SampleCache");
|
||||
}
|
138
Source/AronaCore/Singleton/SampleManager.h
Normal file
138
Source/AronaCore/Singleton/SampleManager.h
Normal file
@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
#include "Singleton.h"
|
||||
|
||||
struct FSamplePathNode;
|
||||
|
||||
FArchive& operator<<(FArchive& Ar, FSamplePathNode* In);
|
||||
|
||||
struct FSamplePathNode
|
||||
{
|
||||
~FSamplePathNode()
|
||||
{
|
||||
for (const auto* ChildNode : Child)
|
||||
{
|
||||
delete ChildNode;
|
||||
}
|
||||
Child.Empty();
|
||||
}
|
||||
enum Type : uint8
|
||||
{
|
||||
File,
|
||||
Directory
|
||||
};
|
||||
|
||||
static FSamplePathNode* Create(const FString& Path);
|
||||
|
||||
FSamplePathNode* AddChild();
|
||||
FSamplePathNode* GetNodeByPath(const FString& Path);
|
||||
FSamplePathNode* CreateNodeByPath(FString Path);
|
||||
FString GetFullPath() const { return Parent ? Parent->GetFullPath() / Value : Value; }
|
||||
FString GetValue() const { return Value; }
|
||||
bool IsRoot() const { return Parent == nullptr; }
|
||||
FSamplePathNode* GetRootNode() { return Parent ? Parent->GetRootNode() : this; }
|
||||
|
||||
FArchive& operator<<(FArchive& Ar)
|
||||
{
|
||||
if (Ar.IsLoading())
|
||||
{
|
||||
FString FullPath;
|
||||
Ar << FullPath;
|
||||
Ar << Value;
|
||||
uint8 TempNodeType;
|
||||
Ar << TempNodeType;
|
||||
NodeType = (Type)TempNodeType;
|
||||
|
||||
int32 ChildCount;
|
||||
Ar << ChildCount;
|
||||
for (int32 i = 0; i < ChildCount; ++i)
|
||||
{
|
||||
FSamplePathNode* ChildNode = new FSamplePathNode();
|
||||
ChildNode->Parent = this;
|
||||
Ar << ChildNode;
|
||||
Child.Add(ChildNode);
|
||||
}
|
||||
if (TempNodeType == Directory)
|
||||
GetRootNode()->ChildMap.Add(FullPath, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
FString FullPath = GetFullPath();
|
||||
Ar << FullPath;
|
||||
Ar << Value;
|
||||
uint8 TempNodeType = NodeType;
|
||||
Ar << TempNodeType;
|
||||
int32 ChildCount = Child.Num();
|
||||
Ar << ChildCount;
|
||||
for (int32 i = 0; i < ChildCount; ++i)
|
||||
{
|
||||
Ar << Child[i];
|
||||
}
|
||||
}
|
||||
return Ar;
|
||||
}
|
||||
|
||||
FString Value;
|
||||
Type NodeType;
|
||||
TArray<FSamplePathNode*> Child;
|
||||
FSamplePathNode* Parent;
|
||||
|
||||
TMap<FString, FSamplePathNode*> ChildMap; // 仅根节点
|
||||
private:
|
||||
FSamplePathNode* CreateNodeByPathInternal(const TArray<FString>& Paths);
|
||||
};
|
||||
|
||||
class FSampleDirectoryCache
|
||||
{
|
||||
public:
|
||||
bool NeedRefresh();
|
||||
|
||||
void Init();
|
||||
void Reload();
|
||||
void Refresh();
|
||||
void RegenCache();
|
||||
bool Save();
|
||||
void Clear();
|
||||
static FString GetCachePath();
|
||||
static void DeleteCache();
|
||||
|
||||
FArchive& Serialize(FArchive& Ar)
|
||||
{
|
||||
Ar << SamplePaths;
|
||||
if (Ar.IsLoading())
|
||||
{
|
||||
int32 ChildCount;
|
||||
Ar << ChildCount;
|
||||
for (int i = 0; i < ChildCount; ++i)
|
||||
{
|
||||
auto Node = new FSamplePathNode();
|
||||
Ar << Node;
|
||||
SamplePathNodes.Add(Node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 ChildCount = SamplePathNodes.Num();
|
||||
Ar << ChildCount;
|
||||
for (int i = 0; i < ChildCount; ++i)
|
||||
{
|
||||
Ar << SamplePathNodes[i];
|
||||
}
|
||||
}
|
||||
return Ar;
|
||||
}
|
||||
|
||||
TArray<FString> SamplePaths;
|
||||
TArray<FSamplePathNode*> SamplePathNodes;
|
||||
std::atomic_bool Loading;
|
||||
};
|
||||
|
||||
class FSampleManager : public TSingleton<FSampleManager>
|
||||
{
|
||||
public:
|
||||
virtual void Init() override;
|
||||
|
||||
void Refresh();
|
||||
private:
|
||||
TArray<FString> SamplePaths;
|
||||
FSampleDirectoryCache Cache;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user