2023-09-12 19:28:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DSP/InterpolatedMultiTapDelay.h"
2023-10-25 00:51:33 -04:00
# include "SignalProcessingModule.h"
2023-09-12 19:28:17 -04:00
# include "DSP/FloatArrayMath.h"
# include "Math/VectorRegister.h"
namespace Audio
{
2023-10-25 00:51:33 -04:00
void FInterpolatedMultiTapDelay : : Init ( const int32 InDelayBufferSamples )
2023-09-12 19:28:17 -04:00
{
WriteIndex = 0 ;
DelayLine . Reset ( ) ;
2023-10-25 00:51:33 -04:00
DelayLine . AddZeroed ( InDelayBufferSamples ) ;
2023-10-11 14:51:35 -04:00
WrapBuffer . Reset ( ) ;
2023-10-25 00:51:33 -04:00
WrapBuffer . AddZeroed ( InDelayBufferSamples ) ;
2023-09-12 19:28:17 -04:00
}
2023-10-25 00:51:33 -04:00
void FInterpolatedMultiTapDelay : : Advance ( TArrayView < const float > InBuffer )
2023-09-12 19:28:17 -04:00
{
2023-10-11 14:51:35 -04:00
check ( InBuffer . Num ( ) % AUDIO_NUM_FLOATS_PER_VECTOR_REGISTER = = 0 ) ;
const uint32 InNumSamples = InBuffer . Num ( ) ;
const uint32 DelayBufferNumSamples = DelayLine . Num ( ) ;
2023-09-12 19:28:17 -04:00
if ( InNumSamples < = 0 )
{
return ;
}
2023-10-11 14:51:35 -04:00
if ( InNumSamples + WriteIndex > DelayBufferNumSamples )
2023-09-12 19:28:17 -04:00
{
2023-10-11 14:51:35 -04:00
const int32 FirstHalfSize = DelayBufferNumSamples - WriteIndex ;
2023-09-12 19:28:17 -04:00
const int32 SecondHalfSize = InNumSamples - FirstHalfSize ;
if ( FirstHalfSize > 0 )
{
2023-10-11 14:51:35 -04:00
FMemory : : Memcpy ( & DelayLine [ WriteIndex ] , InBuffer . GetData ( ) , FirstHalfSize * sizeof ( float ) ) ;
2023-09-12 19:28:17 -04:00
}
if ( SecondHalfSize > 0 )
{
2023-10-11 14:51:35 -04:00
FMemory : : Memcpy ( DelayLine . GetData ( ) , & InBuffer [ FirstHalfSize ] , SecondHalfSize * sizeof ( float ) ) ;
2023-09-12 19:28:17 -04:00
}
WriteIndex = SecondHalfSize ;
}
else
{
2023-10-11 14:51:35 -04:00
FMemory : : Memcpy ( & DelayLine [ WriteIndex ] , InBuffer . GetData ( ) , InNumSamples * sizeof ( float ) ) ;
2023-09-12 19:28:17 -04:00
WriteIndex + = InNumSamples ;
2023-10-11 14:51:35 -04:00
WriteIndex = FMath : : Wrap < uint32 > ( WriteIndex , 0 , DelayLine . Num ( ) ) ;
2023-09-12 19:28:17 -04:00
}
}
2023-10-25 00:51:33 -04:00
uint32 FInterpolatedMultiTapDelay : : Read ( const uint32 StartNumDelaySamples , const uint32 StartSampleFraction , const uint32 EndNumDelaySamples , TArrayView < float > OutBuffer )
2023-09-12 19:28:17 -04:00
{
2023-10-11 14:51:35 -04:00
const int32 OutputNumSamples = OutBuffer . Num ( ) ;
2023-10-25 00:51:33 -04:00
int32 DelayBufferNumSamples = DelayLine . Num ( ) ;
2023-09-12 19:28:17 -04:00
// likely to only run on the first frame, if not configured with enough memory
2023-10-11 14:51:35 -04:00
if ( OutputNumSamples > DelayBufferNumSamples )
2023-09-12 19:28:17 -04:00
{
2023-10-25 00:51:33 -04:00
DelayBufferNumSamples = 2 * OutputNumSamples + 1 ;
DelayLine . SetNumZeroed ( DelayBufferNumSamples ) ;
2023-09-12 19:28:17 -04:00
2023-10-25 00:51:33 -04:00
UE_LOG ( LogSignalProcessing , Warning , TEXT ( " FInterpolatedMultiTapDelay not configured with enough memory to process an output buffer - allocating extra space. " ) ) ;
2023-10-11 14:51:35 -04:00
}
2023-09-12 19:28:17 -04:00
2023-10-11 14:51:35 -04:00
if ( DelayBufferNumSamples < = 0 | | OutputNumSamples < = 0 )
2023-09-12 19:28:17 -04:00
{
2023-10-11 14:51:35 -04:00
return 0 ;
}
2023-09-12 19:28:17 -04:00
2023-10-25 00:51:33 -04:00
int32 StartSample = WriteIndex - OutputNumSamples - FMath : : Clamp ( StartNumDelaySamples , 0 , DelayBufferNumSamples - 1 ) ;
int32 EndSample = WriteIndex - FMath : : Clamp ( EndNumDelaySamples , 0 , DelayBufferNumSamples - 1 ) ;
2023-10-11 14:51:35 -04:00
const float SampleStride = FMath : : Clamp ( ( float ) ( EndSample - StartSample ) / ( float ) OutputNumSamples , 0.25f , 4.f ) ;
const uint32 FixedSampleRate = ( uint32 ) ( SampleStride * 65536.f ) ;
StartSample = FMath : : Wrap ( StartSample , 0 , DelayBufferNumSamples - 1 ) ;
2023-09-12 19:28:17 -04:00
2023-10-11 14:51:35 -04:00
Resampler . CurrentFrameFraction = StartSampleFraction ;
const int32 FramesNeeded = ( int32 ) Resampler . SourceFramesNeeded ( OutputNumSamples , FixedSampleRate ) ;
float * SourceBuffer = & DelayLine [ StartSample ] ;
if ( StartSample + FramesNeeded > = DelayBufferNumSamples )
2023-09-12 19:28:17 -04:00
{
2023-10-11 14:51:35 -04:00
const int32 NumSamplesToEnd = DelayBufferNumSamples - StartSample ;
2023-10-25 00:51:33 -04:00
const int32 SecondBufferNumSamples = FramesNeeded - NumSamplesToEnd ;
if ( FramesNeeded > WrapBuffer . Num ( ) )
{
WrapBuffer . SetNumUninitialized ( FramesNeeded ) ;
}
2023-10-11 14:51:35 -04:00
FMemory : : Memcpy ( WrapBuffer . GetData ( ) , & DelayLine [ StartSample ] , NumSamplesToEnd * sizeof ( float ) ) ;
2023-10-25 00:51:33 -04:00
// if used sensibly this shouldn't happen, but better to inject 0's than to over-read the buffer
if ( SecondBufferNumSamples > StartSample )
{
FMemory : : Memcpy ( & WrapBuffer [ NumSamplesToEnd ] , DelayLine . GetData ( ) , StartSample * sizeof ( float ) ) ;
const int32 FramesRemaining = SecondBufferNumSamples - StartSample ;
FMemory : : Memzero ( & WrapBuffer [ NumSamplesToEnd + SecondBufferNumSamples ] , FramesRemaining * sizeof ( float ) ) ;
}
else
{
FMemory : : Memcpy ( & WrapBuffer [ NumSamplesToEnd ] , DelayLine . GetData ( ) , SecondBufferNumSamples * sizeof ( float ) ) ;
}
2023-10-11 14:51:35 -04:00
SourceBuffer = WrapBuffer . GetData ( ) ;
2023-09-12 19:28:17 -04:00
}
2023-10-11 14:51:35 -04:00
Resampler . ResampleMono ( OutputNumSamples , FixedSampleRate , SourceBuffer , OutBuffer . GetData ( ) ) ;
return Resampler . CurrentFrameFraction ;
2023-09-12 19:28:17 -04:00
}
void FInterpolatedMultiTapDelay : : Reset ( )
{
const int32 NumElements = DelayLine . Num ( ) ;
DelayLine . Reset ( NumElements ) ;
DelayLine . AddZeroed ( NumElements ) ;
}
bool FInterpolatedMultiTapDelay : : IsInitialized ( ) const
{
2023-10-11 14:51:35 -04:00
return DelayLine . GetAllocatedSize ( ) > 0 ;
2023-09-12 19:28:17 -04:00
}
}