Files
UnrealEngineUWP/Engine/Source/Runtime/SignalProcessing/Private/LinearInterpFractionalDelay.cpp
ryan durand 0f0464a30e Updating copyright for Engine Runtime.
#rnx
#rb none


#ROBOMERGE-OWNER: ryan.durand
#ROBOMERGE-AUTHOR: ryan.durand
#ROBOMERGE-SOURCE: CL 10869210 via CL 10869511 via CL 10869900
#ROBOMERGE-BOT: (v613-10869866)

[CL 10870549 by ryan durand in Main branch]
2019-12-26 14:45:42 -05:00

164 lines
5.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DSP/LinearInterpFractionalDelay.h"
#include "DSP/Dsp.h"
#include "DSP/BufferVectorOperations.h"
using namespace Audio;
FLinearInterpFractionalDelay::FLinearInterpFractionalDelay(int32 InMaxDelay, int32 InMaxNumInternalBufferSamples)
: MaxDelay(InMaxDelay)
, NumInternalBufferSamples(InMaxNumInternalBufferSamples)
, UpperDelayPos(nullptr)
, LowerDelayPos(nullptr)
{
checkf(MaxDelay > 0, TEXT("InMaxDelay must be greater than zero"));
if (MaxDelay < 1)
{
MaxDelay = 1;
}
while (0 != (NumInternalBufferSamples % AUDIO_SIMD_FLOAT_ALIGNMENT))
{
NumInternalBufferSamples--;
}
if (NumInternalBufferSamples < 1)
{
NumInternalBufferSamples = AUDIO_SIMD_FLOAT_ALIGNMENT;
}
// Allocate and prepare delay line for maximum delay.
DelayLine = MakeUnique<FAlignedBlockBuffer>((2 * (MaxDelay + 1)) + NumInternalBufferSamples, MaxDelay + NumInternalBufferSamples + 1);
DelayLine->AddZeros(MaxDelay + 1);
IntegerDelayOffsets.Reset(NumInternalBufferSamples);
IntegerDelayOffsets.AddUninitialized(NumInternalBufferSamples);
for (int32 i = 0; i < NumInternalBufferSamples; i++)
{
IntegerDelayOffsets[i] = i + MaxDelay;
}
UpperDelayPos = (int*)FMemory::Malloc(4 * sizeof(int), AUDIO_BUFFER_ALIGNMENT);
LowerDelayPos = (int*)FMemory::Malloc(4 * sizeof(int), AUDIO_BUFFER_ALIGNMENT);
}
// Destructor
FLinearInterpFractionalDelay::~FLinearInterpFractionalDelay()
{
FMemory::Free(UpperDelayPos);
FMemory::Free(LowerDelayPos);
}
// Resets the delay line state, flushes buffer and resets read/write pointers.
void FLinearInterpFractionalDelay::Reset()
{
DelayLine->ClearSamples();
DelayLine->AddZeros(MaxDelay + 1);
}
void FLinearInterpFractionalDelay::ProcessAudio(const AlignedFloatBuffer& InSamples, const AlignedFloatBuffer& InDelays, AlignedFloatBuffer& OutSamples)
{
const int32 InNum = InSamples.Num();
checkf(InNum == InDelays.Num(), TEXT("Input buffers must be equal length"));
// Prepare output buffer
OutSamples.Reset(InNum);
OutSamples.AddUninitialized(InNum);
if (InNum != InDelays.Num())
{
// Return empty buffer on invalid input.
if (InNum > 0)
{
FMemory::Memset(OutSamples.GetData(), 0, sizeof(float) * InNum);
}
return;
}
float* OutSampleData = OutSamples.GetData();
const float* InSampleData = InSamples.GetData();
const float* InDelayData = InDelays.GetData();
// Process audio one block at a time.
int32 LeftOver = InNum;
int32 BufferPos = 0;
while (LeftOver > 0)
{
int32 NumToProcess = FMath::Min(LeftOver, NumInternalBufferSamples);
ProcessAudioBlock(&InSampleData[BufferPos], &InDelayData[BufferPos], NumToProcess, &OutSampleData[BufferPos]);
BufferPos += NumToProcess;
LeftOver -= NumToProcess;
}
}
void FLinearInterpFractionalDelay::ProcessAudioBlock(const float* InSamples, const float* InDelays, const int32 InNum, float* OutSamples)
{
checkf(0 == (InNum % 4), TEXT("Array length must be multiple of 4"));
checkf(IsAligned<const float*>(InSamples, AUDIO_SIMD_FLOAT_ALIGNMENT), TEXT("Memory must be aligned to use vector operations."));
checkf(IsAligned<const float*>(InDelays, AUDIO_SIMD_FLOAT_ALIGNMENT), TEXT("Memory must be aligned to use vector operations."));
checkf(IsAligned<float*>(OutSamples, AUDIO_SIMD_FLOAT_ALIGNMENT), TEXT("Memory must be aligned to use vector operations."));
// Update delay line.
DelayLine->AddSamples(InSamples, InNum);
const float* DelayData = DelayLine->InspectSamples(InNum + MaxDelay + 1);
const int32* IntegerDelayOffsetData = IntegerDelayOffsets.GetData();
const VectorRegister VMaxDelay = MakeVectorRegister((float)MaxDelay, (float)MaxDelay, (float)MaxDelay, (float)MaxDelay);
for (int32 i = 0; i < InNum; i += 4)
{
VectorRegister VFractionalDelays = VectorLoadAligned(&InDelays[i]);
// Ensure fractional delays are positive
VFractionalDelays = VectorMax(VFractionalDelays, GlobalVectorConstants::FloatZero);
VFractionalDelays = VectorMin(VFractionalDelays, VMaxDelay);
// Separate integer from fraction
VectorRegister VFloorDelays = VectorFloor(VFractionalDelays);
// Determine linear weights
VectorRegister VUpperCoefficients = VectorSubtract(VFractionalDelays, VFloorDelays);
VectorRegister VLowerCoefficients = VectorSubtract(GlobalVectorConstants::FloatOne, VUpperCoefficients);
// Make integer locations relative to block
VectorRegisterInt VIntegerDelays = VectorFloatToInt(VFloorDelays);
VectorRegisterInt VIntegerDelayOffset = VectorIntLoadAligned(&IntegerDelayOffsetData[i]);
VIntegerDelays = VectorIntSubtract(VIntegerDelayOffset, VIntegerDelays);
// Lookup samples for interpolation
VectorIntStoreAligned(VIntegerDelays, UpperDelayPos);
VectorIntStoreAligned(VectorIntAdd(VIntegerDelays, GlobalVectorConstants::IntOne), LowerDelayPos);
VectorRegister VLowerSamples = MakeVectorRegister(
DelayData[LowerDelayPos[0]],
DelayData[LowerDelayPos[1]],
DelayData[LowerDelayPos[2]],
DelayData[LowerDelayPos[3]]
);
VectorRegister VUpperSamples = MakeVectorRegister(
DelayData[UpperDelayPos[0]],
DelayData[UpperDelayPos[1]],
DelayData[UpperDelayPos[2]],
DelayData[UpperDelayPos[3]]
);
// Interpolate samples
VectorRegister VOut = VectorMultiplyAdd(
VLowerSamples,
VLowerCoefficients,
VectorMultiply(VUpperSamples, VUpperCoefficients));
VectorStoreAligned(VOut, &OutSamples[i]);
}
// Remove unneeded delay line.
DelayLine->RemoveSamples(InNum);
}