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

106 lines
2.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DSP/AudioLinearCurve.h"
#include "Algo/BinarySearch.h"
namespace Audio
{
FLinearCurve::FLinearCurve(const TArray<FVector2D>& InPoints)
{
Points = InPoints;
Sort();
}
FLinearCurve::FLinearCurve(TArray<FVector2D>&& InPoints)
{
Points = MoveTemp(InPoints);
Sort();
}
void FLinearCurve::SetCurvePoints(const TArray<FVector2D>& InPoints)
{
Points = InPoints;
Sort();
}
void FLinearCurve::SetCurvePoints(TArray<FVector2D>&& InPoints)
{
Points = MoveTemp(InPoints);
Sort();
}
void FLinearCurve::ClearPoints()
{
Points.Reset();
}
void FLinearCurve::AddPoint(const FVector2D& InPoint)
{
Points.Add(InPoint);
Sort();
}
void FLinearCurve::AddPoints(const TArray<FVector2D>& InPoints)
{
Points.Append(InPoints);
Sort();
}
const TArray<FVector2D>& FLinearCurve::GetCurvePoints() const
{
return Points;
}
void FLinearCurve::Sort()
{
Points.Sort([](const FVector2D& A, const FVector2D& B) { return A.X < B.X; });
}
bool FLinearCurve::Eval(float InDomain, float& OutValue) const
{
// Can't be evaluated
if (!Points.Num())
{
return false;
}
// Note the curve is already sorted according to increasing domain values
// Check edge cases first. We clamp the output value to the edges of the curve.
if (InDomain <= Points[0].X)
{
// To the left of the curve domain
OutValue = Points[0].Y;
return true;
}
else if (InDomain >= Points[Points.Num() - 1].X)
{
// To the right of the curve domain
OutValue = Points[Points.Num() - 1].Y;
return true;
}
// Find the two points to select to do an interpolation
FVector2D PrevPoint;
FVector2D CurrPoint;
PrevPoint.X = InDomain;
int32 Index = Algo::UpperBound(Points, PrevPoint, [](const FVector2D& A, const FVector2D& B) { return A.X < B.X; });
if (Index <= Points.Num() && Index >= 1)
{
PrevPoint = Points[Index - 1];
CurrPoint = Points[Index];
// Linearly interpolate between the two points using the given domain value
float Alpha = (InDomain - PrevPoint.X) / FMath::Max(CurrPoint.X - PrevPoint.X, SMALL_NUMBER);
check(Alpha >= 0.0f && Alpha <= 1.0f);
OutValue = FMath::Lerp(PrevPoint.Y, CurrPoint.Y, Alpha);
return true;
}
return false;
}
}