2019-12-26 14:45:42 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2019-08-15 13:50:15 -04:00
|
|
|
|
|
|
|
|
#include "DSP/FloatArrayMath.h"
|
|
|
|
|
#include "CoreMinimal.h"
|
|
|
|
|
|
2019-09-12 13:49:12 -04:00
|
|
|
|
2019-08-15 13:50:15 -04:00
|
|
|
namespace Audio
|
|
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
namespace MathIntrinsics
|
|
|
|
|
{
|
|
|
|
|
const float Loge10 = FMath::Loge(10.f);
|
|
|
|
|
const int32 SimdMask = 0xFFFFFFFC;
|
|
|
|
|
const int32 NotSimdMask = 0x00000003;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void ArrayCumulativeSum(TArrayView<const float> InView, TArray<float>& OutData)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
// Initialize output data
|
2019-10-02 13:45:06 -04:00
|
|
|
int32 Num = InView.Num();
|
2019-08-15 13:50:15 -04:00
|
|
|
OutData.Reset();
|
|
|
|
|
OutData.AddUninitialized(Num);
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float* OutDataPtr = OutData.GetData();
|
2019-10-02 13:45:06 -04:00
|
|
|
const float* InViewPtr = InView.GetData();
|
2019-08-15 13:50:15 -04:00
|
|
|
|
|
|
|
|
// Start summing
|
2019-10-02 13:45:06 -04:00
|
|
|
*OutDataPtr = *InViewPtr++;
|
2019-08-15 13:50:15 -04:00
|
|
|
|
|
|
|
|
for (int32 i = 1; i < Num; i++)
|
|
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
float Temp = *OutDataPtr++ + *InViewPtr++;
|
2019-08-15 13:50:15 -04:00
|
|
|
*OutDataPtr = Temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 12:19:40 -04:00
|
|
|
void ArrayMean(TArrayView<const float> InView, float& OutMean)
|
|
|
|
|
{
|
|
|
|
|
OutMean = 0.f;
|
|
|
|
|
|
|
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float* DataPtr = InView.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
OutMean += DataPtr[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutMean /= static_cast<float>(Num);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void ArrayMeanFilter(TArrayView<const float> InView, int32 WindowSize, int32 WindowOrigin, TArray<float>& OutData)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
// a quick but sinful implementation of a mean filter. encourages floating point rounding errors.
|
|
|
|
|
check(WindowOrigin < WindowSize);
|
|
|
|
|
check(WindowOrigin >= 0);
|
|
|
|
|
check(WindowSize > 0);
|
|
|
|
|
|
|
|
|
|
// Initialize output data
|
2019-10-02 13:45:06 -04:00
|
|
|
const int32 Num = InView.Num();
|
2019-08-15 13:50:15 -04:00
|
|
|
OutData.Reset();
|
|
|
|
|
OutData.AddUninitialized(Num);
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use cumulative sum to avoid multiple summations
|
2019-10-02 13:45:06 -04:00
|
|
|
// Instead of summing over InView[StartIndex:EndIndex], avoid all that
|
2019-08-15 13:50:15 -04:00
|
|
|
// calculation by taking difference of cumulative sum at those two points:
|
|
|
|
|
// cumsum(X[0:b]) - cumsum(X[0:a]) = sum(X[a:b])
|
|
|
|
|
TArray<float> SummedData;
|
2019-10-02 13:45:06 -04:00
|
|
|
ArrayCumulativeSum(InView, SummedData);
|
2019-08-15 13:50:15 -04:00
|
|
|
const float LastSummedData = SummedData.Last();
|
|
|
|
|
|
|
|
|
|
|
2019-09-03 18:19:36 -04:00
|
|
|
const int32 LastIndexBeforeEndBoundaryCondition = FMath::Max(WindowOrigin + 1, Num - WindowSize + WindowOrigin + 1);
|
2019-08-15 13:50:15 -04:00
|
|
|
const int32 StartOffset = -WindowOrigin - 1;
|
|
|
|
|
const int32 EndOffset = WindowSize - WindowOrigin - 1;
|
|
|
|
|
const int32 WindowTail = WindowSize - WindowOrigin;
|
|
|
|
|
|
|
|
|
|
float* OutDataPtr = OutData.GetData();
|
|
|
|
|
const float* SummedDataPtr = SummedData.GetData();
|
|
|
|
|
|
|
|
|
|
if ((WindowSize - WindowOrigin) < Num)
|
|
|
|
|
{
|
|
|
|
|
// Handle boundary condition where analysis window precedes beginning of array.
|
|
|
|
|
for (int32 i = 0; i < (WindowOrigin + 1); i++)
|
|
|
|
|
{
|
|
|
|
|
OutDataPtr[i] = SummedDataPtr[i + EndOffset] / FMath::Max(1.f, static_cast<float>(WindowTail + i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No boundary conditions to handle here.
|
|
|
|
|
const float MeanDivisor = static_cast<float>(WindowSize);
|
|
|
|
|
for (int32 i = WindowOrigin + 1; i < LastIndexBeforeEndBoundaryCondition; i++)
|
|
|
|
|
{
|
|
|
|
|
OutDataPtr[i] = (SummedDataPtr[i + EndOffset] - SummedDataPtr[i + StartOffset]) / MeanDivisor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Handle boundary condition where window precedes beginning and goes past end of array
|
|
|
|
|
const float ArrayMean = LastSummedData / static_cast<float>(Num);
|
|
|
|
|
for (int32 i = 0; i < LastIndexBeforeEndBoundaryCondition; i++)
|
|
|
|
|
{
|
|
|
|
|
OutDataPtr[i] = ArrayMean;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle boundary condition where analysis window goes past end of array.
|
|
|
|
|
for (int32 i = LastIndexBeforeEndBoundaryCondition; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
OutDataPtr[i] = (LastSummedData - SummedDataPtr[i + StartOffset]) / static_cast<float>(Num - i + WindowOrigin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void ArrayMaxFilter(TArrayView<const float> InView, int32 WindowSize, int32 WindowOrigin, TArray<float>& OutData)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
// A reasonable implementation of a max filter for the data we're interested in, though surely not the fastest.
|
|
|
|
|
check(WindowOrigin < WindowSize);
|
|
|
|
|
check(WindowOrigin >= 0);
|
|
|
|
|
check(WindowSize > 0);
|
|
|
|
|
|
|
|
|
|
int32 StartIndex = -WindowOrigin;
|
|
|
|
|
int32 EndIndex = StartIndex + WindowSize;
|
|
|
|
|
|
|
|
|
|
// Initialize output
|
2019-10-02 13:45:06 -04:00
|
|
|
int32 Num = InView.Num();
|
2019-08-15 13:50:15 -04:00
|
|
|
OutData.Reset();
|
|
|
|
|
OutData.AddUninitialized(Num);
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get max in first window
|
|
|
|
|
int32 ActualStartIndex = 0;
|
|
|
|
|
int32 ActualEndIndex = FMath::Min(EndIndex, Num);
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
const float* InViewPtr = InView.GetData();
|
2019-08-15 13:50:15 -04:00
|
|
|
float* OutDataPtr = OutData.GetData();
|
|
|
|
|
int32 MaxIndex = 0;
|
2019-10-02 13:45:06 -04:00
|
|
|
float MaxValue = InView[0];
|
2019-08-15 13:50:15 -04:00
|
|
|
|
|
|
|
|
for (int32 i = ActualStartIndex; i < ActualEndIndex; i++)
|
|
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
if (InViewPtr[i] > MaxValue)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
MaxValue = InViewPtr[i];
|
2019-08-15 13:50:15 -04:00
|
|
|
MaxIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
OutDataPtr[0] = MaxValue;
|
|
|
|
|
|
|
|
|
|
StartIndex++;
|
|
|
|
|
EndIndex++;
|
|
|
|
|
|
|
|
|
|
// Get max in remaining windows
|
|
|
|
|
for (int32 i = 1; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
ActualStartIndex = FMath::Max(StartIndex, 0);
|
|
|
|
|
ActualEndIndex = FMath::Min(EndIndex, Num);
|
|
|
|
|
|
|
|
|
|
if (MaxIndex < StartIndex)
|
|
|
|
|
{
|
|
|
|
|
// We need to evaluate the entire window because the previous maximum value was not in this window.
|
|
|
|
|
MaxIndex = ActualStartIndex;
|
2019-10-02 13:45:06 -04:00
|
|
|
MaxValue = InViewPtr[MaxIndex];
|
2019-08-15 13:50:15 -04:00
|
|
|
for (int32 j = ActualStartIndex + 1; j < ActualEndIndex; j++)
|
|
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
if (InViewPtr[j] > MaxValue)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
MaxIndex = j;
|
2019-10-02 13:45:06 -04:00
|
|
|
MaxValue = InViewPtr[MaxIndex];
|
2019-08-15 13:50:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We only need to inspect the newest sample because the previous maximum value was in this window.
|
2019-10-02 13:45:06 -04:00
|
|
|
if (InViewPtr[ActualEndIndex - 1] > MaxValue)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
MaxIndex = ActualEndIndex - 1;
|
2019-10-02 13:45:06 -04:00
|
|
|
MaxValue = InViewPtr[MaxIndex];
|
2019-08-15 13:50:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutDataPtr[i] = MaxValue;
|
|
|
|
|
|
|
|
|
|
StartIndex++;
|
|
|
|
|
EndIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void ArrayGetEuclideanNorm(TArrayView<const float> InView, float& OutEuclideanNorm)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
|
|
|
|
// Initialize output.
|
|
|
|
|
OutEuclideanNorm = 0.0f;
|
2019-10-02 13:45:06 -04:00
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
const float* InViewData = InView.GetData();
|
2019-08-15 13:50:15 -04:00
|
|
|
|
|
|
|
|
// Sum it up.
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
OutEuclideanNorm += InViewData[i] * InViewData[i];
|
2019-08-15 13:50:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutEuclideanNorm = FMath::Sqrt(OutEuclideanNorm);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 12:19:40 -04:00
|
|
|
void ArrayAbsInPlace(TArrayView<float> InView)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
float* Data = InView.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
Data[i] = FMath::Abs(Data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayClampMinInPlace(TArrayView<float> InView, float InMin)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
float* Data = InView.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
Data[i] = FMath::Max(InMin, Data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayClampMaxInPlace(TArrayView<float> InView, float InMax)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
float* Data = InView.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
Data[i] = FMath::Min(InMax, Data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void ArrayClampInPlace(TArrayView<float> InView, float InMin, float InMax)
|
2019-08-15 13:50:15 -04:00
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
float* Data = InView.GetData();
|
|
|
|
|
|
2019-08-15 13:50:15 -04:00
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
Data[i] = FMath::Clamp(Data[i], InMin, InMax);
|
2019-08-15 13:50:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
2019-09-03 18:19:36 -04:00
|
|
|
|
2019-10-31 12:19:40 -04:00
|
|
|
void ArrayMinMaxNormalize(TArrayView<const float> InView, TArray<float>& OutArray)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InView.Num();
|
|
|
|
|
OutArray.Reset(Num);
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutArray.AddUninitialized(Num);
|
|
|
|
|
|
|
|
|
|
const float* InDataPtr = InView.GetData();
|
|
|
|
|
float MaxValue = InDataPtr[0];
|
|
|
|
|
float MinValue = InDataPtr[0];
|
|
|
|
|
|
|
|
|
|
// determine min and max
|
|
|
|
|
for (int32 i = 1; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
if (InDataPtr[i] < MinValue)
|
|
|
|
|
{
|
|
|
|
|
MinValue = InDataPtr[i];
|
|
|
|
|
}
|
|
|
|
|
else if (InDataPtr[i] > MaxValue)
|
|
|
|
|
{
|
|
|
|
|
MaxValue = InDataPtr[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize data by subtracting minimum value and dividing by range
|
|
|
|
|
float* OutDataPtr = OutArray.GetData();
|
|
|
|
|
float Scale = 1.f / FMath::Max(SMALL_NUMBER, MaxValue - MinValue);
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
OutDataPtr[i] = (InDataPtr[i] - MinValue) * Scale;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 18:21:12 -05:00
|
|
|
void ArrayMultiplyByConstantInPlace(TArrayView<float> InValues, float InMultiplier)
|
2019-09-12 13:49:12 -04:00
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
float* InData = InValues.GetData();
|
|
|
|
|
|
2019-09-12 13:49:12 -04:00
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
InData[i] *= InMultiplier;
|
2019-09-12 13:49:12 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 18:21:12 -05:00
|
|
|
void ArrayMultiplyByConstantInPlace(AlignedFloatBuffer& InValues, float InMultiplier)
|
2019-09-12 13:49:12 -04:00
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
const int32 NumToSimd = Num & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = Num & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
float* InData = InValues.GetData();
|
|
|
|
|
|
|
|
|
|
if (NumToSimd)
|
|
|
|
|
{
|
|
|
|
|
MultiplyBufferByConstantInPlace(InData, NumToSimd, InMultiplier);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<float> ValuesView(&InData[NumToSimd], NumNotToSimd);
|
|
|
|
|
|
|
|
|
|
ArrayMultiplyByConstantInPlace(ValuesView, InMultiplier);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayAddInPlace(TArrayView<const float> InValues, TArrayView<float> InAccumulateValues)
|
|
|
|
|
{
|
|
|
|
|
check(InValues.Num() == InAccumulateValues.Num());
|
|
|
|
|
|
|
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
|
|
|
|
|
const float* InData = InValues.GetData();
|
|
|
|
|
float* InAccumulateData = InAccumulateValues.GetData();
|
|
|
|
|
|
2019-09-12 13:49:12 -04:00
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
InAccumulateData[i] += InData[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayAddInPlace(const AlignedFloatBuffer& InValues, AlignedFloatBuffer& InAccumulateValues)
|
|
|
|
|
{
|
|
|
|
|
check(InValues.Num() == InAccumulateValues.Num());
|
|
|
|
|
|
|
|
|
|
const int32 Num = InAccumulateValues.Num();
|
|
|
|
|
const int32 NumToSimd = Num & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = Num & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
const float* InData = InValues.GetData();
|
|
|
|
|
float* InAccumulateData = InAccumulateValues.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumToSimd; i += 4)
|
|
|
|
|
{
|
|
|
|
|
VectorRegister VectorData = VectorLoadAligned(&InData[i]);
|
|
|
|
|
VectorRegister VectorAccumData = VectorLoadAligned(&InAccumulateData[i]);
|
|
|
|
|
|
|
|
|
|
VectorRegister VectorOut = VectorAdd(VectorData, VectorAccumData);
|
|
|
|
|
VectorStoreAligned(VectorOut, &InAccumulateData[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<const float> ValuesView(&InData[NumToSimd], NumNotToSimd);
|
|
|
|
|
TArrayView<float> AccumulateView(&InAccumulateData[NumToSimd], NumNotToSimd);
|
|
|
|
|
|
|
|
|
|
ArrayAddInPlace(ValuesView, AccumulateView);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArraySubtractByConstantInPlace(TArrayView<float> InValues, float InSubtrahend)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
float* InValuesData = InValues.GetData();
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
InValuesData[i] -= InSubtrahend;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArraySubtractByConstantInPlace(AlignedFloatBuffer& InValues, float InSubtrahend)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
const int32 NumToSimd = Num & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = Num & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
float* InData = InValues.GetData();
|
|
|
|
|
|
|
|
|
|
const VectorRegister VectorSubtrahend = VectorSetFloat1(InSubtrahend);
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumToSimd; i += 4)
|
|
|
|
|
{
|
|
|
|
|
VectorRegister VectorData = VectorLoadAligned(&InData[i]);
|
|
|
|
|
VectorData = VectorSubtract(VectorData, VectorSubtrahend);
|
|
|
|
|
VectorStoreAligned(VectorData, &InData[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<float> View(&InData[NumToSimd], NumNotToSimd);
|
|
|
|
|
ArraySubtractByConstantInPlace(View, InSubtrahend);
|
2019-09-12 13:49:12 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 12:19:40 -04:00
|
|
|
void ArraySubtract(TArrayView<const float> InMinuend, TArrayView<const float> InSubtrahend, TArray<float>& OutArray)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InMinuend.Num();
|
|
|
|
|
|
|
|
|
|
checkf(Num == InSubtrahend.Num(), TEXT("InMinuend and InSubtrahend must have equal Num elements (%d vs %d)"), Num, InSubtrahend.Num());
|
|
|
|
|
|
|
|
|
|
OutArray.Reset(Num);
|
|
|
|
|
|
|
|
|
|
if (Num < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutArray.AddUninitialized(Num);
|
|
|
|
|
|
|
|
|
|
const float* MinuendPtr = InMinuend.GetData();
|
|
|
|
|
const float* SubtrahendPtr = InSubtrahend.GetData();
|
|
|
|
|
float* OutPtr = OutArray.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
OutPtr[i] = MinuendPtr[i] - SubtrahendPtr[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 18:21:12 -05:00
|
|
|
void ArrayMagnitudeToDecibelInPlace(TArrayView<float> InValues)
|
2019-09-12 13:49:12 -04:00
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
float* InValuesData = InValues.GetData();
|
2019-09-12 13:49:12 -04:00
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
InValuesData[i] = 20.f * FMath::Loge(InValuesData[i]) / MathIntrinsics::Loge10;
|
2019-10-02 13:45:06 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 18:21:12 -05:00
|
|
|
void ArrayMagnitudeToDecibelInPlace(AlignedFloatBuffer& InValues)
|
2019-10-02 13:45:06 -04:00
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
const int32 NumToSimd = Num & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = Num & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
float* InData = InValues.GetData();
|
|
|
|
|
|
|
|
|
|
const VectorRegister VectorScale = VectorSetFloat1(20.f / MathIntrinsics::Loge10);
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumToSimd; i += 4)
|
2019-10-02 13:45:06 -04:00
|
|
|
{
|
2020-02-25 18:21:12 -05:00
|
|
|
VectorRegister VectorData = VectorLoadAligned(&InData[i]);
|
|
|
|
|
|
|
|
|
|
VectorData = VectorLog(VectorData);
|
|
|
|
|
VectorData = VectorMultiply(VectorData, VectorScale);
|
|
|
|
|
|
|
|
|
|
VectorStoreAligned(VectorData, &InData[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<float> InView(&InData[NumToSimd], NumNotToSimd);
|
|
|
|
|
ArrayMagnitudeToDecibelInPlace(InView);
|
2019-09-12 13:49:12 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 18:21:12 -05:00
|
|
|
void ArrayPowerToDecibelInPlace(TArrayView<float> InValues)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
float* InValuesData = InValues.GetData();
|
|
|
|
|
for (int32 i = 0; i < Num; i++)
|
|
|
|
|
{
|
|
|
|
|
InValuesData[i] = 10.f * FMath::Loge(InValuesData[i]) / MathIntrinsics::Loge10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayPowerToDecibelInPlace(AlignedFloatBuffer& InValues)
|
|
|
|
|
{
|
|
|
|
|
const int32 Num = InValues.Num();
|
|
|
|
|
const int32 NumToSimd = Num & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = Num & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
float* InData = InValues.GetData();
|
|
|
|
|
|
|
|
|
|
const VectorRegister VectorScale = VectorSetFloat1(10.f / MathIntrinsics::Loge10);
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumToSimd; i += 4)
|
|
|
|
|
{
|
|
|
|
|
VectorRegister VectorData = VectorLoadAligned(&InData[i]);
|
|
|
|
|
|
|
|
|
|
VectorData = VectorLog(VectorData);
|
|
|
|
|
VectorData = VectorMultiply(VectorData, VectorScale);
|
|
|
|
|
|
|
|
|
|
VectorStoreAligned(VectorData, &InData[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<float> InView(&InData[NumToSimd], NumNotToSimd);
|
|
|
|
|
ArrayPowerToDecibelInPlace(InView);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayComplexToPower(TArrayView<const float> InComplexValues, TArrayView<float> OutPowerValues)
|
|
|
|
|
{
|
|
|
|
|
check((InComplexValues.Num() % 2) == 0);
|
|
|
|
|
check(InComplexValues.Num() == (OutPowerValues.Num() * 2));
|
|
|
|
|
|
|
|
|
|
const int32 NumOut = OutPowerValues.Num();
|
|
|
|
|
|
|
|
|
|
const float* InComplexData = InComplexValues.GetData();
|
|
|
|
|
float* OutPowerData = OutPowerValues.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumOut; i++)
|
|
|
|
|
{
|
|
|
|
|
int32 ComplexPos = 2 * i;
|
|
|
|
|
|
|
|
|
|
float RealValue = InComplexData[ComplexPos];
|
|
|
|
|
float ImagValue = InComplexData[ComplexPos + 1];
|
|
|
|
|
|
|
|
|
|
OutPowerData[i] = (RealValue * RealValue) + (ImagValue * ImagValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ArrayComplexToPower(const AlignedFloatBuffer& InComplexValues, AlignedFloatBuffer& OutPowerValues)
|
|
|
|
|
{
|
|
|
|
|
check((InComplexValues.Num() % 2) == 0);
|
|
|
|
|
check(InComplexValues.Num() == (OutPowerValues.Num() * 2));
|
|
|
|
|
|
|
|
|
|
const int32 NumOut = OutPowerValues.Num();
|
|
|
|
|
const int32 NumToSimd = NumOut & MathIntrinsics::SimdMask;
|
|
|
|
|
const int32 NumNotToSimd = NumOut & MathIntrinsics::NotSimdMask;
|
|
|
|
|
|
|
|
|
|
const float* InComplexData = InComplexValues.GetData();
|
|
|
|
|
float* OutPowerData = OutPowerValues.GetData();
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < NumToSimd; i += 4)
|
|
|
|
|
{
|
|
|
|
|
VectorRegister VectorComplex1 = VectorLoadAligned(&InComplexData[2 * i]);
|
|
|
|
|
VectorRegister VectorSquared1 = VectorMultiply (VectorComplex1, VectorComplex1);
|
|
|
|
|
|
|
|
|
|
VectorRegister VectorComplex2 = VectorLoadAligned(&InComplexData[(2 * i) + 4]);
|
|
|
|
|
VectorRegister VectorSquared2 = VectorMultiply (VectorComplex2, VectorComplex2);
|
|
|
|
|
|
|
|
|
|
VectorRegister VectorSquareReal = VectorShuffle(VectorSquared1, VectorSquared2, 0, 2, 0, 2);
|
|
|
|
|
VectorRegister VectorSquareImag = VectorShuffle(VectorSquared1, VectorSquared2, 1, 3, 1, 3);
|
|
|
|
|
|
|
|
|
|
VectorRegister VectorOut = VectorAdd(VectorSquareReal, VectorSquareImag);
|
|
|
|
|
|
|
|
|
|
VectorStoreAligned(VectorOut, &OutPowerData[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumNotToSimd)
|
|
|
|
|
{
|
|
|
|
|
TArrayView<const float> ComplexView(&InComplexData[2 * NumToSimd], 2 * NumNotToSimd);
|
|
|
|
|
TArrayView<float> PowerView(&OutPowerData[NumToSimd], NumNotToSimd);
|
|
|
|
|
|
|
|
|
|
ArrayComplexToPower(ComplexView, PowerView);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-09-03 18:19:36 -04:00
|
|
|
FContiguousSparse2DKernelTransform::FContiguousSparse2DKernelTransform(const int32 NumInElements, const int32 NumOutElements)
|
|
|
|
|
: NumIn(NumInElements)
|
|
|
|
|
, NumOut(NumOutElements)
|
|
|
|
|
{
|
|
|
|
|
check(NumIn >= 0);
|
|
|
|
|
check(NumOut >= 0)
|
|
|
|
|
FRow EmptyRow;
|
|
|
|
|
EmptyRow.StartIndex = 0;
|
|
|
|
|
|
|
|
|
|
// Fill up the kernel with emptp rows
|
|
|
|
|
Kernel.Init(EmptyRow, NumOut);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 FContiguousSparse2DKernelTransform::GetNumInElements() const
|
|
|
|
|
{
|
|
|
|
|
return NumIn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int32 FContiguousSparse2DKernelTransform::GetNumOutElements() const
|
|
|
|
|
{
|
|
|
|
|
return NumOut;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FContiguousSparse2DKernelTransform::SetRow(const int32 RowIndex, const int32 StartIndex, TArrayView<const float> OffsetValues)
|
|
|
|
|
{
|
|
|
|
|
check((StartIndex + OffsetValues.Num()) <= NumIn);
|
|
|
|
|
|
|
|
|
|
// Copy row data internally
|
|
|
|
|
Kernel[RowIndex].StartIndex = StartIndex;
|
|
|
|
|
Kernel[RowIndex].OffsetValues = TArray<float>(OffsetValues.GetData(), OffsetValues.Num());
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void FContiguousSparse2DKernelTransform::TransformArray(TArrayView<const float> InView, TArray<float>& OutArray) const
|
2019-09-03 18:19:36 -04:00
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
check(InView.Num() == NumIn);
|
2019-09-03 18:19:36 -04:00
|
|
|
|
|
|
|
|
// Resize output
|
|
|
|
|
OutArray.Reset(NumOut);
|
|
|
|
|
if (NumOut > 0)
|
|
|
|
|
{
|
|
|
|
|
OutArray.AddUninitialized(NumOut);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
TransformArray(InView.GetData(), OutArray.GetData());
|
2019-09-03 18:19:36 -04:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
void FContiguousSparse2DKernelTransform::TransformArray(TArrayView<const float> InView, AlignedFloatBuffer& OutArray) const
|
2019-09-03 18:19:36 -04:00
|
|
|
{
|
2019-10-02 13:45:06 -04:00
|
|
|
check(InView.Num() == NumIn);
|
2019-09-03 18:19:36 -04:00
|
|
|
|
|
|
|
|
// Resize output
|
|
|
|
|
OutArray.Reset(NumOut);
|
|
|
|
|
if (NumOut > 0)
|
|
|
|
|
{
|
|
|
|
|
OutArray.AddUninitialized(NumOut);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 13:45:06 -04:00
|
|
|
TransformArray(InView.GetData(), OutArray.GetData());
|
2019-09-03 18:19:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FContiguousSparse2DKernelTransform::TransformArray(const float* InArray, float* OutArray) const
|
|
|
|
|
{
|
|
|
|
|
check(nullptr != InArray);
|
|
|
|
|
check(nullptr != OutArray);
|
|
|
|
|
|
|
|
|
|
// Initialize output
|
|
|
|
|
FMemory::Memset(OutArray, 0, sizeof(float) * NumOut);
|
|
|
|
|
|
|
|
|
|
// Apply kernel one row at a time
|
|
|
|
|
const FRow* KernelData = Kernel.GetData();
|
|
|
|
|
for (int32 RowIndex = 0; RowIndex < Kernel.Num(); RowIndex++)
|
|
|
|
|
{
|
|
|
|
|
const FRow& Row = KernelData[RowIndex];
|
|
|
|
|
|
|
|
|
|
// Get offset pointer into input array.
|
|
|
|
|
const float* OffsetInData = &InArray[Row.StartIndex];
|
|
|
|
|
// Get offset pointer of row.
|
|
|
|
|
const float* RowValuePtr = Row.OffsetValues.GetData();
|
|
|
|
|
|
|
|
|
|
// dot prod 'em.
|
|
|
|
|
int32 NumToMult = Row.OffsetValues.Num();
|
|
|
|
|
for (int32 i = 0; i < NumToMult; i++)
|
|
|
|
|
{
|
|
|
|
|
OutArray[RowIndex] += OffsetInData[i] * RowValuePtr[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-15 13:50:15 -04:00
|
|
|
}
|