很早之前的修改,我也忘了改什么了

This commit is contained in:
Nanako 2024-07-08 01:19:49 +08:00
parent 55d6cdaade
commit 77e386c0e5
28 changed files with 5558 additions and 96 deletions

4962
Arona.sln

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"FileVersion": 3,
"EngineAssociation": "{15DECA3A-4D95-597C-AAD4-38A8F659756E}",
"EngineAssociation": "{35222BC2-4318-BEC6-472A-AAA5F298656C}",
"Category": "",
"Description": "",
"Modules": [

View File

@ -1,2 +1,6 @@
[Skin]
Skin=Default
[Sample]
Path="F:\Sample"
Path="F:\Vocaloid DB\Miku V4x\MikuV4x\BCNFCY43LB2LZCD4"

View File

@ -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

View File

@ -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"
}
);
}
}
}

View File

@ -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

View File

@ -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;
@ -60,7 +63,21 @@ FWaveform::~FWaveform()
}
void FWaveform::UpdatePeak(const TArray<TArray64<float>>& 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;
});
}

View File

@ -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;
};

View 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)
{
}
}

View 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;
};

View File

@ -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)
{

View File

@ -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;

View File

@ -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()

View File

@ -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:

View File

@ -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)

View File

@ -21,6 +21,7 @@ public:
FSampler* Sampler = nullptr;
FWaveform* RenderData = nullptr;
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
virtual bool NeedUpdate() const override;
};
/**

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;

View File

@ -10,6 +10,9 @@ class IWaveformHandle
public:
virtual ~IWaveformHandle() = default;
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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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)
{

View File

@ -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;

View 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");
}

View 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;
};