You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
148 lines
4.9 KiB
C++
148 lines
4.9 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "DSP/InterpolatedMultiTapDelay.h"
|
|
|
|
#include "DSP/FloatArrayMath.h"
|
|
#include "Math/VectorRegister.h"
|
|
|
|
namespace Audio
|
|
{
|
|
void FInterpolatedMultiTapDelay::Init(const int32 InBufferSizeSamples, const float InSampleRate)
|
|
{
|
|
WriteIndex = 0;
|
|
MsToSamples = InSampleRate / 1000.f;
|
|
DelayLine.Reset();
|
|
DelayLine.AddZeroed(AlignIndex(InBufferSizeSamples));
|
|
}
|
|
|
|
void FInterpolatedMultiTapDelay::Advance(const FAlignedFloatBuffer& InSamples)
|
|
{
|
|
check (InSamples.Num() % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER == 0);
|
|
const int32 InNumSamples = InSamples.Num();
|
|
|
|
if (InNumSamples <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (InNumSamples + WriteIndex > DelayLine.Num())
|
|
{
|
|
const int32 FirstHalfSize = DelayLine.Num() - WriteIndex;
|
|
const int32 SecondHalfSize = InNumSamples - FirstHalfSize;
|
|
|
|
if (FirstHalfSize > 0)
|
|
{
|
|
FMemory::Memcpy(&DelayLine[WriteIndex], InSamples.GetData(), FirstHalfSize * sizeof(float));
|
|
}
|
|
if (SecondHalfSize > 0)
|
|
{
|
|
FMemory::Memcpy(DelayLine.GetData(), &InSamples[FirstHalfSize], SecondHalfSize * sizeof(float));
|
|
}
|
|
WriteIndex = SecondHalfSize;
|
|
}
|
|
else
|
|
{
|
|
FMemory::Memcpy(&DelayLine[WriteIndex], InSamples.GetData(), InNumSamples * sizeof(float));
|
|
WriteIndex += InNumSamples;
|
|
WriteIndex = FMath::Wrap(WriteIndex, 0, DelayLine.Num());
|
|
}
|
|
}
|
|
|
|
void FInterpolatedMultiTapDelay::Read(const float StartDelayMSec, const float EndDelayMSec, FAlignedFloatBuffer& OutSamples)
|
|
{
|
|
const int32 OutputSamples = OutSamples.Num();
|
|
check (OutputSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER == 0);
|
|
|
|
// likely to only run on the first frame, if not configured with enough memory
|
|
if (OutputSamples > DelayLine.Num())
|
|
{
|
|
DelayLine.SetNumZeroed(OutputSamples);
|
|
}
|
|
|
|
const int32 BufferSize = DelayLine.Num();
|
|
const int32 StartSample = AlignIndex(FMath::Wrap(WriteIndex - FMath::FloorToInt32(StartDelayMSec * MsToSamples), 0, BufferSize - 1));
|
|
const int32 EndSample = AlignIndex(FMath::Wrap(WriteIndex + OutputSamples - FMath::CeilToInt32(EndDelayMSec * MsToSamples), 0, BufferSize - 1));
|
|
|
|
float* OutputPtr = OutSamples.GetData();
|
|
|
|
if (StartSample >= EndSample)
|
|
{
|
|
const int32 DelaySamples = EndSample - (StartSample - BufferSize);
|
|
|
|
// block wraps
|
|
const int32 FirstBlockDelaySamples = BufferSize - StartSample;
|
|
const int32 SecondBlockDelaySamples = DelaySamples - FirstBlockDelaySamples;
|
|
const int32 FirstBlockOutputSamples = AlignIndex(((float)FirstBlockDelaySamples / (float)DelaySamples) * OutputSamples);
|
|
const int32 SecondBlockOuptutSamples = OutputSamples - FirstBlockOutputSamples;
|
|
|
|
ReadBlockInternal(StartSample, FirstBlockDelaySamples, FirstBlockOutputSamples, OutputPtr);
|
|
ReadBlockInternal(0, SecondBlockDelaySamples, SecondBlockOuptutSamples, OutputPtr + FirstBlockOutputSamples);
|
|
}
|
|
else
|
|
{
|
|
ReadBlockInternal(StartSample, EndSample - StartSample, OutputSamples, OutputPtr);
|
|
}
|
|
}
|
|
|
|
void FInterpolatedMultiTapDelay::Reset()
|
|
{
|
|
const int32 NumElements = DelayLine.Num();
|
|
DelayLine.Reset(NumElements);
|
|
DelayLine.AddZeroed(NumElements);
|
|
}
|
|
|
|
bool FInterpolatedMultiTapDelay::IsInitialized() const
|
|
{
|
|
return DelayLine.GetAllocatedSize() > 0 && MsToSamples > 1.f;
|
|
}
|
|
|
|
void FInterpolatedMultiTapDelay::ReadBlockInternal(const int32 StartSample, const int32 SamplesToRead, const int32 NumOutputSamples, float* OutSamples)
|
|
{
|
|
if (NumOutputSamples == 0 || SamplesToRead == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
check(StartSample + SamplesToRead <= DelayLine.Num())
|
|
check(NumOutputSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER == 0)
|
|
|
|
const float SampleStride = (float)SamplesToRead / (float)NumOutputSamples;
|
|
|
|
// protect against overflows while wrapping without branching in the main loop
|
|
int32 MainLoopOutputSamples = NumOutputSamples;
|
|
int32 MainLoopReadSamples = SamplesToRead;
|
|
|
|
if (StartSample + SamplesToRead == DelayLine.Num())
|
|
{
|
|
MainLoopOutputSamples -= AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER;
|
|
MainLoopReadSamples -= FMath::RoundToInt32((float)AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER * SampleStride);
|
|
}
|
|
|
|
ArrayInterpolate(&DelayLine[StartSample], OutSamples, MainLoopReadSamples, MainLoopOutputSamples);
|
|
|
|
float SampleIndex = MainLoopReadSamples;
|
|
|
|
// wrap in the loop only if close to wrapping: protects against rounding up past last index
|
|
for (int32 OutputIndex = MainLoopOutputSamples; OutputIndex < NumOutputSamples; OutputIndex++)
|
|
{
|
|
const int32 LeftSample = FMath::FloorToInt32(SampleIndex);
|
|
int32 RightSample = FMath::CeilToInt32(SampleIndex);
|
|
|
|
if (RightSample >= DelayLine.Num())
|
|
{
|
|
RightSample -= DelayLine.Num();
|
|
}
|
|
|
|
const float Frac = SampleIndex - LeftSample;
|
|
OutSamples[OutputIndex] = (Frac * DelayLine[LeftSample]) + ((1.f - Frac) * DelayLine[RightSample]);
|
|
|
|
SampleIndex += SampleStride;
|
|
}
|
|
}
|
|
|
|
int32 FInterpolatedMultiTapDelay::AlignIndex(const int32 InIndex) const
|
|
{
|
|
return InIndex - (InIndex % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER);
|
|
}
|
|
} |