Files
UnrealEngine/Engine/Source/Runtime/SignalProcessing/Private/FeedbackDelayNetwork.cpp
2025-05-18 13:04:45 +08:00

202 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DSP/FeedbackDelayNetwork.h"
namespace Audio
{
// The presets for the late refelections reverb were based on a strange sample rate. This
// function helps translate delay values in the paper to delay values for this reverb.
// https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf
static int32 FeedbackDelayNetworkGetDelaySamples(float InSampleRate, float InPresetValue) {
return (int32)((float)InSampleRate / 29761.f * InPresetValue);
}
FFDNDelaySettings FFDNDelaySettings::DefaultLeftDelays(float InSampleRate)
{
FFDNDelaySettings Settings;
Settings.APF0DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 142);
Settings.APF1DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 107);
Settings.APF2DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 379);
Settings.APF3DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 277);
return Settings;
}
FFDNDelaySettings FFDNDelaySettings::DefaultRightDelays(float InSampleRate)
{
FFDNDelaySettings Settings;
Settings.APF0DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 279);
Settings.APF1DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 137);
Settings.APF2DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 213);
Settings.APF3DelayNumSamples = FeedbackDelayNetworkGetDelaySamples(InSampleRate, 5 * 327);
return Settings;
}
FFeedbackDelayNetwork::FFeedbackDelayNetwork(int32 InMaxNumInternalBufferSamples, const FFDNDelaySettings& InSettings)
: Settings(InSettings)
{
// num internal buffer samples must be less than the minimum delay line and abide by memory alignment.
NumInternalBufferSamples = FMath::Min(
FMath::Min(Settings.APF0DelayNumSamples, Settings.APF1DelayNumSamples),
FMath::Min(Settings.APF2DelayNumSamples, Settings.APF3DelayNumSamples));
NumInternalBufferSamples = FMath::Min(NumInternalBufferSamples, InMaxNumInternalBufferSamples);
NumInternalBufferSamples -= (NumInternalBufferSamples % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER);
if (NumInternalBufferSamples <= 0)
{
NumInternalBufferSamples = 1;
}
// Allocate delay lines.
DelayLine0 = MakeUnique<FAlignedBlockBuffer>((2 * Settings.APF0DelayNumSamples) + NumInternalBufferSamples, NumInternalBufferSamples);
DelayLine1 = MakeUnique<FAlignedBlockBuffer>((2 * Settings.APF1DelayNumSamples) + NumInternalBufferSamples, NumInternalBufferSamples);
DelayLine2 = MakeUnique<FAlignedBlockBuffer>((2 * Settings.APF2DelayNumSamples) + NumInternalBufferSamples, NumInternalBufferSamples);
DelayLine3 = MakeUnique<FAlignedBlockBuffer>((2 * Settings.APF3DelayNumSamples) + NumInternalBufferSamples, NumInternalBufferSamples);
// Initialize delay lines
DelayLine0->AddZeros(Settings.APF0DelayNumSamples);
DelayLine1->AddZeros(Settings.APF1DelayNumSamples);
DelayLine2->AddZeros(Settings.APF2DelayNumSamples);
DelayLine3->AddZeros(Settings.APF3DelayNumSamples);
// Initialize internal buffers
WorkBuffer0.Reset(NumInternalBufferSamples);
WorkBuffer1.Reset(NumInternalBufferSamples);
WorkBuffer2.Reset(NumInternalBufferSamples);
WorkBuffer3.Reset(NumInternalBufferSamples);
WorkBuffer0.AddUninitialized(NumInternalBufferSamples);
WorkBuffer1.AddUninitialized(NumInternalBufferSamples);
WorkBuffer2.AddUninitialized(NumInternalBufferSamples);
WorkBuffer3.AddUninitialized(NumInternalBufferSamples);
// Initialize filter memory
for (int32 i = 0; i < 4; i++)
{
FMO[i] = 0.0f;
LPFZ[i] = 0.0f;
}
}
FFeedbackDelayNetwork::~FFeedbackDelayNetwork()
{}
void FFeedbackDelayNetwork::SetCoefficients(const FFDNCoefficients& InCoefficients)
{
Coefficients = InCoefficients;
}
void FFeedbackDelayNetwork::ProcessAudio(const FAlignedFloatBuffer& InSamples, FAlignedFloatBuffer& OutSamples)
{
const int32 InNum = InSamples.Num();
const float* InSampleData = InSamples.GetData();
// Prepare output buffer
OutSamples.Reset(InNum);
OutSamples.AddUninitialized(InNum);
// Process audio one block at a time
float* OutSampleData = OutSamples.GetData();
int32 LeftToProcess = InNum;
int32 BufferPos = 0;
while (LeftToProcess > 0)
{
int32 NumToProcess = FMath::Min(NumInternalBufferSamples, LeftToProcess);
ProcessAudioBuffer(&InSampleData[BufferPos], NumToProcess, &OutSampleData[BufferPos]);
LeftToProcess -= NumToProcess;
BufferPos += NumToProcess;
}
}
int32 FFeedbackDelayNetwork::GetNumInternalBufferSamples() const
{
return NumInternalBufferSamples;
}
void FFeedbackDelayNetwork::ProcessAudioBuffer(const float* InSampleData, const int32 InNum, float* OutSampleData)
{
const float* DelayLineData0 = DelayLine0->InspectSamples(InNum);
const float* DelayLineData1 = DelayLine1->InspectSamples(InNum);
const float* DelayLineData2 = DelayLine2->InspectSamples(InNum);
const float* DelayLineData3 = DelayLine3->InspectSamples(InNum);
float* WorkBuffer0Data = WorkBuffer0.GetData();
float* WorkBuffer1Data = WorkBuffer1.GetData();
float* WorkBuffer2Data = WorkBuffer2.GetData();
float* WorkBuffer3Data = WorkBuffer3.GetData();
float AllPassOut[4] = {0.0f};
float DelayInput[4] = {0.0f};
for (int32 i = 0; i < InNum; i++)
{
float DelayOut[4] = {DelayLineData0[i], DelayLineData1[i], DelayLineData2[i], DelayLineData3[i]};
// Scale input
float ScaledInput = Coefficients.InputScale * InSampleData[i];
// Calculate input to delay line.
for (int32 j = 0; j < 4; j++)
{
DelayInput[j] = (ScaledInput + FMO[j]) + (Coefficients.APFG[j] * DelayOut[j]);
}
// Store delay line values
WorkBuffer0Data[i] = DelayInput[0];
WorkBuffer1Data[i] = DelayInput[1];
WorkBuffer2Data[i] = DelayInput[2];
WorkBuffer3Data[i] = DelayInput[3];
// complete all pass filters and process low pass filters
OutSampleData[i] = 0.0f;
for (int32 j = 0; j < 4; j++)
{
AllPassOut[j] = (DelayInput[j] * -Coefficients.APFG[j]) + DelayOut[j];
// Note the reasignment of lpz.
LPFZ[j] = (AllPassOut[j] * Coefficients.LPFA[j]) + (LPFZ[j] * Coefficients.LPFB[j]);
OutSampleData[i] += LPFZ[j];
}
// Mix channels and apply feedback.
FMO[0] = Coefficients.Feedback * (-LPFZ[1] + LPFZ[2]);
FMO[1] = Coefficients.Feedback * (LPFZ[0] + LPFZ[3]);
FMO[2] = Coefficients.Feedback * (LPFZ[0] - LPFZ[3]);
FMO[3] = Coefficients.Feedback * (-LPFZ[1] - LPFZ[2]);
}
// Update delay lines.
DelayLine0->RemoveSamples(InNum);
DelayLine1->RemoveSamples(InNum);
DelayLine2->RemoveSamples(InNum);
DelayLine3->RemoveSamples(InNum);
DelayLine0->AddSamples(WorkBuffer0Data, InNum);
DelayLine1->AddSamples(WorkBuffer1Data, InNum);
DelayLine2->AddSamples(WorkBuffer2Data, InNum);
DelayLine3->AddSamples(WorkBuffer3Data, InNum);
}
void FFeedbackDelayNetwork::FlushAudio()
{
for (int32 i = 0; i < 4; i++)
{
LPFZ[i] = 0.f;
FMO[i] = 0.f;
}
DelayLine0->ClearSamples();
DelayLine0->AddZeros(Settings.APF0DelayNumSamples);
DelayLine1->ClearSamples();
DelayLine1->AddZeros(Settings.APF1DelayNumSamples);
DelayLine2->ClearSamples();
DelayLine2->AddZeros(Settings.APF2DelayNumSamples);
DelayLine3->ClearSamples();
DelayLine3->AddZeros(Settings.APF3DelayNumSamples);
}
}