Files
UnrealEngine/Engine/Source/Runtime/SignalProcessing/Public/DSP/FloatArrayMath.h
2025-05-18 13:04:45 +08:00

385 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "DSP/BufferVectorOperations.h"
namespace Audio
{
/** Sum all values in an array. */
SIGNALPROCESSING_API void ArraySum(TArrayView<const float> InValues, float& OutSum);
/** Sums two buffers together and places the result in the resulting buffer. */
SIGNALPROCESSING_API void ArraySum(TArrayView<const float> InFloatBuffer1, TArrayView<const float> InFloatBuffer2, TArrayView<float> OutputBuffer);
/** Cumulative sum of array.
*
* InView contains data to be cumulatively summed.
* OutData contains sum and is same size as InView.
*/
SIGNALPROCESSING_API void ArrayCumulativeSum(TArrayView<const float> InView, TArray<float>& OutData);
/** Mean of array. Equivalent to Sum(InView) / InView.Num()
*
* InView contains data to be analyzed.
* OutMean contains the result.
*/
SIGNALPROCESSING_API void ArrayMean(TArrayView<const float> InView, float& OutMean);
/** Mean Squared of array. Equivalent to Sum(InView * InView) / InView.Num()
*
* InArray contains data to be analyzed.
* OutMean contains the result.
*/
SIGNALPROCESSING_API void ArrayMeanSquared(TArrayView<const float> InView, float& OutMean);
/** Takes an audio buffer and returns the magnitude across that buffer. */
SIGNALPROCESSING_API float ArrayGetMagnitude(TArrayView<const float> Buffer);
/** Takes an audio buffer and gets the average amplitude across that buffer. */
SIGNALPROCESSING_API float ArrayGetAverageValue(TArrayView<const float> Buffer);
/** Takes an audio buffer and gets the average absolute amplitude across that buffer. */
SIGNALPROCESSING_API float ArrayGetAverageAbsValue(TArrayView<const float> Buffer);
/** Mean filter of array.
*
* Note: Uses standard biased mean estimator of Sum(x) / Count(x).
* Note: At array boundaries, this algorithm truncates windows where no valid array data exists. Values calculated with truncated windows have corresponding increased variances.
*
* InView contains data to be filtered.
* WindowSize determines the number of samples from InView analyzed to produce a value in OutData.
* WindowOrigin describes the offset from the windows first sample to the index of OutData. For example, if WindowOrigin = WindowSize/4, then OutData[i] = Mean(InView[i - Window/4 : i + 3 * Window / 4]).
* OutData contains the produceds data.
*/
SIGNALPROCESSING_API void ArrayMeanFilter(TArrayView<const float> InView, int32 WindowSize, int32 WindowOrigin, TArray<float>& OutData);
/** Max filter of array.
*
* Note: At array boundaries, this algorithm truncates windows where no valid array data exists.
*
* InView contains data to be filtered.
* WindowSize determines the number of samples from InView analyzed to produce a value in OutData.
* WindowOrigin describes the offset from the windows first sample to the index of OutData. For example, if WindowOrigin = WindowSize/4, then OutData[i] = Max(InView[i - Window/4 : i + 3 * Window / 4]).
* OutData contains the produceds data.
*/
SIGNALPROCESSING_API void ArrayMaxFilter(TArrayView<const float> InView, int32 WindowSize, int32 WindowOrigin, TArray<float>& OutData);
/** Computes the EuclideanNorm of the InView. Same as calculating the energy in window. */
SIGNALPROCESSING_API void ArrayGetEuclideanNorm(TArrayView<const float> InView, float& OutEuclideanNorm);
/** Absolute value of array elements */
SIGNALPROCESSING_API void ArrayAbs(TArrayView<const float> InBuffer, TArrayView<float> OutBuffer);
/** Absolute value of array elements in place.
*
* InBuffer contains the data to be manipulated.
*/
SIGNALPROCESSING_API void ArrayAbsInPlace(TArrayView<float> InBuffer);
/** Clamp minimum value of array in place.
*
* InView contains data to be clamped.
* InMin contains the minimum value allowable in InView.
*/
SIGNALPROCESSING_API void ArrayClampMinInPlace(TArrayView<float> InView, float InMin);
/** Clamp maximum value of array in place.
*
* InView contains data to be clamped.
* InMax contains the maximum value allowable in InView.
*/
SIGNALPROCESSING_API void ArrayClampMaxInPlace(TArrayView<float> InView, float InMax);
/** Clamp values in an array.
*
* InView is a view of a float array to be clamped.
* InMin is the minimum value allowable in the array.
* InMax is the maximum value allowable in the array.
*/
SIGNALPROCESSING_API void ArrayClampInPlace(TArrayView<float> InView, float InMin, float InMax);
/** Scale an array so the minimum is 0 and the maximum is 1
*
* InView is the view of a float array with the input data.
* OutArray is an array which will hold the normalized data.
*/
SIGNALPROCESSING_API void ArrayMinMaxNormalize(TArrayView<const float> InView, TArray<float>& OutArray);
/** Element-wise Max
*/
SIGNALPROCESSING_API void ArrayMax(const TArrayView<const float>& InView1, const TArrayView<const float>& InView2, const TArrayView<float>& OutView);
/** Returns the largest value of an array irrespective of sign (ex. {-3, 2, 1} would return 3).
* InView is a view of a float array to get the largest absolute value from.
*/
SIGNALPROCESSING_API float ArrayMaxAbsValue(const TArrayView<const float> InView);
/** Multiply the second buffer by the first buffer. */
SIGNALPROCESSING_API void ArrayMultiply(TArrayView<const float> InFloatBufferA, TArrayView<const float> InFloatBufferB, TArrayView<float> OutBuffer);
/** Multiply the second buffer in place by the first buffer. */
SIGNALPROCESSING_API void ArrayMultiplyInPlace(TArrayView<const float> InFloatBuffer, TArrayView<float> BufferToMultiply);
/** Multiplies two complex valued arrays element-wise.
* This assumes elements are in interleaved format [real_0, imag_0, ..., real_N, imag_N]
* Stores result in InValues2
*/
SIGNALPROCESSING_API void ArrayComplexMultiplyInPlace(TArrayView<const float> InValues1, TArrayView<float> InValues2);
/** Multiplies two complex valued arrays element-wise.
* This assumes elements are in interleaved format [real_0, imag_0, ..., real_N, imag_N]
* Adds result to OutArray
*/
SIGNALPROCESSING_API void ArrayComplexMultiplyAdd(TArrayView<const float> InValues1, TArrayView<const float> InValues2, TArrayView<float> OutArray);
/** Multiplies the input float buffer with the given value. */
SIGNALPROCESSING_API void ArrayMultiplyByConstant(TArrayView<const float> InFloatBuffer, float InValue, TArrayView<float> OutFloatBuffer);
/** Similar to ArrayMultiplyByConstant, but performs the multiply in place. */
SIGNALPROCESSING_API void ArrayMultiplyByConstantInPlace(TArrayView<float> InOutBuffer, float InGain);
/** Add arrays element-wise in place. InAccumulateValues[i] += InValues[i]
*
* InValues is the array to add.
* InAccumulateValues is the array which holds the sum.
*/
SIGNALPROCESSING_API void ArrayAddInPlace(TArrayView<const float> InValues, TArrayView<float> InAccumulateValues);
/** Adds a constant to a buffer (useful for DC offset removal) */
SIGNALPROCESSING_API void ArrayAddConstantInplace(TArrayView<float> InOutBuffer, float InConstant);
/** Multiply Add arrays element-wise in place. InAccumulateValues[i] += InMultiplier * InValues[i]
*
* @param InValues - The array to add.
* @param InMultiplier - The value to multiply against InValues
* @param InAccumulateValues - The array which holds the sum.
*/
SIGNALPROCESSING_API void ArrayMultiplyAddInPlace(TArrayView<const float> InValues, float InMultiplier, TArrayView<float> InAccumulateValues);
/** Linearly Interpolate Add arrays element-wise in place. InAccumulateValues[i] += ((1 - alpha) * InStartMultiplier + alpha * InEndMultipler) * InValues[i]
* Interpolation is performed over the length of the array.
*
* @param InValues - The array to add.
* @param InStartMultiplier - The beginning value to multiply against InValues
* @param InEndMultiplier - The ending value to multiply against InValues
* @param InAccumulateValues - The array which holds the sum.
*/
SIGNALPROCESSING_API void ArrayLerpAddInPlace(TArrayView<const float> InValues, float InStartMultiplier, float InEndMultiplier, TArrayView<float> InAccumulateValues);
/** Subract arrays element-wise. OutArray = InMinuend - InSubtrahend
*
* InMinuend is the array of data to be subtracted from.
* InSubtrahend is the array of data to subtract.
* OutBuffer is the array which holds the result.
*/
SIGNALPROCESSING_API void ArraySubtract(TArrayView<const float> InMinuend, TArrayView<const float> InSubtrahend, TArrayView<float> OutBuffer);
/* Performs element-wise in-place subtraction placing the result in the subtrahend. InOutSubtrahend = InMinuend - InOutSubtrahend */
SIGNALPROCESSING_API void ArraySubtractInPlace1(TArrayView<const float> InMinuend, TArrayView<float> InOutSubtrahend);
/* Performs element-wise in-place subtraction placing the result in the minuend. InOutMinuend = InOutMinuend - InSubtrahend */
SIGNALPROCESSING_API void ArraySubtractInPlace2(TArrayView<float> InOutMinuend, TArrayView<const float> InSubtrahend);
/** Subtract value from each element in InValues */
SIGNALPROCESSING_API void ArraySubtractByConstantInPlace(TArrayView<float> InValues, float InSubtrahend);
/* Square values */
SIGNALPROCESSING_API void ArraySquare(TArrayView<const float> InValues, TArrayView<float> OutValues);
/** Square values in place. */
SIGNALPROCESSING_API void ArraySquareInPlace(TArrayView<float> InValues);
/** Take Square Root of values in place. */
SIGNALPROCESSING_API void ArraySqrtInPlace(TArrayView<float> InValues);
/** Perform complex conjugate of array. Assumes complex numbers are interlaves [real_0, imag_0, real_1, image_1, ..., real_N, imag_N]. */
SIGNALPROCESSING_API void ArrayComplexConjugate(TArrayView<const float> InValues, TArrayView<float> OutValues);
SIGNALPROCESSING_API void ArrayComplexConjugateInPlace(TArrayView<float> InValues);
/** Convert magnitude values to decibel values in place. db = 20 * log10(val) */
SIGNALPROCESSING_API void ArrayMagnitudeToDecibelInPlace(TArrayView<float> InValues, float InMinimumDb);
/** Convert power values to decibel values in place. db = 10 * log10(val) */
SIGNALPROCESSING_API void ArrayPowerToDecibelInPlace(TArrayView<float> InValues, float InMinimumDb);
/** Compute power of complex data. Out[i] = Complex[2 * i] * Complex[2 * i] + Complex[2 * i + 1] * Complex[2 * i + 1] */
SIGNALPROCESSING_API void ArrayComplexToPower(TArrayView<const float> InComplexSamples, TArrayView<float> OutPowerSamples);
/** Compute power of complex data. Out[i] = Real[i] * Real[i] + Imaginary[i] * Imaginary[i] */
SIGNALPROCESSING_API void ArrayComplexToPower(TArrayView<const float> InRealSamples, TArrayView<const float> InImaginarySamples, TArrayView<float> OutPowerSamples);
/* Sets a values to zero if value is denormal. Denormal numbers significantly slow down floating point operations. */
UE_DEPRECATED(5.5, "Audio code relies on denormals flush to zero floating point mode from now on. Please use FScopedFTZFloatMode instead of this API")
SIGNALPROCESSING_API void ArrayUnderflowClamp(TArrayView<float> InOutBuffer);
/* Clamps the values in a buffer between a min and max value.*/
SIGNALPROCESSING_API void ArrayRangeClamp(TArrayView<float> InOutBuffer, float InMinValue, float InMaxValue);
/** Sets a constant to a buffer (useful for DC offset application) */
SIGNALPROCESSING_API void ArraySetToConstantInplace(TArrayView<float> InOutBuffer, float InConstant);
/* Performs an element-wise weighted sum OutputBuffer = (InBuffer1 x InGain1) + (InBuffer2 x InGain2) */
SIGNALPROCESSING_API void ArrayWeightedSum(TArrayView<const float> InBuffer1, float InGain1, TArrayView<const float> InBuffer2, float InGain2, TArrayView<float> OutBuffer);
/* Performs an element-wise weighted sum OutputBuffer = (InBuffer1 x InGain1) + InBuffer2 */
SIGNALPROCESSING_API void ArrayWeightedSum(TArrayView<const float> InBuffer1, float InGain1, TArrayView<const float> InBuffer2, TArrayView<float> OutBuffer);
/* Takes a float buffer and quickly interpolates it's gain from StartValue to EndValue. */
/* This operation completely ignores channel counts, so avoid using this function on buffers that are not mono, stereo or quad */
/* if the buffer needs to fade all channels uniformly. */
SIGNALPROCESSING_API void ArrayFade(TArrayView<float> InOutBuffer, const float StartValue, const float EndValue);
SIGNALPROCESSING_API void ArrayFade(TArrayView<const float> InBuffer, const float InStartValue, const float InEndValue, TArrayView<float> OutBuffer);
/** Takes buffer InFloatBuffer, optionally multiplies it by Gain, and adds it to BufferToSumTo. */
SIGNALPROCESSING_API void ArrayMixIn(TArrayView<const float> InFloatBuffer, TArrayView<float> BufferToSumTo, const float Gain);
SIGNALPROCESSING_API void ArrayMixIn(TArrayView<const float> InFloatBuffer, TArrayView<float> BufferToSumTo);
/** Takes buffer InPcm16Buffer, converts it to float and optionally multiplies it by Gain, and adds it to BufferToSumTo. */
SIGNALPROCESSING_API void ArrayMixIn(TArrayView<const int16> InPcm16Buffer, TArrayView<float> BufferToSumTo, const float Gain = 1.0f);
/** This version of ArrayMixIn will fade from StartGain to EndGain. */
SIGNALPROCESSING_API void ArrayMixIn(TArrayView<const float> InFloatBuffer, TArrayView<float> BufferToSumTo, const float StartGain, const float EndGain);
SIGNALPROCESSING_API void ArrayFloatToPcm16(TArrayView<const float> InView, TArrayView<int16> OutView);
SIGNALPROCESSING_API void ArrayPcm16ToFloat(TArrayView<const int16> InView, TArrayView<float> OutView);
/** Converts float PCM data to 24 bit integer PCM data */
SIGNALPROCESSING_API void ArrayFloatToPcm24(TArrayView<const float> InView, TArrayView<int8> OutView);
/** Copies 24 bits of a 32 bit integer (assumes data has been clamped to 24-bits) */
SIGNALPROCESSING_API void AssignPcm24Value(int8* OutPtr, const int32 InValue, const bool bIsOutputLittleEndian);
/** Converts float PCM data to 32 bit integer PCM data */
SIGNALPROCESSING_API void ArrayFloatToPcm32(TArrayView<const float> InView, TArrayView<int32> OutView);
/** Converts float PCM data to double PCM data */
SIGNALPROCESSING_API void ArrayFloatToPcmDouble(TArrayView<const float> InView, TArrayView<double> OutView);
/** Interleaves samples from an array of input buffers */
SIGNALPROCESSING_API void ArrayInterleave(const TArray<FAlignedFloatBuffer>& InBuffers, FAlignedFloatBuffer& OutBuffer);
/** Interleaves samples from an array of input buffers */
SIGNALPROCESSING_API void ArrayInterleave(const float* const* RESTRICT InBuffers, float* RESTRICT OutBuffer, const int32 InFrames, const int32 InChannels);
/** Interleaves samples from an array of input buffers */
SIGNALPROCESSING_API void ArrayDeinterleave(const FAlignedFloatBuffer& InBuffer, TArray<FAlignedFloatBuffer>& OutBuffers, const int32 InChannels);
/** Interleaves samples from an array of input buffers */
SIGNALPROCESSING_API void ArrayDeinterleave(const TArrayView<const float> InView, TArray<FAlignedFloatBuffer>& OutBuffers, const int32 InChannels);
/** Interleaves samples from an array of input buffers */
SIGNALPROCESSING_API void ArrayDeinterleave(const float* RESTRICT InBuffer, float* const* RESTRICT OutBuffers, const int32 InFrames, const int32 InChannels);
/** Interpolates a Mono audio buffer. */
SIGNALPROCESSING_API void ArrayInterpolate(const float* RESTRICT InBuffer, float* RESTRICT OutBuffer, const int32 NumInSamples, const int32 NumOutSamples);
/** Vectorized 16-bit integer byte swapping. */
SIGNALPROCESSING_API void ArrayInt16SwapBytes(TArrayView<int16> InView);
/** Vectorized 24-bit integer byte swapping. */
SIGNALPROCESSING_API void ArrayInt24SwapBytes(TArrayView<int8> InView);
/** Vectorized 32-bit integer byte swapping. */
SIGNALPROCESSING_API void ArrayInt32SwapBytes(TArrayView<int32> InView);
/** Vectorized 32-bit float byte swapping. */
SIGNALPROCESSING_API void ArrayFloatSwapBytes(TArrayView<float> InView);
/** Vectorized 64-bit float byte swapping. */
SIGNALPROCESSING_API void ArrayDoubleSwapBytes(TArrayView<double> InView);
/** Returns true if host has little endian byte ordering */
UE_DEPRECATED(5.5, "Big Endian platforms are no longer supported.")
SIGNALPROCESSING_API constexpr bool IsHostLittleEndian()
{
#if PLATFORM_LITTLE_ENDIAN
return true;
#else
return false;
#endif // PLATFORM_LITTLE_ENDIAN
}
/** All Pass Filter with a long delay. */
SIGNALPROCESSING_API void ArrayAPFLongDelayProcess(const float* InSamples, const float* InDelaySamples, const int32 InNum, float* OutSamples, float* OutDelaySamples, const float Gain);
/** Fractional delay using linear interpolation. */
SIGNALPROCESSING_API void ArrayLerpFractionalDelay(const float* InSamples, const float* InDelays, const float* DelayData, const int* IntegerDelays, int* UpperDelayPos, int* LowerDelayPos, const int32 InNum, float* OutSamples, const float MaxDelay);
/** Perform complex conjugate as well as scale. */
SIGNALPROCESSING_API void ArrayScaledComplexConjugate(const float* RESTRICT InValues, const int32 Num, float* RESTRICT OutValues, const float Scale);
/** FContiguousSparse2DKernelTransform
*
* FContiguousSparse2DKernelTransform applies a matrix transformation to an input array.
* [OutArray] = [[Kernal]][InView]
*
* It provides some optimization by exploit the contiguous and sparse qualities of the kernel rows,
* which allows it to skip multiplications with the number zero.
*
* It works with non-sparse and non-contiguous kernels as well, but will be more computationally
* expensive than a naive implementation. Also, only takes advantage of sparse contiguous rows, not columns.
*/
class FContiguousSparse2DKernelTransform
{
struct FRow
{
int32 StartIndex = 0;
TArray<float> OffsetValues;
};
public:
FContiguousSparse2DKernelTransform(const FContiguousSparse2DKernelTransform& ) = delete;
FContiguousSparse2DKernelTransform(const FContiguousSparse2DKernelTransform&& ) = delete;
FContiguousSparse2DKernelTransform& operator=(const FContiguousSparse2DKernelTransform& ) = delete;
/**
* NumInElements sets the expected number of input array elements as well as the number of elements in a row.
* NumOutElements sets the number of output array elements as well as the number or rows.
*/
SIGNALPROCESSING_API FContiguousSparse2DKernelTransform(const int32 NumInElements, const int32 NumOutElements);
SIGNALPROCESSING_API virtual ~FContiguousSparse2DKernelTransform();
/** Returns the required size of the input array */
SIGNALPROCESSING_API int32 GetNumInElements() const;
/** Returns the size of the output array */
SIGNALPROCESSING_API int32 GetNumOutElements() const;
/** Set the kernel values for an individual row.
*
* RowIndex determines which row is being set.
* StartIndex denotes the offset into the row where the OffsetValues will be inserted.
* OffsetValues contains the contiguous chunk of values which represent all the nonzero elements in the row.
*/
SIGNALPROCESSING_API void SetRow(const int32 RowIndex, const int32 StartIndex, TArrayView<const float> OffsetValues);
/** Transforms the input array given the kernel.
*
* InView is the array to be transformed. It must have `NumInElements` number of elements.
* OutArray is the transformed array. It will have `NumOutElements` number of elements.
*/
SIGNALPROCESSING_API void TransformArray(TArrayView<const float> InView, TArray<float>& OutArray) const;
/** Transforms the input array given the kernel.
*
* InView is the array to be transformed. It must have `NumInElements` number of elements.
* OutArray is the transformed array. It will have `NumOutElements` number of elements.
*/
SIGNALPROCESSING_API void TransformArray(TArrayView<const float> InView, FAlignedFloatBuffer& OutArray) const;
/** Transforms the input array given the kernel.
*
* InArray is the array to be transformed. It must have `NumInElements` number of elements.
* OutArray is the transformed array. It must be allocated to hold at least NumOutElements.
*/
SIGNALPROCESSING_API void TransformArray(const float* InArray, float* OutArray) const;
private:
int32 NumIn;
int32 NumOut;
TArray<FRow> Kernel;
};
}