FFTBlock.h (7102B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef FFTBlock_h_ 8 #define FFTBlock_h_ 9 10 #include "AlignedTArray.h" 11 #include "AudioNodeEngine.h" 12 #include "FFVPXRuntimeLinker.h" 13 #include "ffvpx/tx.h" 14 15 namespace mozilla { 16 17 // This class defines an FFT block, loosely modeled after Blink's FFTFrame 18 // class to make sharing code with Blink easy. 19 class FFTBlock final { 20 union ComplexU { 21 float f[2]; 22 struct { 23 float r; 24 float i; 25 }; 26 }; 27 28 public: 29 static void MainThreadInit() { 30 FFVPXRuntimeLinker::Init(); 31 if (!sFFTFuncs.init) { 32 FFVPXRuntimeLinker::GetFFTFuncs(&sFFTFuncs); 33 } 34 } 35 explicit FFTBlock(uint32_t aFFTSize, float aInverseScaling = 1.0f) 36 : mInverseScaling(aInverseScaling) { 37 MOZ_COUNT_CTOR(FFTBlock); 38 SetFFTSize(aFFTSize); 39 } 40 ~FFTBlock() { 41 MOZ_COUNT_DTOR(FFTBlock); 42 Clear(); 43 } 44 45 // Return a new FFTBlock with frequency components interpolated between 46 // |block0| and |block1| with |interp| between 0.0 and 1.0. 47 static FFTBlock* CreateInterpolatedBlock(const FFTBlock& block0, 48 const FFTBlock& block1, 49 double interp); 50 51 // Transform FFTSize() points of aData and store the result internally. 52 void PerformFFT(const float* aData) { 53 if (!EnsureFFT()) { 54 return; 55 } 56 57 mFn(mTxCtx, mOutputBuffer.Elements()->f, const_cast<float*>(aData), 58 2 * sizeof(float)); 59 #ifdef DEBUG 60 mInversePerformed = false; 61 #endif 62 } 63 // Inverse-transform internal frequency data and store the resulting 64 // FFTSize() points in |aDataOut|. If frequency data has not already been 65 // scaled, then the output will need scaling by 1/FFTSize(). 66 void GetInverse(float* aDataOut) { 67 if (!EnsureIFFT()) { 68 std::fill_n(aDataOut, mFFTSize, 0.0f); 69 return; 70 }; 71 // When performing an inverse transform, tx overwrites the input. This 72 // asserts that forward / inverse transforms are interleaved to avoid having 73 // to keep the input around. 74 MOZ_ASSERT(!mInversePerformed); 75 mIFn(mITxCtx, aDataOut, mOutputBuffer.Elements()->f, 2 * sizeof(float)); 76 #ifdef DEBUG 77 mInversePerformed = true; 78 #endif 79 } 80 81 void Multiply(const FFTBlock& aFrame) { 82 MOZ_ASSERT(!mInversePerformed); 83 84 uint32_t halfSize = mFFTSize / 2; 85 // DFTs are not packed. 86 MOZ_ASSERT(mOutputBuffer[0].i == 0); 87 MOZ_ASSERT(aFrame.mOutputBuffer[0].i == 0); 88 89 BufferComplexMultiply(mOutputBuffer.Elements()->f, 90 aFrame.mOutputBuffer.Elements()->f, 91 mOutputBuffer.Elements()->f, halfSize); 92 mOutputBuffer[halfSize].r *= aFrame.mOutputBuffer[halfSize].r; 93 // This would have been set to NaN if either real component was NaN. 94 mOutputBuffer[0].i = 0.0f; 95 } 96 97 // Perform a forward FFT on |aData|, assuming zeros after dataSize samples, 98 // and pre-scale the generated internal frequency domain coefficients so 99 // that GetInverseWithoutScaling() can be used to transform to the time 100 // domain. This is useful for convolution kernels. 101 void PadAndMakeScaledDFT(const float* aData, size_t dataSize) { 102 MOZ_ASSERT(dataSize <= FFTSize()); 103 AlignedTArray<float> paddedData; 104 paddedData.SetLength(FFTSize()); 105 AudioBufferCopyWithScale(aData, 1.0f / AssertedCast<float>(FFTSize()), 106 paddedData.Elements(), dataSize); 107 PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize); 108 PerformFFT(paddedData.Elements()); 109 } 110 111 // aSize must be a power of 2 112 void SetFFTSize(uint32_t aSize) { 113 MOZ_ASSERT(CountPopulation32(aSize) == 1); 114 mFFTSize = aSize; 115 mOutputBuffer.SetLength(aSize / 2 + 1); 116 PodZero(mOutputBuffer.Elements(), aSize / 2 + 1); 117 Clear(); 118 } 119 120 // Return the average group delay and removes this from the frequency data. 121 double ExtractAverageGroupDelay(); 122 123 uint32_t FFTSize() const { return mFFTSize; } 124 float RealData(uint32_t aIndex) const { 125 MOZ_ASSERT(!mInversePerformed); 126 return mOutputBuffer[aIndex].r; 127 } 128 float& RealData(uint32_t aIndex) { 129 MOZ_ASSERT(!mInversePerformed); 130 return mOutputBuffer[aIndex].r; 131 } 132 float ImagData(uint32_t aIndex) const { 133 MOZ_ASSERT(!mInversePerformed); 134 return mOutputBuffer[aIndex].i; 135 } 136 float& ImagData(uint32_t aIndex) { 137 MOZ_ASSERT(!mInversePerformed); 138 return mOutputBuffer[aIndex].i; 139 } 140 141 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 142 size_t amount = 0; 143 144 // malloc_usable_size can't be used here because the pointer isn't 145 // necessarily from malloc. This value has been manually checked. 146 if (mTxCtx) { 147 amount += 711; 148 } 149 if (mTxCtx) { 150 amount += 711; 151 } 152 153 amount += mOutputBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf); 154 return amount; 155 } 156 157 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 158 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 159 } 160 161 FFTBlock(const FFTBlock& other) = delete; 162 void operator=(const FFTBlock& other) = delete; 163 164 private: 165 bool EnsureFFT() { 166 if (!mTxCtx) { 167 if (!sFFTFuncs.init) { 168 return false; 169 } 170 // Forward transform is always unscaled for our purpose. 171 float scale = 1.0f; 172 int rv = sFFTFuncs.init(&mTxCtx, &mFn, AV_TX_FLOAT_RDFT, 0 /* forward */, 173 AssertedCast<int>(mFFTSize), &scale, 0); 174 MOZ_ASSERT(!rv, "av_tx_init: invalid parameters (forward)"); 175 return !rv; 176 } 177 return true; 178 } 179 bool EnsureIFFT() { 180 if (!mITxCtx) { 181 if (!sFFTFuncs.init) { 182 return false; 183 } 184 int rv = 185 sFFTFuncs.init(&mITxCtx, &mIFn, AV_TX_FLOAT_RDFT, 1 /* inverse */, 186 AssertedCast<int>(mFFTSize), &mInverseScaling, 0); 187 MOZ_ASSERT(!rv, "av_tx_init: invalid parameters (inverse)"); 188 return !rv; 189 } 190 return true; 191 } 192 void Clear() { 193 if (mTxCtx) { 194 sFFTFuncs.uninit(&mTxCtx); 195 mTxCtx = nullptr; 196 mFn = nullptr; 197 } 198 if (mITxCtx) { 199 sFFTFuncs.uninit(&mITxCtx); 200 mITxCtx = nullptr; 201 mIFn = nullptr; 202 } 203 } 204 void AddConstantGroupDelay(double sampleFrameDelay); 205 void InterpolateFrequencyComponents(const FFTBlock& block0, 206 const FFTBlock& block1, double interp); 207 static FFmpegFFTFuncs sFFTFuncs; 208 // Context and function pointer for forward transform 209 AVTXContext* mTxCtx{}; 210 av_tx_fn mFn{}; 211 // Context and function pointer for inverse transform 212 AVTXContext* mITxCtx{}; 213 av_tx_fn mIFn{}; 214 AlignedTArray<ComplexU> mOutputBuffer; 215 uint32_t mFFTSize{}; 216 // A scaling that is performed when doing an inverse transform. The forward 217 // transform is always unscaled. 218 float mInverseScaling; 219 #ifdef DEBUG 220 bool mInversePerformed = false; 221 #endif 222 }; 223 224 } // namespace mozilla 225 226 #endif