优化Waveform渲染性能
This commit is contained in:
parent
e79b8f3ed2
commit
55d6cdaade
@ -5,24 +5,26 @@ cbuffer Params : register(b0)
|
||||
float4 WaveColor;
|
||||
float4 BgColor;
|
||||
float LineUV;
|
||||
float PeaksPerPixel;
|
||||
};
|
||||
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void Main(uint3 id : SV_DispatchThreadID)
|
||||
{
|
||||
uint width, height;
|
||||
Result.GetDimensions(width, height);
|
||||
|
||||
// float2 uv = float2(id.xy / float2(width, height));
|
||||
// uv.y = abs(((1.0 - uv.y) - 0.5) * 2); // 居中
|
||||
|
||||
int X = id.x;
|
||||
int Y = id.y;
|
||||
|
||||
// float4 color = lerp(BgColor, WaveColor, min(step(value.x, uv.y), step(uv.y , value.y)));
|
||||
|
||||
float Top = Samples[X * 2] + 1; // -1;
|
||||
float Bottom = Samples[X * 2 + 1] + 1; // 1;
|
||||
// Resample
|
||||
uint TopIndex = X * 2;
|
||||
uint BottomIndex = TopIndex + 1;
|
||||
float Top = Samples[TopIndex] + 1; // -1;
|
||||
float Bottom = Samples[BottomIndex] + 1; // 1;
|
||||
Top = min(Top, 1);
|
||||
Bottom = max(Bottom, 0);
|
||||
Top *= height;
|
||||
@ -30,13 +32,10 @@ void Main(uint3 id : SV_DispatchThreadID)
|
||||
Bottom *= height;
|
||||
Bottom *= 0.5;
|
||||
|
||||
|
||||
if ((id.y <= Bottom && id.y >= Top) || (Y == height / 2))
|
||||
{
|
||||
Result[id.xy] = WaveColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result[id.xy] = BgColor;
|
||||
}
|
||||
// (id.y >= Top && id.y <= Bottom)
|
||||
float b1 = min(step(Top, Y), step(Y , Bottom));
|
||||
// (Y == height / 2)
|
||||
float b2 = min(step(Y, height / 2), step(height / 2, Y));
|
||||
float b3 = max(b1, b2);
|
||||
Result[id.xy] = lerp(BgColor, WaveColor, b3);
|
||||
}
|
@ -45,12 +45,11 @@ void main()
|
||||
Bottom *= height;
|
||||
Bottom *= 0.5;
|
||||
|
||||
if ((Y <= Bottom && Y >= Top) || (Y == int(height / 2)))
|
||||
{
|
||||
imageStore(Result, pos, WaveColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageStore(Result, pos, BgColor);
|
||||
}
|
||||
|
||||
// (id.y >= Top && id.y <= Bottom)
|
||||
float b1 = min(step(Top, Y), step(Y , Bottom));
|
||||
// (Y == height / 2)
|
||||
float b2 = step(Y, height * LineUV) * step(height * LineUV, Y);
|
||||
float b3 = max(b1, b2);
|
||||
imageStore(Result, pos, lerp(BgColor, WaveColor, b3));
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ int RunArona( const TCHAR* CommandLine )
|
||||
FWindowManager& WindowManager = FWindowManager::Get();
|
||||
FCallRateLimiterManager& RateLimiterManager = FCallRateLimiterManager::Get();
|
||||
|
||||
// AronaTest();
|
||||
AronaTest();
|
||||
|
||||
constexpr float FrameRate = 1.f / 360.0f;
|
||||
FSlateApplication& SlateApplication = FSlateApplication::Get();
|
||||
|
66
Source/Arona/PeakFile/PeakFile.cpp
Normal file
66
Source/Arona/PeakFile/PeakFile.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "PeakFile.h"
|
||||
|
||||
#include "PluginHost/Sampler.h"
|
||||
|
||||
#define MID_TO_LOW (16 * 16)
|
||||
|
||||
TArray<TArray<FSamplePeak>> GenPeakData(const TArray<TArray64<float>>& Data, int32 BlockSize)
|
||||
{
|
||||
TArray<TArray<FSamplePeak>> Out;
|
||||
Out.SetNum(Data.Num());
|
||||
|
||||
for (int64 Channel = 0; Channel < Data.Num(); ++Channel)
|
||||
{
|
||||
const uint64 SourceNum = Data[Channel].Num();
|
||||
uint64 BlockNum = SourceNum / BlockSize;
|
||||
BlockNum += SourceNum % BlockSize ? 1 : 0;
|
||||
|
||||
Out[Channel].SetNum(BlockNum);
|
||||
uint64 Index = 0;
|
||||
for (uint64 j = 0; j < BlockNum; j++)
|
||||
{
|
||||
float Max = FLT_MIN_EXP;
|
||||
float Min = FLT_MAX;
|
||||
|
||||
for (uint64 S = 0; S < BlockSize; ++S)
|
||||
{
|
||||
if (Index >= SourceNum) break;
|
||||
const float& Sample = Data[Channel][Index];
|
||||
if (Max < Sample) Max = Sample;
|
||||
if (Min > Sample) Min = Sample;
|
||||
++Index;
|
||||
}
|
||||
Out[Channel][j].Max = Max;
|
||||
Out[Channel][j].Min = Min;
|
||||
}
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
void FWaveformRenderData::Generate(const TArray<TArray64<float>>& Data)
|
||||
{
|
||||
Buffer = GenPeakData(Data, BlockSize);
|
||||
}
|
||||
|
||||
FWaveform::FWaveform(int32 LevelNum, int32 MaxBlockSize)
|
||||
{
|
||||
RenderData.SetNum(LevelNum);
|
||||
int32 BeginBlock = MaxBlockSize;
|
||||
for (int i = 0; i < LevelNum; ++i)
|
||||
{
|
||||
RenderData[i] = new FWaveformRenderData(BeginBlock);
|
||||
BeginBlock /= 4;
|
||||
}
|
||||
}
|
||||
|
||||
FWaveform::~FWaveform()
|
||||
{
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
delete RenderData[i];
|
||||
}
|
||||
|
||||
void FWaveform::UpdatePeak(const TArray<TArray64<float>>& SampleBuffer)
|
||||
{
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
RenderData[i]->Generate(SampleBuffer);
|
||||
}
|
101
Source/Arona/PeakFile/PeakFile.h
Normal file
101
Source/Arona/PeakFile/PeakFile.h
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
class FSampler;
|
||||
|
||||
struct FSamplePeak
|
||||
{
|
||||
float Min = 0;
|
||||
float Max = 0;
|
||||
|
||||
void operator+=(const FSamplePeak& Other)
|
||||
{
|
||||
if (Min > Other.Min) Min = Other.Min;
|
||||
if (Max < Other.Max) Max = Other.Max;
|
||||
}
|
||||
};
|
||||
|
||||
class FPeakBlock
|
||||
{
|
||||
public:
|
||||
uint16 BlockNum;
|
||||
uint16 Size;
|
||||
uint16 Res;
|
||||
uint16 Resolution;
|
||||
TArray<void*> Buffer;
|
||||
uint16 MaxLevel;
|
||||
};
|
||||
|
||||
struct FWaveformBuffer
|
||||
{
|
||||
TArray<int16> Buffer;
|
||||
void* HiResBuffer;
|
||||
TArray<int8> RMSBuffer;
|
||||
};
|
||||
|
||||
class FWaveformRenderData
|
||||
{
|
||||
public:
|
||||
explicit FWaveformRenderData(int32 BlockSize)
|
||||
: BlockSize(BlockSize)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FWaveformRenderData() = default;
|
||||
virtual void Generate(const TArray<TArray64<float>>& Data);
|
||||
int32 GetMemSize() const
|
||||
{
|
||||
int32 Size = 0;
|
||||
for (const auto& Array : Buffer)
|
||||
{
|
||||
Size += Array.GetAllocatedSize();
|
||||
}
|
||||
return Buffer.GetAllocatedSize() + Size;
|
||||
}
|
||||
const FSamplePeak& GetPeak(const uint16 Channel, const uint64 Frame) const
|
||||
{
|
||||
const int32 BlockIndex = Frame / BlockSize;
|
||||
return Buffer[Channel][BlockIndex];
|
||||
}
|
||||
|
||||
TArrayView<const FSamplePeak> GetPeakFromRange(const uint16 Channel, uint64 StartFrame, uint64 EndFrame) const
|
||||
{
|
||||
const int32 StartBlockIndex = StartFrame / BlockSize;
|
||||
int32 EndBlockIndex = EndFrame / BlockSize;
|
||||
EndBlockIndex = FMath::Min(EndBlockIndex, Buffer[Channel].Num() - 1);
|
||||
|
||||
// TArray<FSamplePeak> Out;
|
||||
// Out.SetNum(EndBlockIndex - StartBlockIndex + 1);
|
||||
// FMemory::Memcpy(Out.GetData(), Buffer[Channel].GetData() + StartBlockIndex, Out.Num() * sizeof(FSamplePeak));
|
||||
return MakeArrayView(&Buffer[Channel][StartBlockIndex], EndBlockIndex - StartBlockIndex + 1);
|
||||
}
|
||||
TArray<TArray<FSamplePeak>> Buffer;
|
||||
const int32 BlockSize; // 每一个块包含多少个Frame
|
||||
};
|
||||
|
||||
class FWaveform
|
||||
{
|
||||
public:
|
||||
FWaveform(int32 LevelNum, int32 MaxBlockSize = 128 * 128);
|
||||
~FWaveform();
|
||||
|
||||
void UpdatePeak(const TArray<TArray64<float>>& SampleBuffer);
|
||||
int32 GetMemSize() const
|
||||
{
|
||||
int32 Size = 0;
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
Size += RenderData[i]->GetMemSize();
|
||||
return Size;
|
||||
}
|
||||
|
||||
TArrayView<const FSamplePeak> GetPeakFromRange(const uint16 Channel, const int32 Width, const uint64 StartFrame, const uint64 EndFrame) const
|
||||
{
|
||||
const uint64 FrameCount = EndFrame - StartFrame;
|
||||
const int32 BlockSize = FrameCount / Width;
|
||||
for (int i = 0; i < RenderData.Num(); ++i)
|
||||
if (RenderData[i]->BlockSize <= BlockSize)
|
||||
return RenderData[i]->GetPeakFromRange(Channel, StartFrame, EndFrame);
|
||||
return RenderData.Last()->GetPeakFromRange(Channel, StartFrame, EndFrame);
|
||||
}
|
||||
TArray<FWaveformRenderData*> RenderData;
|
||||
};
|
@ -13,29 +13,29 @@ void AronaTest()
|
||||
UE_LOG(LogTemp, Log, TEXT("Audio Device%d: %s"), AudioDeviceInfo.DeviceIndex, *AudioDeviceInfo.Name);
|
||||
}
|
||||
|
||||
const FString& MidiFilePath = TEXT("E:\\Projects\\Arona\\脑浆炸裂女孩 - 初音ミク.mid");
|
||||
// const FString& MidiFilePath = TEXT("E:\\Projects\\Arona\\脑浆炸裂女孩 - 初音ミク.mid");
|
||||
// const FString& PluginFilePath = TEXT("D:\\Projects\\Rolling\\4Front Piano x64.dll");
|
||||
const FString& PluginFilePath = TEXT("F:\\VST\\VST64\\4Front Piano x64.dll");
|
||||
// const FString& PluginFilePath = TEXT("F:\\VST\\VST64\\4Front Piano x64.dll");
|
||||
|
||||
FMidiFile Midi;
|
||||
Midi.readFrom(MidiFilePath);
|
||||
// FMidiFile Midi;
|
||||
// Midi.readFrom(MidiFilePath);
|
||||
|
||||
FMidiSequencer& MidiSequencer = FMidiSequencer::Get();
|
||||
// FMidiSequencer& MidiSequencer = FMidiSequencer::Get();
|
||||
// MidiSequencer.SetTicksPerQuarter(Midi.getTimeFormat());
|
||||
|
||||
FMidiPattern* MidiPattern = MidiSequencer.NewMidiPattern();
|
||||
MidiPattern->Name = TEXT("脑浆炸裂女孩 - 初音ミク");
|
||||
// FMidiPattern* MidiPattern = MidiSequencer.NewMidiPattern();
|
||||
// MidiPattern->Name = TEXT("脑浆炸裂女孩 - 初音ミク");
|
||||
|
||||
for (int i = 0; i < Midi.getNumTracks(); ++i)
|
||||
{
|
||||
FPluginHost* Plugin = FPluginHostList::Get().TryLoadPlugin(PluginFilePath);
|
||||
FPluginHostList::Get().RegisterInstrument(Plugin);
|
||||
|
||||
const FMidiMessageSequence* Track = Midi.getTrack(i);
|
||||
FMidiMessageSequence& Sequence = MidiPattern->GetSequence(Plugin);
|
||||
Sequence.addSequence(*Track, 0);
|
||||
Sequence.updateMatchedPairs();
|
||||
}
|
||||
// for (int i = 0; i < Midi.getNumTracks(); ++i)
|
||||
// {
|
||||
// FPluginHost* Plugin = FPluginHostList::Get().TryLoadPlugin(PluginFilePath);
|
||||
// FPluginHostList::Get().RegisterInstrument(Plugin);
|
||||
//
|
||||
// const FMidiMessageSequence* Track = Midi.getTrack(i);
|
||||
// FMidiMessageSequence& Sequence = MidiPattern->GetSequence(Plugin);
|
||||
// Sequence.addSequence(*Track, 0);
|
||||
// Sequence.updateMatchedPairs();
|
||||
// }
|
||||
// FPluginHost* Kontakt = FPluginHostList::Get().TryLoadPlugin(TEXT("I:\\VST\\VST64\\Kontakt.dll"));
|
||||
// FPluginHostList::Get().RegisterInstrument(Kontakt);
|
||||
|
||||
|
@ -91,20 +91,6 @@ void FMainWindow::Init()
|
||||
[
|
||||
SAssignNew(MainWindowCanvas, SConstraintCanvas)
|
||||
]
|
||||
+SOverlay::Slot()
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+SHorizontalBox::Slot()
|
||||
[
|
||||
SNew(SWaveformViewer)
|
||||
.Visibility(EVisibility::SelfHitTestInvisible)
|
||||
].FillWidth(1.f)
|
||||
// +SHorizontalBox::Slot()
|
||||
// [
|
||||
// SNew(SWaveformViewer)
|
||||
// .Visibility(EVisibility::SelfHitTestInvisible)
|
||||
// ].FillWidth(1.f)
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -20,6 +20,7 @@ void SPatternInstance::Construct(const FArguments& InArgs)
|
||||
PatternInstance = InArgs._PatternInstance;
|
||||
FrameToPixel = InArgs._FrameToPixel;
|
||||
SnapFrame = InArgs._SnapFrame;
|
||||
FrameViewRange = InArgs._FrameViewRange;
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
@ -57,7 +58,7 @@ void SPatternInstance::Construct(const FArguments& InArgs)
|
||||
+SVerticalBox::Slot()
|
||||
.FillHeight(1.f)
|
||||
[
|
||||
InArgs._View
|
||||
GetViewWidget()
|
||||
]
|
||||
]
|
||||
];
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
SLATE_ATTRIBUTE(float, FrameToPixel)
|
||||
|
||||
SLATE_ATTRIBUTE(AudioFrame, SnapFrame)
|
||||
SLATE_ARGUMENT(TSharedRef<SWidget>, View)
|
||||
SLATE_ATTRIBUTE(TRange<AudioFrame>, FrameViewRange)
|
||||
|
||||
SLATE_END_ARGS()
|
||||
|
||||
@ -49,8 +49,12 @@ public:
|
||||
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
FPatternInstance* GetPatternInstance() const { return PatternInstance; }
|
||||
virtual TSharedRef<SWidget> GetViewWidget() = 0;
|
||||
virtual void RequestUpdate() = 0;
|
||||
|
||||
FPatternInstance* GetPatternInstance() const { return PatternInstance; }
|
||||
TRange<AudioFrame> GetTimeRange() const { return PatternInstance->TimeRange; }
|
||||
TRange<AudioFrame> GetFrameViewRange() const { return FrameViewRange.Get(); }
|
||||
float ResizeHandleSize = 5.f;
|
||||
protected:
|
||||
virtual FReply OpenPatternMenu() { return FReply::Unhandled(); }
|
||||
@ -61,6 +65,7 @@ protected:
|
||||
|
||||
TAttribute<float> FrameToPixel;
|
||||
TAttribute<AudioFrame> SnapFrame;
|
||||
TAttribute<TRange<AudioFrame>> FrameViewRange;
|
||||
|
||||
FVector2f MouseDownPos; // 屏幕坐标
|
||||
AudioFrame BeginPatternEnd;
|
||||
|
51
Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.cpp
Normal file
51
Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "SSamplePatternInstance.h"
|
||||
|
||||
#include "Pattern/SamplePatternInstance.h"
|
||||
#include "UI/Widget/Thumbnail.h"
|
||||
#include "UI/Widget/WaveformViewer.h"
|
||||
|
||||
FSamplePatternInstanceWaveformHandle::FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance):
|
||||
PatternInstanceWidget(InPatternInstance)
|
||||
{
|
||||
}
|
||||
|
||||
TArrayView<const FSamplePeak> FSamplePatternInstanceWaveformHandle::GetWaveform(int32 SizeX)
|
||||
{
|
||||
FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget->GetPatternInstance();
|
||||
TRange<AudioFrame> FrameViewRange = PatternInstanceWidget->GetFrameViewRange();
|
||||
const FSampler* Sampler = Instance->GetInstanceOwner()->GetSampler();
|
||||
const TRange<AudioFrame>& TimeRange = Instance->TimeRange;
|
||||
|
||||
if (!RenderData)
|
||||
{
|
||||
RenderData = new FWaveform(7, 256 * 128);
|
||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
||||
const float MemSize = RenderData->GetMemSize();
|
||||
UE_LOG(LogTemp, Warning, TEXT("Waveform MemSize: %f MB"), MemSize / 1024 / 1024);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
SSamplePatternInstance::SSamplePatternInstance()
|
||||
{
|
||||
WaveformHandle = MakeShared<FSamplePatternInstanceWaveformHandle>(this);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SSamplePatternInstance::GetViewWidget()
|
||||
{
|
||||
return SAssignNew(WaveformViewer, SWaveformViewer)
|
||||
.WaveformHandle(WaveformHandle);
|
||||
}
|
||||
|
||||
void SSamplePatternInstance::RequestUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
FReply SSamplePatternInstance::OpenPatternMenu()
|
||||
{
|
||||
return FReply::Handled();
|
||||
}
|
27
Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.h
Normal file
27
Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "SPatternInstance.h"
|
||||
#include "PeakFile/PeakFile.h"
|
||||
#include "UI/Widget/WaveformViewer.h"
|
||||
|
||||
class SSamplePatternInstance;
|
||||
|
||||
class FSamplePatternInstanceWaveformHandle : public IWaveformHandle
|
||||
{
|
||||
public:
|
||||
FSamplePatternInstanceWaveformHandle(SSamplePatternInstance* InPatternInstance);
|
||||
SSamplePatternInstance* PatternInstanceWidget;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
||||
FWaveform* RenderData = nullptr;
|
||||
};
|
||||
|
||||
class ARONA_API SSamplePatternInstance : public SPatternInstance
|
||||
{
|
||||
public:
|
||||
SSamplePatternInstance();
|
||||
virtual TSharedRef<SWidget> GetViewWidget() override;
|
||||
virtual void RequestUpdate() override;
|
||||
protected:
|
||||
virtual FReply OpenPatternMenu() override;
|
||||
TSharedPtr<FSamplePatternInstanceWaveformHandle> WaveformHandle;
|
||||
TSharedPtr<SWaveformViewer> WaveformViewer;
|
||||
};
|
@ -1 +0,0 @@
|
||||
#include "SWaveformPatternInstance.h"
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
#include "SPatternInstance.h"
|
||||
|
||||
class ARONA_API SWaveformPatternInstance : public SPatternInstance
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "SPatternThumbnail.h"
|
||||
|
||||
#include "Async.h"
|
||||
#include "SInvalidationPanel.h"
|
||||
#include "SlateOptMacros.h"
|
||||
#include "SMidiPatternThumbnail.h"
|
||||
@ -44,7 +45,6 @@ void SAutoPatternThumbnail::SetPattern(FPattern* InPattern)
|
||||
Thumbnail.ToSharedRef()
|
||||
]
|
||||
];
|
||||
Thumbnail->Redraw();
|
||||
}
|
||||
|
||||
FReply SAutoPatternThumbnail::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
@ -64,6 +64,16 @@ FVector2D SAutoPatternThumbnail::ComputeDesiredSize(float LayoutScaleMultiplier)
|
||||
return FVector2D(0, 36) * LayoutScaleMultiplier;
|
||||
}
|
||||
|
||||
void SAutoPatternThumbnail::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
if (bNeedRedraw)
|
||||
{
|
||||
Thumbnail->Redraw();
|
||||
bNeedRedraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
TSharedPtr<SPatternThumbnail> SAutoPatternThumbnail::CreateThumbnail(FPattern* InPattern)
|
||||
{
|
||||
TSharedPtr<SPatternThumbnail> Out;
|
||||
|
@ -59,10 +59,12 @@ public:
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual FVector2D ComputeDesiredSize(float LayoutScaleMultiplier) const override;
|
||||
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
||||
FPatternDelegate OnPatternClicked;
|
||||
private:
|
||||
TSharedPtr<SPatternThumbnail> CreateThumbnail(FPattern* InPattern);
|
||||
|
||||
TSharedPtr<SPatternThumbnail> Thumbnail;
|
||||
FPattern* Pattern = nullptr;
|
||||
bool bNeedRedraw = true;
|
||||
};
|
||||
|
@ -3,32 +3,29 @@
|
||||
|
||||
#include "SSamplePatternThumbnail.h"
|
||||
|
||||
#include "Async.h"
|
||||
#include "SlateApplication.h"
|
||||
#include "SlateOptMacros.h"
|
||||
#include "ExecutionTime.h"
|
||||
#include "Pattern/SamplePatternInstance.h"
|
||||
#include "PluginHost/Sampler.h"
|
||||
#include "UI/Widget/SUpdatableImage.h"
|
||||
#include "UI/Widget/Thumbnail.h"
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
TArray<float> FSampleWaveformHandle::GetWaveform(int32 SizeX) const
|
||||
TArrayView<const FSamplePeak> FSamplePatternWaveformHandle::GetWaveform(int32 SizeX)
|
||||
{
|
||||
|
||||
const FSampler* Sampler = SampleInstance->GetInstanceOwner()->GetSampler();
|
||||
TArray<TArray64<float>> Copy = Sampler->GetSampleBuffer(); // 拷贝以防在渲染时被修改
|
||||
uint32 Count = Sampler->GetFrameCount();
|
||||
TRange<uint32> Range = SampleInstance->GetRange();
|
||||
return Thumbnail::GenerateWaveformData(SizeX, Copy, Count, Range);
|
||||
if (!RenderData)
|
||||
{
|
||||
RenderData = new FWaveform(1, 1024);
|
||||
RenderData->UpdatePeak(Sampler->GetSampleBuffer());
|
||||
}
|
||||
return RenderData->GetPeakFromRange(0, SizeX, 0, Sampler->GetFrameCount());
|
||||
}
|
||||
|
||||
void SSamplePatternThumbnail::Construct(const FArguments& InArgs, FSampler* InSampler)
|
||||
{
|
||||
WaveformHandle = MakeShared<FSamplePatternWaveformHandle>(InSampler);
|
||||
ChildSlot
|
||||
[
|
||||
|
||||
SAssignNew(WaveformViewer, SWaveformViewer)
|
||||
.WaveformHandle(WaveformHandle)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,16 @@
|
||||
class FSampler;
|
||||
|
||||
|
||||
class FSampleWaveformHandle : public IWaveformHandle
|
||||
class FSamplePatternWaveformHandle : public IWaveformHandle
|
||||
{
|
||||
public:
|
||||
FSamplePatternInstance* SampleInstance = nullptr;
|
||||
virtual TArray<float> GetWaveform(int32 SizeX) const override;
|
||||
FSamplePatternWaveformHandle(FSampler* InSampler)
|
||||
: Sampler(InSampler)
|
||||
{
|
||||
}
|
||||
FSampler* Sampler = nullptr;
|
||||
FWaveform* RenderData = nullptr;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) override;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -33,5 +38,6 @@ public:
|
||||
|
||||
virtual void Redraw() override;
|
||||
private:
|
||||
FSampleWaveformHandle* WaveformHandle = nullptr;
|
||||
TSharedPtr<FSamplePatternWaveformHandle> WaveformHandle;
|
||||
TSharedPtr<SWaveformViewer> WaveformViewer;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "EditTool/PlayListEditTool_Select.h"
|
||||
#include "EditTool/PlayListEditTool_Write.h"
|
||||
#include "UI/Widget/Pattern/SPatternInstance.h"
|
||||
#include "UI/Widget/Pattern/SSamplePatternInstance.h"
|
||||
#include "UI/Widget/PlayList/PlayListPanel/SPlayListPanel.h"
|
||||
|
||||
FPlayListEdit::FPlayListEdit(TSharedRef<SPlayListPanel> InPlayListPanel)
|
||||
@ -155,14 +156,25 @@ FReply FPlayListEdit::OnPatternInstanceReleased(FPatternInstanceClickData D)
|
||||
|
||||
TSharedRef<SPatternInstance> FPlayListEdit::CreatePatternInstanceWidget(FPatternInstance* PatternInstance)
|
||||
{
|
||||
const TSharedRef<SPatternInstance> InstanceWidget = SNew(SPatternInstance)
|
||||
.Texture(UpdatableTexture)
|
||||
.PatternInstance(PatternInstance)
|
||||
.FrameToPixel_Raw(this, &FPlayListEdit::GetSampleToPixelScaler)
|
||||
.SnapFrame_Raw(this, &FPlayListEdit::GetSnapFrame)
|
||||
.Clipping(EWidgetClipping::ClipToBoundsAlways);
|
||||
TSharedPtr<SPatternInstance> PatternInstanceWidget;
|
||||
|
||||
switch (PatternInstance->GetOwner()->Type)
|
||||
{
|
||||
case EPatternType::Midi:
|
||||
break;
|
||||
case EPatternType::Automation:
|
||||
break;
|
||||
case EPatternType::Sample:
|
||||
PatternInstanceWidget = SNew(SSamplePatternInstance)
|
||||
.PatternInstance(PatternInstance)
|
||||
.FrameViewRange(PlayListPanel.Pin()->GetViewRange())
|
||||
.FrameToPixel_Raw(this, &FPlayListEdit::GetSampleToPixelScaler)
|
||||
.SnapFrame_Raw(this, &FPlayListEdit::GetSnapFrame)
|
||||
.Clipping(EWidgetClipping::ClipToBoundsAlways);
|
||||
break;
|
||||
}
|
||||
|
||||
return InstanceWidget;
|
||||
return PatternInstanceWidget.ToSharedRef();
|
||||
}
|
||||
|
||||
AudioFrame FPlayListEdit::GetSnapFrame() const
|
||||
|
@ -59,12 +59,6 @@ int32 SPlayListPanel::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG
|
||||
const AudioFrame RangeBegin = Range.GetLowerBoundValue();
|
||||
const AudioFrame RangeEnd = Range.GetUpperBoundValue();
|
||||
|
||||
if (LastSize != Size)
|
||||
{
|
||||
LastSize = Size;
|
||||
PlayListEdit->UpdateTexture(FrameToPixelScaler, TrackHeightManager.TrackHeight.Get());
|
||||
}
|
||||
|
||||
++LayerId;
|
||||
// 绘制轨道间隔线
|
||||
for (int i = 0; i < TrackCount; ++i)
|
||||
@ -107,6 +101,7 @@ int32 SPlayListPanel::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG
|
||||
);
|
||||
}
|
||||
|
||||
++LayerId;
|
||||
const AudioFrame BeatDelta = FMath::Fmod(RangeBegin, BeatFrameCount);
|
||||
for (float i = RangeBegin - BeatDelta; i < RangeEnd; i += BeatFrameCount)
|
||||
{
|
||||
@ -115,7 +110,7 @@ int32 SPlayListPanel::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG
|
||||
Points.Add(FVector2f((i - RangeBegin) * FrameToPixelScaler, Size.Y));
|
||||
FSlateDrawElement::MakeLines(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(),
|
||||
Points,
|
||||
ESlateDrawEffect::None,
|
||||
@ -144,7 +139,7 @@ int32 SPlayListPanel::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG
|
||||
}
|
||||
|
||||
LayerId = PlayListEdit->OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
||||
|
||||
|
||||
return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
||||
}
|
||||
|
||||
@ -214,16 +209,24 @@ AudioFrame SPlayListPanel::GetPixelToSampleScaler() const
|
||||
|
||||
FMargin SPlayListPanel::CalculatePatternInstancePos(FPatternInstance* PatternInstance) const
|
||||
{
|
||||
// const double Scaler = GetSampleToPixelScaler();
|
||||
const double Scaler = GetSampleToPixelScaler();
|
||||
check(PatternInstance->TrackIndex >= 0)
|
||||
|
||||
|
||||
const TRange<AudioFrame>& Range = ViewRange.Get();
|
||||
|
||||
FMargin Offset;
|
||||
Offset.Left = (PatternInstance->Pos - Range.GetLowerBoundValue()).Pos * GetSampleToPixelScaler(); // X
|
||||
const AudioFrame XOffset = PatternInstance->Pos - Range.GetLowerBoundValue();
|
||||
|
||||
Offset.Left = XOffset.Pos * Scaler; // X
|
||||
Offset.Left = FMath::Max(Offset.Left, 0.0f);
|
||||
Offset.Top = TrackHeightManager.GetTrackPos(PatternInstance->TrackIndex); // Y
|
||||
|
||||
Offset.Bottom = TrackHeightManager.GetTrackHeight(PatternInstance->TrackIndex); // Height
|
||||
Offset.Right = PatternInstance->GetLength().Pos * GetSampleToPixelScaler(); // Width
|
||||
|
||||
float LeftWidth = (PatternInstance->GetLength() + PatternInstance->Pos) - Range.GetLowerBoundValue();
|
||||
LeftWidth = FMath::Min(PatternInstance->GetLength().Pos, LeftWidth);
|
||||
Offset.Right = LeftWidth * Scaler; // Width
|
||||
Offset.Right = FMath::Min(Offset.Right, GetTickSpaceGeometry().Size.X - Offset.Left);
|
||||
|
||||
return Offset;
|
||||
}
|
||||
@ -237,14 +240,33 @@ void SPlayListPanel::OnCreatePatternInstance(FPatternInstance* PatternInstance)
|
||||
.Alignment(FVector2D(0, 0))
|
||||
.Offset(Offset)
|
||||
[
|
||||
SNew(SInvalidationPanel)
|
||||
[
|
||||
// SNew(SInvalidationPanel)
|
||||
// [
|
||||
PlayListEdit->CreatePatternInstanceWidget(PatternInstance)
|
||||
]
|
||||
// ]
|
||||
];
|
||||
}
|
||||
|
||||
void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance)
|
||||
{
|
||||
TPanelChildren<SConstraintCanvas::FSlot>* Children = (TPanelChildren<SConstraintCanvas::FSlot>*)TrackCanvas->GetChildren();
|
||||
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);
|
||||
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
||||
{
|
||||
Children->RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SPlayListPanel::UpdateAllPatternInstance()
|
||||
{
|
||||
TPanelChildren<SConstraintCanvas::FSlot>* Children = (TPanelChildren<SConstraintCanvas::FSlot>*)TrackCanvas->GetChildren();
|
||||
for (int i = 0; i < Children->Num(); ++i)
|
||||
@ -255,11 +277,8 @@ void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance)
|
||||
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
||||
InvalidationPanel->SetCanCache(true);
|
||||
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
||||
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
||||
{
|
||||
Children->RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
InstanceWidget->RequestUpdate();
|
||||
// InstanceWidget->Invalidate(EInvalidateWidgetReason::Paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "FTrackHeightManager.h"
|
||||
#include "PlayListPatternTextureManager.h"
|
||||
#include "PlayListSelector.h"
|
||||
#include "SCompoundWidget.h"
|
||||
#include "Pattern/PatternInstance.h"
|
||||
@ -49,8 +48,10 @@ public:
|
||||
float GetMidiTickToPixelScaler() const { return 0.125f; }
|
||||
AudioFrame GetSampleToPixelScaler() const;
|
||||
AudioFrame GetPixelToSampleScaler() const;
|
||||
TAttribute<TRange<AudioFrame>> GetViewRange() const { return ViewRange; }
|
||||
|
||||
virtual TSharedRef<SWidget> GetWidget() override { return AsShared(); }
|
||||
void UpdateAllPatternInstance();
|
||||
|
||||
FTrackHeightManager TrackHeightManager;
|
||||
|
||||
@ -62,8 +63,6 @@ private:
|
||||
void OnCreatePatternInstance(FPatternInstance* PatternInstance);
|
||||
void OnDeletePatternInstance(FPatternInstance* PatternInstance);
|
||||
|
||||
mutable FVector2f LastSize;
|
||||
|
||||
TSharedPtr<SConstraintCanvas> TrackCanvas;
|
||||
TSharedPtr<FPlayListEdit> PlayListEdit;
|
||||
|
||||
|
@ -79,7 +79,7 @@ FReply SPlayList::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent&
|
||||
{
|
||||
if (Zooming)
|
||||
{
|
||||
const AudioFrame MinRange = FMidiSequencer::Get().GetBar();
|
||||
const AudioFrame MinRange = FMidiSequencer::Get().GetBeat() * 4;
|
||||
|
||||
const TRange<AudioFrame>& CurrentRange = TargetFrameRange;
|
||||
const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel() * 2; // 乘以2是因为MousePosPercent
|
||||
|
@ -200,8 +200,6 @@ namespace Thumbnail
|
||||
|
||||
const double SampleZoom = (double)FrameRange.Size<int32>() / Width;
|
||||
|
||||
// const int32& BeginX = FMath::FloorToInt(BeginFrame / SampleZoom);
|
||||
// const int32& EndX = FMath::Min(Width - 1, FMath::FloorToInt(EndFrame / SampleZoom));
|
||||
for (int32 X = 0; X < Width; ++X)
|
||||
{
|
||||
if (FrameIndex >= EndFrame || FrameIndex >= FullFrameCount) break;
|
||||
@ -252,6 +250,7 @@ namespace Thumbnail
|
||||
const int64 BeginFrame = FrameRange.GetLowerBoundValue();
|
||||
const int64 EndFrame = FrameRange.GetUpperBoundValue();
|
||||
int64 FrameIndex = BeginFrame;
|
||||
const uint16 ChannelCount = SampleBuffer.Num();
|
||||
|
||||
const double SampleZoom = (double)FrameRange.Size<int32>() / Width;
|
||||
for (int32 X = 0; X < Width; ++X)
|
||||
@ -259,16 +258,19 @@ namespace Thumbnail
|
||||
if (FrameIndex >= EndFrame || FrameIndex >= FullFrameCount) break;
|
||||
|
||||
float cand_min = FLT_MAX;
|
||||
float cand_max = FLT_MIN;
|
||||
float cand_max = FLT_MIN_EXP;
|
||||
int total_advance = SampleZoom;
|
||||
do
|
||||
{
|
||||
if (FrameIndex >= EndFrame || FrameIndex >= FullFrameCount) break;
|
||||
const float& Sample = SampleBuffer[0][FrameIndex];
|
||||
if (cand_min > Sample)
|
||||
cand_min = Sample;
|
||||
if (cand_max < Sample)
|
||||
cand_max = Sample;
|
||||
for (uint16 Channel = 0; Channel < ChannelCount; ++Channel)
|
||||
{
|
||||
const float& Sample = SampleBuffer[Channel][FrameIndex];
|
||||
if (cand_min > Sample)
|
||||
cand_min = Sample;
|
||||
if (cand_max < Sample)
|
||||
cand_max = Sample;
|
||||
}
|
||||
if (total_advance > 0)
|
||||
FrameIndex++;
|
||||
}
|
||||
|
@ -1,32 +1,37 @@
|
||||
#include "WaveformViewer.h"
|
||||
#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()
|
||||
{
|
||||
Param.WaveColor = FLinearColor(0.0f, 1.0f, 0.0f, 1.0f).ToFColorSRGB();
|
||||
Param.BgColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f).ToFColorSRGB();
|
||||
Param.LineUV = 0.0f;
|
||||
}
|
||||
|
||||
void SWaveformViewer::Construct(const FArguments& InArgs)
|
||||
{
|
||||
Sampler.Load(TEXT("F:\\FL垃圾桶\\Kawaii Anokoga Kiniiranai.mp3"));
|
||||
// Sampler.Load(TEXT("F:\\Sample\\cs1.6\\26_Kick_15_95_SP.wav"));
|
||||
const TArray<TArray64<float>>& Buffer = Sampler.GetSampleBuffer();
|
||||
|
||||
Param.WaveColor = FLinearColor(0.0f, 1.0f, 1.0f, 1.0f).ToFColorSRGB();
|
||||
Param.BgColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f).ToFColorSRGB();
|
||||
|
||||
WaveformHandle = InArgs._WaveformHandle;
|
||||
|
||||
FSlateRenderer* Renderer = FSlateApplication::Get().GetRenderer();
|
||||
|
||||
RenderTarget = Renderer->CreateRenderTargetTexture(16, 16, EPixelFormat::PF_R8G8B8A8);
|
||||
RenderTarget->OnResizeDelegate.AddSP(this, &SWaveformViewer::OnResize);
|
||||
RenderTarget = Renderer->CreateRenderTargetTexture(1, 1, EPixelFormat::PF_R8G8B8A8);
|
||||
|
||||
TArray<float> InitData;
|
||||
InitData.SetNumZeroed(2);
|
||||
if (Renderer->GetRendererAPI() == ERendererAPI::D3D11)
|
||||
{
|
||||
Params = Renderer->CreateComputeShaderParam(ComputeShader);
|
||||
Params->Init();
|
||||
Params->AddComputeShaderTextureParameter("Result", RenderTarget, 2);
|
||||
Params->AddComputeShaderParam("Samples", Buffer[0].GetData(), Buffer[0].Num(), 0);
|
||||
Params->AddComputeShaderParam("Samples", InitData.GetData(), InitData.Num(), 0);
|
||||
Params->AddShaderParam("Params", (bool*)&Param, sizeof(FCSParam) / sizeof(bool));
|
||||
|
||||
ComputeShader = Renderer->CreateComputeShader(TEXT("Arona"), TEXT("WaveformCS"));
|
||||
@ -38,7 +43,7 @@ void SWaveformViewer::Construct(const FArguments& InArgs)
|
||||
Params = Renderer->CreateComputeShaderParam(ComputeShader);
|
||||
Params->Init();
|
||||
Params->AddComputeShaderTextureParameter("Result", RenderTarget, 2);
|
||||
Params->AddComputeShaderParam("Samples", Buffer[0].GetData(), Buffer[0].Num(), 0);
|
||||
Params->AddComputeShaderParam("Samples", InitData.GetData(), InitData.Num(), 0);
|
||||
Params->AddComputeShaderParam("Params", (bool*)&Param, sizeof(FCSParam) / sizeof(bool), 1);
|
||||
}
|
||||
|
||||
@ -48,52 +53,54 @@ void SWaveformViewer::Construct(const FArguments& InArgs)
|
||||
void SWaveformViewer::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
UpdateTimer += InDeltaTime;
|
||||
if (UpdateTimer > UpdateInterval && bNeedCompute)
|
||||
{
|
||||
UpdateTimer = 0.0f;
|
||||
if (UpdateQueue.Peek())
|
||||
Invalidate(EInvalidateWidgetReason::Paint);
|
||||
}
|
||||
|
||||
Async(EAsyncExecution::TaskGraph, [this]()
|
||||
{
|
||||
const FIntPoint& Size = RenderTarget->GetSize();
|
||||
UpdateTask Task;
|
||||
Task.LineUV = 1.0f / Size.Y;
|
||||
Task.Array = WaveformHandle->GetWaveform(Size.X);
|
||||
Task.Size = Size;
|
||||
UpdateQueue.Enqueue(Task);
|
||||
});
|
||||
|
||||
bNeedCompute = false;
|
||||
TArray<FSamplePeak> ScaleWaveform(const TArrayView<const FSamplePeak>& Waveform, const int32 SizeX)
|
||||
{
|
||||
TArray<FSamplePeak> Out;
|
||||
Out.SetNum(SizeX);
|
||||
const float Scale = (float)Waveform.Num() / SizeX;
|
||||
float Index = 0;
|
||||
for (int32 i = 0; i < SizeX; ++i)
|
||||
{
|
||||
const int32 Begin = FMath::FloorToInt(Index);
|
||||
const int32 End = FMath::Min(FMath::CeilToInt(Index + Scale), Waveform.Num() - 1);
|
||||
FSamplePeak& Peak = Out[i];
|
||||
for (int32 j = Begin; j < End; ++j)
|
||||
{
|
||||
Peak += Waveform[j];
|
||||
}
|
||||
Index += Scale;
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
int32 SWaveformViewer::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry,
|
||||
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId,
|
||||
const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId,
|
||||
const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
if (const UpdateTask* UpdateTask = UpdateQueue.Peek())
|
||||
{
|
||||
Param.LineUV = UpdateTask->LineUV;
|
||||
Params->AddComputeShaderParam("Samples", UpdateTask->Array.GetData(), UpdateTask->Array.Num(), 0);
|
||||
UpdateQueue.Pop();
|
||||
|
||||
const FIntPoint& Size = FIntPoint(AllottedGeometry.Size.X, AllottedGeometry.Size.Y);
|
||||
if (Size.X <= 0 || Size.Y <= 0) return LayerId;
|
||||
const TArrayView<const FSamplePeak>& SamplePeaks = WaveformHandle->GetWaveform(Size.X);
|
||||
if (!SamplePeaks.Num()) return LayerId;
|
||||
Param.LineUV = 1.0f / Size.Y;
|
||||
Param.PeaksPerPixel = (float)SamplePeaks.Num() / Size.X;
|
||||
const TArray<FSamplePeak>& Peaks = ScaleWaveform(SamplePeaks, Size.X);
|
||||
Params->AddComputeShaderParam("Samples", (float*)Peaks.GetData(), Peaks.Num() * 2, 0);
|
||||
|
||||
if (FSlateApplication::Get().GetRenderer()->GetRendererAPI() == ERendererAPI::OpenGL)
|
||||
Params->AddComputeShaderParam("Params", (bool*)&Param, sizeof(FCSParam) / sizeof(bool), 1);
|
||||
else
|
||||
Params->AddShaderParam("Params", (bool*)&Param, sizeof(FCSParam) / sizeof(bool));
|
||||
|
||||
Params->AddComputeShaderParam("Samples", UpdateTask->Array.GetData(), UpdateTask->Array.Num(), 0);
|
||||
const FIntPoint& Size = UpdateTask->Size;
|
||||
RenderTarget->Resize(Size);
|
||||
FSlateDrawElement::MakeComputeShaderDispatch(OutDrawElements, LayerId, Size.X , Size.Y, 1, ComputeElement);
|
||||
}
|
||||
|
||||
LayerId++;
|
||||
RenderTarget->OnDrawViewport(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
||||
FSlateDrawElement::MakeViewport(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), RenderTarget, ESlateDrawEffect::None, FLinearColor::White);
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
void SWaveformViewer::OnResize(const FIntPoint& InSize) const
|
||||
{
|
||||
bNeedCompute = true;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "PixelShaderViewer.h"
|
||||
#include "PeakFile/PeakFile.h"
|
||||
#include "PluginHost/Sampler.h"
|
||||
|
||||
class FSamplePatternInstance;
|
||||
@ -7,16 +8,18 @@ class FSamplePatternInstance;
|
||||
class IWaveformHandle
|
||||
{
|
||||
public:
|
||||
virtual TArray<float> GetWaveform(int32 SizeX) const = 0;
|
||||
virtual ~IWaveformHandle() = default;
|
||||
virtual TArrayView<const FSamplePeak> GetWaveform(int32 SizeX) = 0;
|
||||
};
|
||||
|
||||
|
||||
class ARONA_API SWaveformViewer : public SLeafWidget
|
||||
{
|
||||
public:
|
||||
SWaveformViewer();
|
||||
SLATE_BEGIN_ARGS(SWaveformViewer)
|
||||
{
|
||||
}
|
||||
SLATE_ARGUMENT(TSharedPtr<IWaveformHandle>, WaveformHandle)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Constructs this widget with InArgs */
|
||||
@ -25,18 +28,11 @@ public:
|
||||
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
||||
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
|
||||
virtual FVector2D ComputeDesiredSize(float LayoutScaleMultiplier) const override { return RenderTarget->GetSize(); }
|
||||
|
||||
void OnResize(const FIntPoint& InSize) const;
|
||||
void SetWaveformHandle(TSharedPtr<IWaveformHandle> InHandle) { WaveformHandle = InHandle; }
|
||||
private:
|
||||
TSharedPtr<FSlateRenderTarget> RenderTarget;
|
||||
TSharedPtr<FSlateComputeShader> ComputeShader;
|
||||
TSharedPtr<FSlateShaderParam> Params;
|
||||
TSharedPtr<IComputeShaderSlateElement> ComputeElement;
|
||||
FSampler Sampler;
|
||||
mutable bool bNeedCompute = false;
|
||||
float UpdateInterval = 0.1f;
|
||||
float UpdateTimer = 0.0f;
|
||||
|
||||
struct UpdateTask
|
||||
{
|
||||
@ -51,6 +47,7 @@ private:
|
||||
FLinearColor WaveColor;
|
||||
FLinearColor BgColor;
|
||||
float LineUV;
|
||||
float PeaksPerPixel;
|
||||
} mutable Param;
|
||||
TSharedPtr<IWaveformHandle> WaveformHandle;
|
||||
};
|
||||
|
@ -16,8 +16,9 @@ FSampler::FSampler()
|
||||
IncomingRange = TRange<AudioFrame>(0, 0);
|
||||
}
|
||||
|
||||
bool FSampler::Load(const FString& Path)
|
||||
bool FSampler::Load(const FString& InPath)
|
||||
{
|
||||
Path = InPath;
|
||||
#if PLATFORM_WINDOWS
|
||||
SndfileHandle FileHandle(TCHAR_TO_WCHAR(*Path));
|
||||
#else
|
||||
|
@ -3,16 +3,19 @@
|
||||
#include "Midi/MidiType.h"
|
||||
#include "Midi/Time/TimePos.h"
|
||||
|
||||
|
||||
class FSampler : public FPluginHost
|
||||
{
|
||||
public:
|
||||
typedef TArray<TArray64<float>> FSampleBuffer;
|
||||
|
||||
FSampler();
|
||||
|
||||
virtual bool Load(const FString& Path) override;
|
||||
virtual bool Load(const FString& InPath) override;
|
||||
virtual void UpdateSampleRate(float InSampleRate) override;
|
||||
virtual void Process(int32 FrameNum) override;
|
||||
|
||||
const TArray<TArray64<float>>& GetSampleBuffer() const { return SampleBuffer; }
|
||||
const FSampleBuffer& GetSampleBuffer() const { return SampleBuffer; }
|
||||
|
||||
float GetSampleInterlace(uint32 Index) const { return SampleBuffer[Index % ChannelCount][Index / ChannelCount]; }
|
||||
|
||||
@ -20,6 +23,7 @@ public:
|
||||
float GetSampleRate() const { return SampleRate; }
|
||||
uint32 GetFrameCount() const { return FrameCount; }
|
||||
uint32 GetSampleCount() const { return FrameCount * ChannelCount; }
|
||||
const FString& GetPath() const { return Path; }
|
||||
|
||||
uint32 IncomingOffset;
|
||||
TRange<AudioFrame> IncomingRange;
|
||||
@ -31,5 +35,6 @@ private:
|
||||
uint32 FrameCount;
|
||||
float SampleRate;
|
||||
|
||||
TArray<TArray64<float>> SampleBuffer;
|
||||
FSampleBuffer SampleBuffer;
|
||||
FString Path;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user