summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/FFTBlock.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/FFTBlock.h')
-rw-r--r--dom/media/webaudio/FFTBlock.h290
1 files changed, 84 insertions, 206 deletions
diff --git a/dom/media/webaudio/FFTBlock.h b/dom/media/webaudio/FFTBlock.h
index 6af882f9af..ecce39d7b1 100644
--- a/dom/media/webaudio/FFTBlock.h
+++ b/dom/media/webaudio/FFTBlock.h
@@ -7,31 +7,17 @@
#ifndef FFTBlock_h_
#define FFTBlock_h_
-#ifdef BUILD_ARM_NEON
-# include <cmath>
-# include "mozilla/arm.h"
-# include "dl/sp/api/omxSP.h"
-#endif
-
#include "AlignedTArray.h"
#include "AudioNodeEngine.h"
-#if defined(MOZ_LIBAV_FFT)
-# include "FFmpegRDFTTypes.h"
-# include "FFVPXRuntimeLinker.h"
-#else
-# include "kiss_fft/kiss_fftr.h"
-#endif
+#include "FFVPXRuntimeLinker.h"
+#include "ffvpx/tx.h"
namespace mozilla {
// This class defines an FFT block, loosely modeled after Blink's FFTFrame
// class to make sharing code with Blink easy.
-// Currently it's implemented on top of KissFFT on all platforms.
class FFTBlock final {
union ComplexU {
-#if !defined(MOZ_LIBAV_FFT)
- kiss_fft_cpx c;
-#endif
float f[2];
struct {
float r;
@@ -41,28 +27,13 @@ class FFTBlock final {
public:
static void MainThreadInit() {
-#ifdef MOZ_LIBAV_FFT
FFVPXRuntimeLinker::Init();
- if (!sRDFTFuncs.init) {
- FFVPXRuntimeLinker::GetRDFTFuncs(&sRDFTFuncs);
+ if (!sFFTFuncs.init) {
+ FFVPXRuntimeLinker::GetFFTFuncs(&sFFTFuncs);
}
-#endif
}
-
- explicit FFTBlock(uint32_t aFFTSize)
-#if defined(MOZ_LIBAV_FFT)
- : mAvRDFT(nullptr),
- mAvIRDFT(nullptr)
-#else
- : mKissFFT(nullptr),
- mKissIFFT(nullptr)
-# ifdef BUILD_ARM_NEON
- ,
- mOmxFFT(nullptr),
- mOmxIFFT(nullptr)
-# endif
-#endif
- {
+ explicit FFTBlock(uint32_t aFFTSize, float aInverseScaling = 1.0f)
+ : mInverseScaling(aInverseScaling) {
MOZ_COUNT_CTOR(FFTBlock);
SetFFTSize(aFFTSize);
}
@@ -83,63 +54,33 @@ class FFTBlock final {
return;
}
-#if defined(MOZ_LIBAV_FFT)
- PodCopy(mOutputBuffer.Elements()->f, aData, mFFTSize);
- sRDFTFuncs.calc(mAvRDFT, mOutputBuffer.Elements()->f);
- // Recover packed Nyquist.
- mOutputBuffer[mFFTSize / 2].r = mOutputBuffer[0].i;
- mOutputBuffer[0].i = 0.0f;
-#else
-# ifdef BUILD_ARM_NEON
- if (mozilla::supports_neon()) {
- omxSP_FFTFwd_RToCCS_F32_Sfs(aData, mOutputBuffer.Elements()->f, mOmxFFT);
- } else
-# endif
- {
- kiss_fftr(mKissFFT, aData, &(mOutputBuffer.Elements()->c));
- }
+ mFn(mTxCtx, mOutputBuffer.Elements()->f, const_cast<float*>(aData),
+ 2 * sizeof(float));
+#ifdef DEBUG
+ mInversePerformed = false;
#endif
}
- // Inverse-transform internal data and store the resulting FFTSize()
- // points in aDataOut.
- void GetInverse(float* aDataOut) {
- GetInverseWithoutScaling(aDataOut);
- AudioBufferInPlaceScale(aDataOut, 1.0f / mFFTSize, mFFTSize);
- }
-
// Inverse-transform internal frequency data and store the resulting
// FFTSize() points in |aDataOut|. If frequency data has not already been
// scaled, then the output will need scaling by 1/FFTSize().
- void GetInverseWithoutScaling(float* aDataOut) {
+ void GetInverse(float* aDataOut) {
if (!EnsureIFFT()) {
std::fill_n(aDataOut, mFFTSize, 0.0f);
return;
};
-
-#if defined(MOZ_LIBAV_FFT)
- {
- // Even though this function doesn't scale, the libav forward transform
- // gives a value that needs scaling by 2 in order for things to turn out
- // similar to how we expect from kissfft/openmax.
- AudioBufferCopyWithScale(mOutputBuffer.Elements()->f, 2.0f, aDataOut,
- mFFTSize);
- aDataOut[1] = 2.0f * mOutputBuffer[mFFTSize / 2].r; // Packed Nyquist
- sRDFTFuncs.calc(mAvIRDFT, aDataOut);
- }
-#else
-# ifdef BUILD_ARM_NEON
- if (mozilla::supports_neon()) {
- omxSP_FFTInv_CCSToR_F32_Sfs_unscaled(mOutputBuffer.Elements()->f,
- aDataOut, mOmxIFFT);
- } else
-# endif
- {
- kiss_fftri(mKissIFFT, &(mOutputBuffer.Elements()->c), aDataOut);
- }
+ // When performing an inverse transform, tx overwrites the input. This
+ // asserts that forward / inverse transforms are interleaved to avoid having
+ // to keep the input around.
+ MOZ_ASSERT(!mInversePerformed);
+ mIFn(mITxCtx, aDataOut, mOutputBuffer.Elements()->f, 2 * sizeof(float));
+#ifdef DEBUG
+ mInversePerformed = true;
#endif
}
void Multiply(const FFTBlock& aFrame) {
+ MOZ_ASSERT(!mInversePerformed);
+
uint32_t halfSize = mFFTSize / 2;
// DFTs are not packed.
MOZ_ASSERT(mOutputBuffer[0].i == 0);
@@ -161,8 +102,8 @@ class FFTBlock final {
MOZ_ASSERT(dataSize <= FFTSize());
AlignedTArray<float> paddedData;
paddedData.SetLength(FFTSize());
- AudioBufferCopyWithScale(aData, 1.0f / FFTSize(), paddedData.Elements(),
- dataSize);
+ AudioBufferCopyWithScale(aData, 1.0f / AssertedCast<float>(FFTSize()),
+ paddedData.Elements(), dataSize);
PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize);
PerformFFT(paddedData.Elements());
}
@@ -180,46 +121,35 @@ class FFTBlock final {
double ExtractAverageGroupDelay();
uint32_t FFTSize() const { return mFFTSize; }
- float RealData(uint32_t aIndex) const { return mOutputBuffer[aIndex].r; }
- float& RealData(uint32_t aIndex) { return mOutputBuffer[aIndex].r; }
- float ImagData(uint32_t aIndex) const { return mOutputBuffer[aIndex].i; }
- float& ImagData(uint32_t aIndex) { return mOutputBuffer[aIndex].i; }
+ float RealData(uint32_t aIndex) const {
+ MOZ_ASSERT(!mInversePerformed);
+ return mOutputBuffer[aIndex].r;
+ }
+ float& RealData(uint32_t aIndex) {
+ MOZ_ASSERT(!mInversePerformed);
+ return mOutputBuffer[aIndex].r;
+ }
+ float ImagData(uint32_t aIndex) const {
+ MOZ_ASSERT(!mInversePerformed);
+ return mOutputBuffer[aIndex].i;
+ }
+ float& ImagData(uint32_t aIndex) {
+ MOZ_ASSERT(!mInversePerformed);
+ return mOutputBuffer[aIndex].i;
+ }
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
size_t amount = 0;
-#if defined(MOZ_LIBAV_FFT)
- auto ComputedSizeOfContextIfSet = [this](void* aContext) -> size_t {
- if (!aContext) {
- return 0;
- }
- // RDFTContext is only forward declared in public headers, but this is
- // an estimate based on a value of 231 seen requested from
- // _aligned_alloc on Win64. Don't use malloc_usable_size() because the
- // context pointer is not necessarily from malloc.
- size_t amount = 232;
- // Add size of allocations performed in ff_fft_init().
- // The maximum FFT size used is 32768 = 2^15 and so revtab32 is not
- // allocated.
- MOZ_ASSERT(mFFTSize <= 32768);
- amount += mFFTSize * (sizeof(uint16_t) + 2 * sizeof(float));
-
- return amount;
- };
+ // malloc_usable_size can't be used here because the pointer isn't
+ // necessarily from malloc. This value has been manually checked.
+ if (mTxCtx) {
+ amount += 711;
+ }
+ if (mTxCtx) {
+ amount += 711;
+ }
- amount += ComputedSizeOfContextIfSet(mAvRDFT);
- amount += ComputedSizeOfContextIfSet(mAvIRDFT);
-#else
-# ifdef BUILD_ARM_NEON
- amount += aMallocSizeOf(mOmxFFT);
- amount += aMallocSizeOf(mOmxIFFT);
-# endif
-# ifdef USE_SIMD
-# error kiss fft uses malloc only when USE_SIMD is not defined
-# endif
- amount += aMallocSizeOf(mKissFFT);
- amount += aMallocSizeOf(mKissIFFT);
-#endif
amount += mOutputBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
return amount;
}
@@ -228,119 +158,67 @@ class FFTBlock final {
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
- private:
FFTBlock(const FFTBlock& other) = delete;
void operator=(const FFTBlock& other) = delete;
+ private:
bool EnsureFFT() {
-#if defined(MOZ_LIBAV_FFT)
- if (!mAvRDFT) {
- if (!sRDFTFuncs.init) {
+ if (!mTxCtx) {
+ if (!sFFTFuncs.init) {
return false;
}
-
- mAvRDFT = sRDFTFuncs.init(FloorLog2(mFFTSize), DFT_R2C);
- }
-#else
-# ifdef BUILD_ARM_NEON
- if (mozilla::supports_neon()) {
- if (!mOmxFFT) {
- mOmxFFT = createOmxFFT(mFFTSize);
- }
- } else
-# endif
- {
- if (!mKissFFT) {
- mKissFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
- }
+ // Forward transform is always unscaled for our purpose.
+ float scale = 1.0f;
+ int rv = sFFTFuncs.init(&mTxCtx, &mFn, AV_TX_FLOAT_RDFT, 0 /* forward */,
+ AssertedCast<int>(mFFTSize), &scale, 0);
+ MOZ_ASSERT(!rv, "av_tx_init: invalid parameters (forward)");
+ return !rv;
}
-#endif
return true;
}
-
bool EnsureIFFT() {
-#if defined(MOZ_LIBAV_FFT)
- if (!mAvIRDFT) {
- if (!sRDFTFuncs.init) {
+ if (!mITxCtx) {
+ if (!sFFTFuncs.init) {
return false;
}
-
- mAvIRDFT = sRDFTFuncs.init(FloorLog2(mFFTSize), IDFT_C2R);
- }
-#else
-# ifdef BUILD_ARM_NEON
- if (mozilla::supports_neon()) {
- if (!mOmxIFFT) {
- mOmxIFFT = createOmxFFT(mFFTSize);
- }
- } else
-# endif
- {
- if (!mKissIFFT) {
- mKissIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
- }
+ int rv =
+ sFFTFuncs.init(&mITxCtx, &mIFn, AV_TX_FLOAT_RDFT, 1 /* inverse */,
+ AssertedCast<int>(mFFTSize), &mInverseScaling, 0);
+ MOZ_ASSERT(!rv, "av_tx_init: invalid parameters (inverse)");
+ return !rv;
}
-#endif
return true;
}
-
-#ifdef BUILD_ARM_NEON
- static OMXFFTSpec_R_F32* createOmxFFT(uint32_t aFFTSize) {
- MOZ_ASSERT((aFFTSize & (aFFTSize - 1)) == 0);
- OMX_INT bufSize;
- OMX_INT order = FloorLog2(aFFTSize);
- MOZ_ASSERT(aFFTSize >> order == 1);
- OMXResult status = omxSP_FFTGetBufSize_R_F32(order, &bufSize);
- if (status == OMX_Sts_NoErr) {
- OMXFFTSpec_R_F32* context =
- static_cast<OMXFFTSpec_R_F32*>(malloc(bufSize));
- if (omxSP_FFTInit_R_F32(context, order) != OMX_Sts_NoErr) {
- return nullptr;
- }
- return context;
- }
- return nullptr;
- }
-#endif
-
void Clear() {
-#if defined(MOZ_LIBAV_FFT)
- if (mAvRDFT) {
- sRDFTFuncs.end(mAvRDFT);
- mAvRDFT = nullptr;
+ if (mTxCtx) {
+ sFFTFuncs.uninit(&mTxCtx);
+ mTxCtx = nullptr;
+ mFn = nullptr;
}
- if (mAvIRDFT) {
- sRDFTFuncs.end(mAvIRDFT);
- mAvIRDFT = nullptr;
+ if (mITxCtx) {
+ sFFTFuncs.uninit(&mITxCtx);
+ mITxCtx = nullptr;
+ mIFn = nullptr;
}
-#else
-# ifdef BUILD_ARM_NEON
- free(mOmxFFT);
- free(mOmxIFFT);
- mOmxFFT = mOmxIFFT = nullptr;
-# endif
- free(mKissFFT);
- free(mKissIFFT);
- mKissFFT = mKissIFFT = nullptr;
-#endif
}
void AddConstantGroupDelay(double sampleFrameDelay);
void InterpolateFrequencyComponents(const FFTBlock& block0,
const FFTBlock& block1, double interp);
-#if defined(MOZ_LIBAV_FFT)
- static FFmpegRDFTFuncs sRDFTFuncs;
- RDFTContext* mAvRDFT;
- RDFTContext* mAvIRDFT;
-#else
- kiss_fftr_cfg mKissFFT;
- kiss_fftr_cfg mKissIFFT;
-# ifdef BUILD_ARM_NEON
- OMXFFTSpec_R_F32* mOmxFFT;
- OMXFFTSpec_R_F32* mOmxIFFT;
-# endif
-#endif
+ static FFmpegFFTFuncs sFFTFuncs;
+ // Context and function pointer for forward transform
+ AVTXContext* mTxCtx{};
+ av_tx_fn mFn{};
+ // Context and function pointer for inverse transform
+ AVTXContext* mITxCtx{};
+ av_tx_fn mIFn{};
AlignedTArray<ComplexU> mOutputBuffer;
- uint32_t mFFTSize;
+ uint32_t mFFTSize{};
+ // A scaling that is performed when doing an inverse transform. The forward
+ // transform is always unscaled.
+ float mInverseScaling;
+#ifdef DEBUG
+ bool mInversePerformed = false;
+#endif
};
} // namespace mozilla