2021-11-07 23:43:01 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DSP/AudioBufferDistanceAttenuation.h"
# include "DSP/Dsp.h"
# include "DSP/BufferVectorOperations.h"
namespace Audio
{
2021-11-18 14:37:34 -05:00
static float ComputeNextLinearAttenuation ( const FAudioBufferDistanceAttenuationSettings & InSettings , float InCurrentDistance )
2021-11-07 23:43:01 -05:00
{
float NextAttenuationDb = 0.0f ;
2021-11-18 14:37:34 -05:00
const float Denom = FMath : : Max ( InSettings . DistanceRange . Y - InSettings . DistanceRange . X , SMALL_NUMBER ) ;
const float Alpha = FMath : : Clamp ( ( InCurrentDistance - InSettings . DistanceRange . X ) / Denom , 0.0f , 1.0f ) ;
2021-11-07 23:43:01 -05:00
float CurveValue = 0.0f ;
2022-04-28 18:53:13 -04:00
if ( InSettings . AttenuationCurve . Num ( ) > 0 )
{
bool bSuccess = InSettings . AttenuationCurve . Eval ( Alpha , CurveValue ) ;
// This should succeed since we ensure we always have at least two points in the curve when it's set
check ( bSuccess ) ;
}
else
{
// We do a linear attenuation if there is no curve
CurveValue = 1.0f - Alpha ;
}
2021-11-07 23:43:01 -05:00
// Note the curve is expected to map to the attenuation amount (i.e. at right-most value, it'll be 0.0, which corresponds to max dB attenuation)
// This then needs to be used to interpolate the dB range (0.0 is no attenuation, -60 for example, is a lot of attenuation)
2021-11-18 14:37:34 -05:00
NextAttenuationDb = FMath : : Lerp ( InSettings . AttenuationDbAtMaxRange , 0.0f , CurveValue ) ;
2021-11-07 23:43:01 -05:00
float TargetAttenuationLinear = 0.0f ;
2021-11-18 14:37:34 -05:00
if ( NextAttenuationDb > InSettings . AttenuationDbAtMaxRange )
2021-11-07 23:43:01 -05:00
{
TargetAttenuationLinear = Audio : : ConvertToLinear ( NextAttenuationDb ) ;
}
return TargetAttenuationLinear ;
}
2021-11-18 14:37:34 -05:00
void DistanceAttenuationProcessAudio ( TArrayView < int16 > & InOutBuffer , uint32 InNumChannels , float InDistance , const FAudioBufferDistanceAttenuationSettings & InSettings , float & InOutAttenuation )
2021-11-07 23:43:01 -05:00
{
2021-11-18 14:37:34 -05:00
check ( InOutBuffer . Num ( ) > 0 ) ;
2021-11-07 23:43:01 -05:00
check ( InNumChannels > 0 ) ;
2021-11-18 14:37:34 -05:00
check ( InDistance > = 0.0f ) ;
2021-11-07 23:43:01 -05:00
2021-11-18 14:37:34 -05:00
int32 FrameCount = InOutBuffer . Num ( ) / InNumChannels ;
float TargetAttenuationLinear = ComputeNextLinearAttenuation ( InSettings , InDistance ) ;
2021-11-07 23:43:01 -05:00
// TODO: investigate adding int16 flavors of utilities in BufferVectorOperations.h to avoid format conversions
uint32 CurrentSampleIndex = 0 ;
2021-11-18 14:37:34 -05:00
// If we're passed in a negative value for InOutAttenuation, that means we don't want to interpolate from that value to target value (i.e. it's the first one).
// This prevents a pop when first applying attenuation if a sound is far away.
float Gain = InOutAttenuation < 0.0f ? TargetAttenuationLinear : InOutAttenuation ;
2021-12-10 20:59:47 -05:00
const float DeltaValue = ( TargetAttenuationLinear - Gain ) / FrameCount ;
2021-11-18 14:37:34 -05:00
for ( int32 FrameIndex = 0 ; FrameIndex < FrameCount ; + + FrameIndex )
2021-11-07 23:43:01 -05:00
{
for ( uint32 ChannelIndex = 0 ; ChannelIndex < InNumChannels ; + + ChannelIndex )
{
2021-11-18 14:37:34 -05:00
InOutBuffer [ CurrentSampleIndex + ChannelIndex ] = InOutBuffer [ CurrentSampleIndex + ChannelIndex ] * Gain ;
2021-11-07 23:43:01 -05:00
}
CurrentSampleIndex + = InNumChannels ;
Gain + = DeltaValue ;
}
// Update the current attenuation linear for the next render block
2021-11-18 14:37:34 -05:00
InOutAttenuation = TargetAttenuationLinear ;
2021-11-07 23:43:01 -05:00
}
2021-11-18 14:37:34 -05:00
void DistanceAttenuationProcessAudio ( TArrayView < float > & InOutBuffer , uint32 InNumChannels , float InDistance , const FAudioBufferDistanceAttenuationSettings & InSettings , float & InOutAttenuation )
2021-11-07 23:43:01 -05:00
{
2021-11-18 14:37:34 -05:00
check ( InOutBuffer . Num ( ) > 0 ) ;
2021-11-07 23:43:01 -05:00
check ( InNumChannels > 0 ) ;
2021-11-18 14:37:34 -05:00
check ( InDistance > = 0.0f ) ;
2021-11-07 23:43:01 -05:00
2021-11-18 14:37:34 -05:00
int32 FrameCount = InOutBuffer . Num ( ) / InNumChannels ;
float TargetAttenuationLinear = ComputeNextLinearAttenuation ( InSettings , InDistance ) ;
2021-11-07 23:43:01 -05:00
2021-11-18 14:37:34 -05:00
int32 NumSamples = ( int32 ) ( FrameCount * InNumChannels ) ;
// If we're passed in a negative value for InOutAttenuation, that means we don't want to interpolate from that value to target value (i.e. it's the first one).
// This prevents a pop when first applying attenuation if a sound is far away.
float Gain = InOutAttenuation < 0.0f ? TargetAttenuationLinear : InOutAttenuation ;
2022-04-04 13:09:43 -04:00
TArrayView < float > InOutBufferView ( InOutBuffer . GetData ( ) , NumSamples ) ;
Audio : : ArrayFade ( InOutBufferView , Gain , TargetAttenuationLinear ) ;
2021-11-07 23:43:01 -05:00
2021-11-18 14:37:34 -05:00
InOutAttenuation = TargetAttenuationLinear ;
2021-11-07 23:43:01 -05:00
}
2021-11-18 14:37:34 -05:00
}