很早之前的修改,我也忘了改什么了
This commit is contained in:
parent
55d6cdaade
commit
77e386c0e5
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"FileVersion": 3,
|
"FileVersion": 3,
|
||||||
"EngineAssociation": "{15DECA3A-4D95-597C-AAD4-38A8F659756E}",
|
"EngineAssociation": "{35222BC2-4318-BEC6-472A-AAA5F298656C}",
|
||||||
"Category": "",
|
"Category": "",
|
||||||
"Description": "",
|
"Description": "",
|
||||||
"Modules": [
|
"Modules": [
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
[Skin]
|
[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)
|
public AronaTarget(TargetInfo Target) : base(Target)
|
||||||
{
|
{
|
||||||
Type = TargetType.Program;
|
Type = TargetType.Program;
|
||||||
LinkType = TargetLinkType.Default;
|
LinkType = TargetLinkType.Monolithic;
|
||||||
LaunchModuleName = "Arona";
|
LaunchModuleName = "Arona";
|
||||||
|
|
||||||
// Arona.exe has no exports, so no need to verify that a .lib and .exp file was emitted by
|
// 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",
|
"StandaloneRenderer",
|
||||||
"DesktopPlatform",
|
"DesktopPlatform",
|
||||||
"AronaCore",
|
"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
|
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)
|
int32 Init(const TCHAR* CmdLine)
|
||||||
{
|
{
|
||||||
FCommandLine::Set(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();
|
GThreadPool = new FQueuedLowLevelThreadPool();
|
||||||
InitLog();
|
InitLog();
|
||||||
// BUG: Memoty leak
|
// BUG: Memoty leak
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include "PeakFile.h"
|
#include "PeakFile.h"
|
||||||
|
|
||||||
|
#include "Async.h"
|
||||||
#include "PluginHost/Sampler.h"
|
#include "PluginHost/Sampler.h"
|
||||||
|
|
||||||
#define MID_TO_LOW (16 * 16)
|
#define MID_TO_LOW (16 * 16)
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(LogPeakFile);
|
||||||
|
|
||||||
TArray<TArray<FSamplePeak>> GenPeakData(const TArray<TArray64<float>>& Data, int32 BlockSize)
|
TArray<TArray<FSamplePeak>> GenPeakData(const TArray<TArray64<float>>& Data, int32 BlockSize)
|
||||||
{
|
{
|
||||||
TArray<TArray<FSamplePeak>> Out;
|
TArray<TArray<FSamplePeak>> Out;
|
||||||
@ -61,6 +64,20 @@ FWaveform::~FWaveform()
|
|||||||
|
|
||||||
void FWaveform::UpdatePeak(const TArray<TArray64<float>>& SampleBuffer)
|
void FWaveform::UpdatePeak(const TArray<TArray64<float>>& SampleBuffer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < RenderData.Num(); ++i)
|
Processing = true;
|
||||||
RenderData[i]->Generate(SampleBuffer);
|
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;
|
class FSampler;
|
||||||
|
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(LogPeakFile, Log, All);
|
||||||
|
|
||||||
struct FSamplePeak
|
struct FSamplePeak
|
||||||
{
|
{
|
||||||
float Min = 0;
|
float Min = 0;
|
||||||
@ -73,6 +75,7 @@ public:
|
|||||||
const int32 BlockSize; // 每一个块包含多少个Frame
|
const int32 BlockSize; // 每一个块包含多少个Frame
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 最好由一个管理器统一管理
|
||||||
class FWaveform
|
class FWaveform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -90,6 +93,8 @@ public:
|
|||||||
|
|
||||||
TArrayView<const FSamplePeak> GetPeakFromRange(const uint16 Channel, const int32 Width, const uint64 StartFrame, const uint64 EndFrame) const
|
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 uint64 FrameCount = EndFrame - StartFrame;
|
||||||
const int32 BlockSize = FrameCount / Width;
|
const int32 BlockSize = FrameCount / Width;
|
||||||
for (int i = 0; i < RenderData.Num(); ++i)
|
for (int i = 0; i < RenderData.Num(); ++i)
|
||||||
@ -98,4 +103,8 @@ public:
|
|||||||
return RenderData.Last()->GetPeakFromRange(Channel, StartFrame, EndFrame);
|
return RenderData.Last()->GetPeakFromRange(Channel, StartFrame, EndFrame);
|
||||||
}
|
}
|
||||||
TArray<FWaveformRenderData*> RenderData;
|
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 "SingletonManager.h"
|
||||||
|
|
||||||
#include "Misc/AronaConfig.h"
|
#include "Misc/AronaConfig.h"
|
||||||
|
#include "PeakFile/PeakFileManager.h"
|
||||||
#include "Singleton/CallRateLimiterManager.h"
|
#include "Singleton/CallRateLimiterManager.h"
|
||||||
#include "Singleton/MidiSequencer.h"
|
#include "Singleton/MidiSequencer.h"
|
||||||
#include "Singleton/MixerList.h"
|
#include "Singleton/MixerList.h"
|
||||||
#include "Singleton/PluginHostList.h"
|
#include "Singleton/PluginHostList.h"
|
||||||
#include "Singleton/PortAudioAPI.h"
|
#include "Singleton/PortAudioAPI.h"
|
||||||
|
#include "Singleton/SampleManager.h"
|
||||||
#include "UI/Widget/WindowManager.h"
|
#include "UI/Widget/WindowManager.h"
|
||||||
|
|
||||||
#define REGISTER_MANAGER(ManagerClass) RegisterManager(&ManagerClass::Get());
|
#define REGISTER_MANAGER(ManagerClass) RegisterManager(&ManagerClass::Get());
|
||||||
@ -14,11 +16,13 @@ void FSingletonManager::Init()
|
|||||||
{
|
{
|
||||||
REGISTER_MANAGER(FCallRateLimiterManager)
|
REGISTER_MANAGER(FCallRateLimiterManager)
|
||||||
REGISTER_MANAGER(FAronaConfig)
|
REGISTER_MANAGER(FAronaConfig)
|
||||||
|
REGISTER_MANAGER(FSampleManager)
|
||||||
REGISTER_MANAGER(FMixerList)
|
REGISTER_MANAGER(FMixerList)
|
||||||
REGISTER_MANAGER(FPortAudioAPI)
|
REGISTER_MANAGER(FPortAudioAPI)
|
||||||
REGISTER_MANAGER(FPluginHostList)
|
REGISTER_MANAGER(FPluginHostList)
|
||||||
REGISTER_MANAGER(FMidiSequencer)
|
REGISTER_MANAGER(FMidiSequencer)
|
||||||
REGISTER_MANAGER(FWindowManager)
|
REGISTER_MANAGER(FWindowManager)
|
||||||
|
REGISTER_MANAGER(FPeakFileManager)
|
||||||
|
|
||||||
for (ISingleton* Manager : Managers)
|
for (ISingleton* Manager : Managers)
|
||||||
{
|
{
|
||||||
|
@ -17,5 +17,5 @@ public:
|
|||||||
void RegisterManager(ISingleton* Manager);
|
void RegisterManager(ISingleton* Manager);
|
||||||
private:
|
private:
|
||||||
FSingletonManager();
|
FSingletonManager();
|
||||||
TArray<ISingleton*> Managers;
|
TArray<ISingleton*> Managers;
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
SLATE_END_ARGS()
|
SLATE_END_ARGS()
|
||||||
|
|
||||||
/** Constructs this widget with InArgs */
|
/** 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 OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||||
virtual FReply OnMouseButtonUp(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/Thumbnail.h"
|
||||||
#include "UI/Widget/WaveformViewer.h"
|
#include "UI/Widget/WaveformViewer.h"
|
||||||
|
|
||||||
FSamplePatternInstanceWaveformHandle::FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance):
|
FSamplePatternInstanceWaveformHandle::FSamplePatternInstanceWaveformHandle(TSharedRef<SSamplePatternInstance> InPatternInstance):
|
||||||
PatternInstanceWidget(InPatternInstance)
|
PatternInstanceWidget(InPatternInstance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TArrayView<const FSamplePeak> FSamplePatternInstanceWaveformHandle::GetWaveform(int32 SizeX)
|
TArrayView<const FSamplePeak> FSamplePatternInstanceWaveformHandle::GetWaveform(int32 SizeX)
|
||||||
{
|
{
|
||||||
FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget->GetPatternInstance();
|
if (!PatternInstanceWidget.IsValid())
|
||||||
TRange<AudioFrame> FrameViewRange = PatternInstanceWidget->GetFrameViewRange();
|
return TArrayView<const FSamplePeak>();
|
||||||
|
const FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget.Pin()->GetPatternInstance();
|
||||||
const FSampler* Sampler = Instance->GetInstanceOwner()->GetSampler();
|
const FSampler* Sampler = Instance->GetInstanceOwner()->GetSampler();
|
||||||
const TRange<AudioFrame>& TimeRange = Instance->TimeRange;
|
|
||||||
|
|
||||||
if (!RenderData)
|
if (!RenderData)
|
||||||
{
|
{
|
||||||
RenderData = new FWaveform(7, 256 * 128);
|
RenderData = new FWaveform(7, 256 * 128);
|
||||||
|
RenderData->OnPostUpdatePeak.AddLambda([this]() { OnRequestUpdate.Broadcast(); });
|
||||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
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();
|
const AudioFrame InstancePos = Instance->GetMidiPos();
|
||||||
FrameViewRange = TRange<AudioFrame>(FrameViewRange.GetLowerBoundValue() - InstancePos, FrameViewRange.GetUpperBoundValue() - InstancePos);
|
FrameViewRange = TRange<AudioFrame>(FrameViewRange.GetLowerBoundValue() - InstancePos, FrameViewRange.GetUpperBoundValue() - InstancePos);
|
||||||
const AudioFrame BeginFrame = FMath::Max(TimeRange.GetLowerBoundValue(), FrameViewRange.GetLowerBoundValue());
|
const AudioFrame BeginFrame = FMath::Max(TimeRange.GetLowerBoundValue(), FrameViewRange.GetLowerBoundValue());
|
||||||
const AudioFrame EndFrame = FMath::Min(TimeRange.GetUpperBoundValue(), FrameViewRange.GetUpperBoundValue());
|
const AudioFrame EndFrame = FMath::Min(TimeRange.GetUpperBoundValue(), FrameViewRange.GetUpperBoundValue());
|
||||||
return RenderData->GetPeakFromRange(0, SizeX, BeginFrame, EndFrame);
|
|
||||||
|
return TRange<AudioFrame>(BeginFrame, EndFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
SSamplePatternInstance::SSamplePatternInstance()
|
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()
|
TSharedRef<SWidget> SSamplePatternInstance::GetViewWidget()
|
||||||
@ -43,6 +73,8 @@ TSharedRef<SWidget> SSamplePatternInstance::GetViewWidget()
|
|||||||
|
|
||||||
void SSamplePatternInstance::RequestUpdate()
|
void SSamplePatternInstance::RequestUpdate()
|
||||||
{
|
{
|
||||||
|
if (WaveformHandle.IsValid() && WaveformHandle->NeedUpdate())
|
||||||
|
Invalidate(EInvalidateWidgetReason::Paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
FReply SSamplePatternInstance::OpenPatternMenu()
|
FReply SSamplePatternInstance::OpenPatternMenu()
|
||||||
|
@ -8,16 +8,22 @@ class SSamplePatternInstance;
|
|||||||
class FSamplePatternInstanceWaveformHandle : public IWaveformHandle
|
class FSamplePatternInstanceWaveformHandle : public IWaveformHandle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance);
|
FSamplePatternInstanceWaveformHandle(TSharedRef<SSamplePatternInstance> InPatternInstance);
|
||||||
SSamplePatternInstance* PatternInstanceWidget;
|
virtual ~FSamplePatternInstanceWaveformHandle() override { delete RenderData; }
|
||||||
|
TWeakPtr<SSamplePatternInstance> PatternInstanceWidget;
|
||||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
||||||
|
virtual bool NeedUpdate() const override;
|
||||||
|
|
||||||
|
TRange<AudioFrame> GetRenderRange() const;
|
||||||
FWaveform* RenderData = nullptr;
|
FWaveform* RenderData = nullptr;
|
||||||
|
TRange<AudioFrame> LastRenderRange;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ARONA_API SSamplePatternInstance : public SPatternInstance
|
class ARONA_API SSamplePatternInstance : public SPatternInstance
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SSamplePatternInstance();
|
SSamplePatternInstance();
|
||||||
|
virtual void Construct(const FArguments& InArgs) override;
|
||||||
virtual TSharedRef<SWidget> GetViewWidget() override;
|
virtual TSharedRef<SWidget> GetViewWidget() override;
|
||||||
virtual void RequestUpdate() override;
|
virtual void RequestUpdate() override;
|
||||||
protected:
|
protected:
|
||||||
|
@ -14,14 +14,24 @@ TArrayView<const FSamplePeak> FSamplePatternWaveformHandle::GetWaveform(int32 Si
|
|||||||
if (!RenderData)
|
if (!RenderData)
|
||||||
{
|
{
|
||||||
RenderData = new FWaveform(1, 1024);
|
RenderData = new FWaveform(1, 1024);
|
||||||
|
RenderData->OnPostUpdatePeak.AddLambda([this]()
|
||||||
|
{
|
||||||
|
OnRequestUpdate.Broadcast();
|
||||||
|
});
|
||||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
||||||
}
|
}
|
||||||
return RenderData->GetPeakFromRange(0, SizeX, 0, Sampler->GetFrameCount());
|
return RenderData->GetPeakFromRange(0, SizeX, 0, Sampler->GetFrameCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FSamplePatternWaveformHandle::NeedUpdate() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SSamplePatternThumbnail::Construct(const FArguments& InArgs, FSampler* InSampler)
|
void SSamplePatternThumbnail::Construct(const FArguments& InArgs, FSampler* InSampler)
|
||||||
{
|
{
|
||||||
WaveformHandle = MakeShared<FSamplePatternWaveformHandle>(InSampler);
|
WaveformHandle = MakeShared<FSamplePatternWaveformHandle>(InSampler);
|
||||||
|
WaveformHandle->OnRequestUpdate.AddLambda([this](){ Invalidate(EInvalidateWidgetReason::Paint); });
|
||||||
ChildSlot
|
ChildSlot
|
||||||
[
|
[
|
||||||
SAssignNew(WaveformViewer, SWaveformViewer)
|
SAssignNew(WaveformViewer, SWaveformViewer)
|
||||||
|
@ -21,6 +21,7 @@ public:
|
|||||||
FSampler* Sampler = nullptr;
|
FSampler* Sampler = nullptr;
|
||||||
FWaveform* RenderData = nullptr;
|
FWaveform* RenderData = nullptr;
|
||||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
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))
|
.Alignment(FVector2D(0, 0))
|
||||||
.Offset(Offset)
|
.Offset(Offset)
|
||||||
[
|
[
|
||||||
// SNew(SInvalidationPanel)
|
SNew(SInvalidationPanel)
|
||||||
// [
|
[
|
||||||
PlayListEdit->CreatePatternInstanceWidget(PatternInstance)
|
PlayListEdit->CreatePatternInstanceWidget(PatternInstance)
|
||||||
// ]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,11 +253,12 @@ void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance)
|
|||||||
for (int i = 0; i < Children->Num(); ++i)
|
for (int i = 0; i < Children->Num(); ++i)
|
||||||
{
|
{
|
||||||
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
||||||
// TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
const TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||||
// InvalidationPanel->SetCanCache(false);
|
// 设置CanCache为false, 因为SInvalidationPanel重写了GetChildren;
|
||||||
// TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
InvalidationPanel->SetCanCache(false);
|
||||||
// InvalidationPanel->SetCanCache(true);
|
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||||
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(Widget);
|
InvalidationPanel->SetCanCache(true);
|
||||||
|
const TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||||
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
||||||
{
|
{
|
||||||
Children->RemoveAt(i);
|
Children->RemoveAt(i);
|
||||||
@ -272,13 +273,12 @@ void SPlayListPanel::UpdateAllPatternInstance()
|
|||||||
for (int i = 0; i < Children->Num(); ++i)
|
for (int i = 0; i < Children->Num(); ++i)
|
||||||
{
|
{
|
||||||
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
||||||
TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
const TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
||||||
InvalidationPanel->SetCanCache(false);
|
InvalidationPanel->SetCanCache(false);
|
||||||
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||||
InvalidationPanel->SetCanCache(true);
|
InvalidationPanel->SetCanCache(true);
|
||||||
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
const TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||||
InstanceWidget->RequestUpdate();
|
InstanceWidget->RequestUpdate();
|
||||||
// InstanceWidget->Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "SPlayList.h"
|
#include "SPlayList.h"
|
||||||
|
|
||||||
|
#include "SInvalidationPanel.h"
|
||||||
#include "SlateOptMacros.h"
|
#include "SlateOptMacros.h"
|
||||||
#include "SPlayListTimeLine.h"
|
#include "SPlayListTimeLine.h"
|
||||||
#include "PlayListPanel/SPlayListPanel.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)
|
void SPlayList::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||||
{
|
{
|
||||||
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, 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.SetLowerBoundValue(FMath::FInterpTo<double>(FrameRange.GetLowerBoundValue(), TargetFrameRange.GetLowerBoundValue(), InDeltaTime, 20.f));
|
||||||
FrameRange.SetUpperBoundValue(FMath::FInterpTo<double>(FrameRange.GetUpperBoundValue(), TargetFrameRange.GetUpperBoundValue(), 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)
|
FReply SPlayList::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
#include "WaveformViewer.h"
|
#include "WaveformViewer.h"
|
||||||
|
|
||||||
#include "Async.h"
|
|
||||||
#include "ExecutionTime.h"
|
|
||||||
#include "MaxElement.h"
|
|
||||||
#include "SlateComputeShader.h"
|
|
||||||
#include "SlatePixelShader.h"
|
#include "SlatePixelShader.h"
|
||||||
#include "Thumbnail.h"
|
|
||||||
#include "Pattern/SamplePattern.h"
|
|
||||||
#include "Pattern/SamplePatternInstance.h"
|
|
||||||
|
|
||||||
SWaveformViewer::SWaveformViewer()
|
SWaveformViewer::SWaveformViewer()
|
||||||
{
|
{
|
||||||
@ -53,8 +46,6 @@ void SWaveformViewer::Construct(const FArguments& InArgs)
|
|||||||
void SWaveformViewer::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
void SWaveformViewer::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||||
{
|
{
|
||||||
SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||||
if (UpdateQueue.Peek())
|
|
||||||
Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FSamplePeak> ScaleWaveform(const TArrayView<const FSamplePeak>& Waveform, const int32 SizeX)
|
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 FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId,
|
||||||
const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||||
{
|
{
|
||||||
|
if (WaveformHandle.IsValid())
|
||||||
{
|
{
|
||||||
const FIntPoint& Size = FIntPoint(AllottedGeometry.Size.X, AllottedGeometry.Size.Y);
|
const FIntPoint& Size = FIntPoint(AllottedGeometry.Size.X, AllottedGeometry.Size.Y);
|
||||||
if (Size.X <= 0 || Size.Y <= 0) return LayerId;
|
if (Size.X <= 0 || Size.Y <= 0) return LayerId;
|
||||||
|
@ -9,7 +9,10 @@ class IWaveformHandle
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IWaveformHandle() = default;
|
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
|
class ARONA_API SWaveformViewer : public SLeafWidget
|
||||||
@ -34,14 +37,6 @@ private:
|
|||||||
TSharedPtr<FSlateShaderParam> Params;
|
TSharedPtr<FSlateShaderParam> Params;
|
||||||
TSharedPtr<IComputeShaderSlateElement> ComputeElement;
|
TSharedPtr<IComputeShaderSlateElement> ComputeElement;
|
||||||
|
|
||||||
struct UpdateTask
|
|
||||||
{
|
|
||||||
TArray<float> Array;
|
|
||||||
FIntPoint Size;
|
|
||||||
float LineUV;
|
|
||||||
};
|
|
||||||
mutable TQueue<UpdateTask, EQueueMode::Mpsc> UpdateQueue;
|
|
||||||
|
|
||||||
struct FCSParam
|
struct FCSParam
|
||||||
{
|
{
|
||||||
FLinearColor WaveColor;
|
FLinearColor WaveColor;
|
||||||
|
@ -7,17 +7,29 @@ class MidiTick;
|
|||||||
class AudioFrame
|
class AudioFrame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef double ValueType;
|
||||||
AudioFrame() : Pos(0) {}
|
AudioFrame() : Pos(0) {}
|
||||||
AudioFrame(const double InFrame) : Pos(InFrame) {}
|
AudioFrame(const double InFrame) : Pos(InFrame) {}
|
||||||
AudioFrame(const MidiTick& InMidiTicks);
|
AudioFrame(const MidiTick& InMidiTicks);
|
||||||
|
|
||||||
AudioFrame operator=(const double& InPos) { Pos = InPos; return *this; }
|
template<typename T>
|
||||||
AudioFrame operator+(const double& InFrame) const { return Pos + InFrame; }
|
AudioFrame operator=(const T& InPos) { Pos = InPos; return *this; }
|
||||||
AudioFrame operator-(const double& InFrame) const { return Pos - InFrame; }
|
template<typename T>
|
||||||
AudioFrame operator*(const double& InFrame) const { return Pos * InFrame; }
|
AudioFrame operator+(const T& InFrame) const { return Pos + InFrame; }
|
||||||
AudioFrame operator/(const double& InFrame) const { return Pos / InFrame; }
|
template<typename T>
|
||||||
AudioFrame& operator+=(const double& InFrame) { Pos += InFrame; return *this; }
|
AudioFrame operator-(const T& InFrame) const { return Pos - InFrame; }
|
||||||
AudioFrame& operator-=(const double& InFrame) { Pos -= InFrame; 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) { 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; }
|
||||||
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; }
|
||||||
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);
|
void SetTick(MidiTick InMidiTicks);
|
||||||
MidiTick Ticks() const;
|
MidiTick Ticks() const;
|
||||||
@ -36,7 +48,7 @@ public:
|
|||||||
static AudioFrame FromFrames(double InFrames, float InFramesPerTick);
|
static AudioFrame FromFrames(double InFrames, float InFramesPerTick);
|
||||||
static double FrameToMilliseconds(double InFrame);
|
static double FrameToMilliseconds(double InFrame);
|
||||||
|
|
||||||
double Pos;
|
ValueType Pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MidiTick
|
class MidiTick
|
||||||
|
@ -31,20 +31,7 @@ public:
|
|||||||
static bool ParseString(const FString& String, T& Out);
|
static bool ParseString(const FString& String, T& Out);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static int32 GetValueArray(const FString& Section, const FString& Key, TArray<T>& Out)
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
FConfigFile Config;
|
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(FLinearColor, ParseLinearColor);
|
||||||
ARONA_CONFIG_PARSE_STRING_FUNC(FColor, ParseColor);
|
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_PARSE_STRING_FUNC
|
||||||
#undef ARONA_CONFIG_GET_VALUE_FUNC
|
#undef ARONA_CONFIG_GET_VALUE_FUNC
|
@ -28,7 +28,7 @@ FMidiPattern::~FMidiPattern()
|
|||||||
void FMidiPattern::Process(AudioFrame PatternPos, uint32 InLength)
|
void FMidiPattern::Process(AudioFrame PatternPos, uint32 InLength)
|
||||||
{
|
{
|
||||||
UE::TScopeLock SpinLock(SequenceLock);
|
UE::TScopeLock SpinLock(SequenceLock);
|
||||||
AudioFrame PatternEndFrame = PatternPos + (double)InLength;
|
AudioFrame PatternEndFrame = PatternPos + InLength;
|
||||||
const MidiTick PatternEndPos = PatternEndFrame.Ticks();
|
const MidiTick PatternEndPos = PatternEndFrame.Ticks();
|
||||||
const MidiTick PatternBeginPos = PatternPos.Ticks();
|
const MidiTick PatternBeginPos = PatternPos.Ticks();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
DEFINE_LOG_CATEGORY(LogVST2PluginHost);
|
DEFINE_LOG_CATEGORY(LogVST2PluginHost);
|
||||||
|
|
||||||
ARONACORE_API static TMap<FString, TSharedPtr<FVST2PluginHandle>> PluginHandles;
|
static TMap<FString, TSharedPtr<FVST2PluginHandle>> PluginHandles;
|
||||||
|
|
||||||
TSharedPtr<FVST2PluginHandle> TryLoadHandle(FString Path)
|
TSharedPtr<FVST2PluginHandle> TryLoadHandle(FString Path)
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ FPortAudioAPI::FPortAudioAPI() : InputParameters(), OutputParameters()
|
|||||||
void FPortAudioAPI::Init()
|
void FPortAudioAPI::Init()
|
||||||
{
|
{
|
||||||
Pa_Initialize();
|
Pa_Initialize();
|
||||||
OpenStream(0, 20);
|
OpenStream(0, Pa_GetDefaultOutputDevice());
|
||||||
// OpenStream(0, 1);
|
// OpenStream(0, 1);
|
||||||
LogAllDevices();
|
LogAllDevices();
|
||||||
UE_LOG(PortAudioAPILog, Log, TEXT("PortAudioAPI Initialized"));
|
UE_LOG(PortAudioAPILog, Log, TEXT("PortAudioAPI Initialized"));
|
||||||
@ -47,7 +47,7 @@ void FPortAudioAPI::Release()
|
|||||||
|
|
||||||
void FPortAudioAPI::OpenStream(int32 InputDevice, int32 OutputDevice)
|
void FPortAudioAPI::OpenStream(int32 InputDevice, int32 OutputDevice)
|
||||||
{
|
{
|
||||||
PaStreamParameters* Input = nullptr;
|
const PaStreamParameters* Input = nullptr;
|
||||||
if (InputDevice > 0)
|
if (InputDevice > 0)
|
||||||
{
|
{
|
||||||
InputParameters.device = InputDevice;
|
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