diff --git a/Content/Shader/Arona/D3D/WaveformCS.hlsl b/Content/Shader/Arona/D3D/WaveformCS.hlsl index b16451a..1e35d0e 100644 --- a/Content/Shader/Arona/D3D/WaveformCS.hlsl +++ b/Content/Shader/Arona/D3D/WaveformCS.hlsl @@ -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); } \ No newline at end of file diff --git a/Content/Shader/Arona/OpenGL/WaveformCS.glsl b/Content/Shader/Arona/OpenGL/WaveformCS.glsl index 81a3904..e5721f4 100644 --- a/Content/Shader/Arona/OpenGL/WaveformCS.glsl +++ b/Content/Shader/Arona/OpenGL/WaveformCS.glsl @@ -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)); } diff --git a/Source/Arona/EntryPoints/AronaApp.cpp b/Source/Arona/EntryPoints/AronaApp.cpp index d7a13bb..cac1d63 100644 --- a/Source/Arona/EntryPoints/AronaApp.cpp +++ b/Source/Arona/EntryPoints/AronaApp.cpp @@ -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(); diff --git a/Source/Arona/PeakFile/PeakFile.cpp b/Source/Arona/PeakFile/PeakFile.cpp new file mode 100644 index 0000000..0dfa7cf --- /dev/null +++ b/Source/Arona/PeakFile/PeakFile.cpp @@ -0,0 +1,66 @@ +#include "PeakFile.h" + +#include "PluginHost/Sampler.h" + +#define MID_TO_LOW (16 * 16) + +TArray> GenPeakData(const TArray>& Data, int32 BlockSize) +{ + TArray> 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>& 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>& SampleBuffer) +{ + for (int i = 0; i < RenderData.Num(); ++i) + RenderData[i]->Generate(SampleBuffer); +} diff --git a/Source/Arona/PeakFile/PeakFile.h b/Source/Arona/PeakFile/PeakFile.h new file mode 100644 index 0000000..d1dcfaa --- /dev/null +++ b/Source/Arona/PeakFile/PeakFile.h @@ -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 Buffer; + uint16 MaxLevel; +}; + +struct FWaveformBuffer +{ + TArray Buffer; + void* HiResBuffer; + TArray RMSBuffer; +}; + +class FWaveformRenderData +{ +public: + explicit FWaveformRenderData(int32 BlockSize) + : BlockSize(BlockSize) + { + } + + virtual ~FWaveformRenderData() = default; + virtual void Generate(const TArray>& 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 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 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> Buffer; + const int32 BlockSize; // 每一个块包含多少个Frame +}; + +class FWaveform +{ +public: + FWaveform(int32 LevelNum, int32 MaxBlockSize = 128 * 128); + ~FWaveform(); + + void UpdatePeak(const TArray>& SampleBuffer); + int32 GetMemSize() const + { + int32 Size = 0; + for (int i = 0; i < RenderData.Num(); ++i) + Size += RenderData[i]->GetMemSize(); + return Size; + } + + TArrayView 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 RenderData; +}; diff --git a/Source/Arona/Test.cpp b/Source/Arona/Test.cpp index 964ea2d..b9ec5d9 100644 --- a/Source/Arona/Test.cpp +++ b/Source/Arona/Test.cpp @@ -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); diff --git a/Source/Arona/UI/Widget/MainWindow.cpp b/Source/Arona/UI/Widget/MainWindow.cpp index 3d89374..3bfc30a 100644 --- a/Source/Arona/UI/Widget/MainWindow.cpp +++ b/Source/Arona/UI/Widget/MainWindow.cpp @@ -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) - ] ] ); diff --git a/Source/Arona/UI/Widget/Pattern/SPatternInstance.cpp b/Source/Arona/UI/Widget/Pattern/SPatternInstance.cpp index 69df8e0..a47e565 100644 --- a/Source/Arona/UI/Widget/Pattern/SPatternInstance.cpp +++ b/Source/Arona/UI/Widget/Pattern/SPatternInstance.cpp @@ -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() ] ] ]; diff --git a/Source/Arona/UI/Widget/Pattern/SPatternInstance.h b/Source/Arona/UI/Widget/Pattern/SPatternInstance.h index e59905c..ceeeea9 100644 --- a/Source/Arona/UI/Widget/Pattern/SPatternInstance.h +++ b/Source/Arona/UI/Widget/Pattern/SPatternInstance.h @@ -38,7 +38,7 @@ public: SLATE_ATTRIBUTE(float, FrameToPixel) SLATE_ATTRIBUTE(AudioFrame, SnapFrame) - SLATE_ARGUMENT(TSharedRef, View) + SLATE_ATTRIBUTE(TRange, 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 GetViewWidget() = 0; + virtual void RequestUpdate() = 0; + FPatternInstance* GetPatternInstance() const { return PatternInstance; } + TRange GetTimeRange() const { return PatternInstance->TimeRange; } + TRange GetFrameViewRange() const { return FrameViewRange.Get(); } float ResizeHandleSize = 5.f; protected: virtual FReply OpenPatternMenu() { return FReply::Unhandled(); } @@ -61,6 +65,7 @@ protected: TAttribute FrameToPixel; TAttribute SnapFrame; + TAttribute> FrameViewRange; FVector2f MouseDownPos; // 屏幕坐标 AudioFrame BeginPatternEnd; diff --git a/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.cpp b/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.cpp new file mode 100644 index 0000000..fff5f97 --- /dev/null +++ b/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.cpp @@ -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 FSamplePatternInstanceWaveformHandle::GetWaveform(int32 SizeX) +{ + FSamplePatternInstance* Instance = (FSamplePatternInstance*)PatternInstanceWidget->GetPatternInstance(); + TRange FrameViewRange = PatternInstanceWidget->GetFrameViewRange(); + const FSampler* Sampler = Instance->GetInstanceOwner()->GetSampler(); + const TRange& 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(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(this); +} + +TSharedRef SSamplePatternInstance::GetViewWidget() +{ + return SAssignNew(WaveformViewer, SWaveformViewer) + .WaveformHandle(WaveformHandle); +} + +void SSamplePatternInstance::RequestUpdate() +{ +} + +FReply SSamplePatternInstance::OpenPatternMenu() +{ + return FReply::Handled(); +} diff --git a/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.h b/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.h new file mode 100644 index 0000000..49fb722 --- /dev/null +++ b/Source/Arona/UI/Widget/Pattern/SSamplePatternInstance.h @@ -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 GetWaveform(int32 SizeX) override; + FWaveform* RenderData = nullptr; +}; + +class ARONA_API SSamplePatternInstance : public SPatternInstance +{ +public: + SSamplePatternInstance(); + virtual TSharedRef GetViewWidget() override; + virtual void RequestUpdate() override; +protected: + virtual FReply OpenPatternMenu() override; + TSharedPtr WaveformHandle; + TSharedPtr WaveformViewer; +}; diff --git a/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.cpp b/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.cpp deleted file mode 100644 index dd92109..0000000 --- a/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "SWaveformPatternInstance.h" diff --git a/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.h b/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.h deleted file mode 100644 index 50d83cb..0000000 --- a/Source/Arona/UI/Widget/Pattern/SWaveformPatternInstance.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "SPatternInstance.h" - -class ARONA_API SWaveformPatternInstance : public SPatternInstance -{ -public: - -}; diff --git a/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.cpp b/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.cpp index cd31a34..b3fe1a3 100644 --- a/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.cpp +++ b/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.cpp @@ -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 SAutoPatternThumbnail::CreateThumbnail(FPattern* InPattern) { TSharedPtr Out; diff --git a/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.h b/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.h index d505f44..f97832e 100644 --- a/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.h +++ b/Source/Arona/UI/Widget/PatternThumbnail/SPatternThumbnail.h @@ -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 CreateThumbnail(FPattern* InPattern); TSharedPtr Thumbnail; FPattern* Pattern = nullptr; + bool bNeedRedraw = true; }; diff --git a/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.cpp b/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.cpp index ca59cbc..537b405 100644 --- a/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.cpp +++ b/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.cpp @@ -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 FSampleWaveformHandle::GetWaveform(int32 SizeX) const +TArrayView FSamplePatternWaveformHandle::GetWaveform(int32 SizeX) { - - const FSampler* Sampler = SampleInstance->GetInstanceOwner()->GetSampler(); - TArray> Copy = Sampler->GetSampleBuffer(); // 拷贝以防在渲染时被修改 - uint32 Count = Sampler->GetFrameCount(); - TRange 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(InSampler); ChildSlot [ - + SAssignNew(WaveformViewer, SWaveformViewer) + .WaveformHandle(WaveformHandle) ]; } diff --git a/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.h b/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.h index 92a14df..03589f9 100644 --- a/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.h +++ b/Source/Arona/UI/Widget/PatternThumbnail/SSamplePatternThumbnail.h @@ -11,11 +11,16 @@ class FSampler; -class FSampleWaveformHandle : public IWaveformHandle +class FSamplePatternWaveformHandle : public IWaveformHandle { public: - FSamplePatternInstance* SampleInstance = nullptr; - virtual TArray GetWaveform(int32 SizeX) const override; + FSamplePatternWaveformHandle(FSampler* InSampler) + : Sampler(InSampler) + { + } + FSampler* Sampler = nullptr; + FWaveform* RenderData = nullptr; + virtual TArrayView GetWaveform(int32 SizeX) override; }; /** @@ -33,5 +38,6 @@ public: virtual void Redraw() override; private: - FSampleWaveformHandle* WaveformHandle = nullptr; + TSharedPtr WaveformHandle; + TSharedPtr WaveformViewer; }; diff --git a/Source/Arona/UI/Widget/PlayList/PlayListPanel/PlayListEdit.cpp b/Source/Arona/UI/Widget/PlayList/PlayListPanel/PlayListEdit.cpp index 014d9d2..6cc47b0 100644 --- a/Source/Arona/UI/Widget/PlayList/PlayListPanel/PlayListEdit.cpp +++ b/Source/Arona/UI/Widget/PlayList/PlayListPanel/PlayListEdit.cpp @@ -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 InPlayListPanel) @@ -155,14 +156,25 @@ FReply FPlayListEdit::OnPatternInstanceReleased(FPatternInstanceClickData D) TSharedRef FPlayListEdit::CreatePatternInstanceWidget(FPatternInstance* PatternInstance) { - const TSharedRef InstanceWidget = SNew(SPatternInstance) - .Texture(UpdatableTexture) - .PatternInstance(PatternInstance) - .FrameToPixel_Raw(this, &FPlayListEdit::GetSampleToPixelScaler) - .SnapFrame_Raw(this, &FPlayListEdit::GetSnapFrame) - .Clipping(EWidgetClipping::ClipToBoundsAlways); + TSharedPtr 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 diff --git a/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.cpp b/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.cpp index 8306fae..d1d236a 100644 --- a/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.cpp +++ b/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.cpp @@ -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& 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* Children = (TPanelChildren*)TrackCanvas->GetChildren(); + for (int i = 0; i < Children->Num(); ++i) + { + TSharedRef Widget = Children->GetChildAt(i); + // TSharedRef InvalidationPanel = StaticCastSharedRef(Widget); + // InvalidationPanel->SetCanCache(false); + // TSharedRef SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0); + // InvalidationPanel->SetCanCache(true); + TSharedRef InstanceWidget = StaticCastSharedRef(Widget); + if (InstanceWidget->GetPatternInstance() == PatternInstance) + { + Children->RemoveAt(i); + break; + } + } +} + +void SPlayListPanel::UpdateAllPatternInstance() { TPanelChildren* Children = (TPanelChildren*)TrackCanvas->GetChildren(); for (int i = 0; i < Children->Num(); ++i) @@ -255,11 +277,8 @@ void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance) TSharedRef SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0); InvalidationPanel->SetCanCache(true); TSharedRef InstanceWidget = StaticCastSharedRef(SharedRef); - if (InstanceWidget->GetPatternInstance() == PatternInstance) - { - Children->RemoveAt(i); - break; - } + InstanceWidget->RequestUpdate(); + // InstanceWidget->Invalidate(EInvalidateWidgetReason::Paint); } } diff --git a/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.h b/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.h index 1323d68..2ec8841 100644 --- a/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.h +++ b/Source/Arona/UI/Widget/PlayList/PlayListPanel/SPlayListPanel.h @@ -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> GetViewRange() const { return ViewRange; } virtual TSharedRef 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 TrackCanvas; TSharedPtr PlayListEdit; diff --git a/Source/Arona/UI/Widget/PlayList/SPlayList.cpp b/Source/Arona/UI/Widget/PlayList/SPlayList.cpp index 45c0949..f3f9fb0 100644 --- a/Source/Arona/UI/Widget/PlayList/SPlayList.cpp +++ b/Source/Arona/UI/Widget/PlayList/SPlayList.cpp @@ -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& CurrentRange = TargetFrameRange; const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel() * 2; // 乘以2是因为MousePosPercent diff --git a/Source/Arona/UI/Widget/Thumbnail.cpp b/Source/Arona/UI/Widget/Thumbnail.cpp index 88ead2c..731c001 100644 --- a/Source/Arona/UI/Widget/Thumbnail.cpp +++ b/Source/Arona/UI/Widget/Thumbnail.cpp @@ -200,8 +200,6 @@ namespace Thumbnail const double SampleZoom = (double)FrameRange.Size() / 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() / 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++; } diff --git a/Source/Arona/UI/Widget/WaveformViewer.cpp b/Source/Arona/UI/Widget/WaveformViewer.cpp index 05c6368..a636dfc 100644 --- a/Source/Arona/UI/Widget/WaveformViewer.cpp +++ b/Source/Arona/UI/Widget/WaveformViewer.cpp @@ -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>& 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 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 ScaleWaveform(const TArrayView& Waveform, const int32 SizeX) +{ + TArray 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& 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& 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; -} diff --git a/Source/Arona/UI/Widget/WaveformViewer.h b/Source/Arona/UI/Widget/WaveformViewer.h index ba7e84e..e2defa5 100644 --- a/Source/Arona/UI/Widget/WaveformViewer.h +++ b/Source/Arona/UI/Widget/WaveformViewer.h @@ -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 GetWaveform(int32 SizeX) const = 0; + virtual ~IWaveformHandle() = default; + virtual TArrayView GetWaveform(int32 SizeX) = 0; }; - class ARONA_API SWaveformViewer : public SLeafWidget { public: + SWaveformViewer(); SLATE_BEGIN_ARGS(SWaveformViewer) { } + SLATE_ARGUMENT(TSharedPtr, 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 InHandle) { WaveformHandle = InHandle; } private: TSharedPtr RenderTarget; TSharedPtr ComputeShader; TSharedPtr Params; TSharedPtr 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 WaveformHandle; }; diff --git a/Source/AronaCore/PluginHost/Sampler.cpp b/Source/AronaCore/PluginHost/Sampler.cpp index fe420da..d556954 100644 --- a/Source/AronaCore/PluginHost/Sampler.cpp +++ b/Source/AronaCore/PluginHost/Sampler.cpp @@ -16,8 +16,9 @@ FSampler::FSampler() IncomingRange = TRange(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 diff --git a/Source/AronaCore/PluginHost/Sampler.h b/Source/AronaCore/PluginHost/Sampler.h index dbf3ba8..913221c 100644 --- a/Source/AronaCore/PluginHost/Sampler.h +++ b/Source/AronaCore/PluginHost/Sampler.h @@ -3,16 +3,19 @@ #include "Midi/MidiType.h" #include "Midi/Time/TimePos.h" + class FSampler : public FPluginHost { public: + typedef TArray> 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>& 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 IncomingRange; @@ -31,5 +35,6 @@ private: uint32 FrameCount; float SampleRate; - TArray> SampleBuffer; + FSampleBuffer SampleBuffer; + FString Path; };