Line data Source code
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 : #ifdef BUILD_ARM_NEON
11 : #include <cmath>
12 : #include "mozilla/arm.h"
13 : #include "dl/sp/api/omxSP.h"
14 : #endif
15 :
16 : #include "AlignedTArray.h"
17 : #include "AudioNodeEngine.h"
18 : #if defined(MOZ_LIBAV_FFT)
19 : #ifdef __cplusplus
20 : extern "C" {
21 : #endif
22 : #include "libavcodec/avfft.h"
23 : #ifdef __cplusplus
24 : }
25 : #endif
26 : #else
27 : #include "kiss_fft/kiss_fftr.h"
28 : #endif
29 :
30 : namespace mozilla {
31 :
32 : // This class defines an FFT block, loosely modeled after Blink's FFTFrame
33 : // class to make sharing code with Blink easy.
34 : // Currently it's implemented on top of KissFFT on all platforms.
35 : class FFTBlock final
36 : {
37 : union ComplexU {
38 : #if !defined(MOZ_LIBAV_FFT)
39 : kiss_fft_cpx c;
40 : #endif
41 : float f[2];
42 : struct {
43 : float r;
44 : float i;
45 : };
46 : };
47 :
48 : public:
49 0 : explicit FFTBlock(uint32_t aFFTSize)
50 : #if defined(MOZ_LIBAV_FFT)
51 0 : : mAvRDFT(nullptr)
52 0 : , mAvIRDFT(nullptr)
53 : #else
54 : : mKissFFT(nullptr)
55 : , mKissIFFT(nullptr)
56 : #ifdef BUILD_ARM_NEON
57 : , mOmxFFT(nullptr)
58 : , mOmxIFFT(nullptr)
59 : #endif
60 : #endif
61 : {
62 0 : MOZ_COUNT_CTOR(FFTBlock);
63 0 : SetFFTSize(aFFTSize);
64 0 : }
65 0 : ~FFTBlock()
66 0 : {
67 0 : MOZ_COUNT_DTOR(FFTBlock);
68 0 : Clear();
69 0 : }
70 :
71 : // Return a new FFTBlock with frequency components interpolated between
72 : // |block0| and |block1| with |interp| between 0.0 and 1.0.
73 : static FFTBlock*
74 : CreateInterpolatedBlock(const FFTBlock& block0,
75 : const FFTBlock& block1, double interp);
76 :
77 : // Transform FFTSize() points of aData and store the result internally.
78 0 : void PerformFFT(const float* aData)
79 : {
80 0 : EnsureFFT();
81 : #if defined(MOZ_LIBAV_FFT)
82 0 : PodCopy(mOutputBuffer.Elements()->f, aData, mFFTSize);
83 0 : av_rdft_calc(mAvRDFT, mOutputBuffer.Elements()->f);
84 : // Recover packed Nyquist.
85 0 : mOutputBuffer[mFFTSize / 2].r = mOutputBuffer[0].i;
86 0 : mOutputBuffer[0].i = 0.0f;
87 : #else
88 : #ifdef BUILD_ARM_NEON
89 : if (mozilla::supports_neon()) {
90 : omxSP_FFTFwd_RToCCS_F32_Sfs(aData, mOutputBuffer.Elements()->f, mOmxFFT);
91 : } else
92 : #endif
93 : {
94 : kiss_fftr(mKissFFT, aData, &(mOutputBuffer.Elements()->c));
95 : }
96 : #endif
97 0 : }
98 : // Inverse-transform internal data and store the resulting FFTSize()
99 : // points in aDataOut.
100 0 : void GetInverse(float* aDataOut)
101 : {
102 0 : GetInverseWithoutScaling(aDataOut);
103 0 : AudioBufferInPlaceScale(aDataOut, 1.0f / mFFTSize, mFFTSize);
104 0 : }
105 : // Inverse-transform internal frequency data and store the resulting
106 : // FFTSize() points in |aDataOut|. If frequency data has not already been
107 : // scaled, then the output will need scaling by 1/FFTSize().
108 0 : void GetInverseWithoutScaling(float* aDataOut)
109 : {
110 0 : EnsureIFFT();
111 : #if defined(MOZ_LIBAV_FFT)
112 : {
113 : // Even though this function doesn't scale, the libav forward transform
114 : // gives a value that needs scaling by 2 in order for things to turn out
115 : // similar to how we expect from kissfft/openmax.
116 0 : AudioBufferCopyWithScale(mOutputBuffer.Elements()->f, 2.0f,
117 0 : aDataOut, mFFTSize);
118 0 : aDataOut[1] = 2.0f * mOutputBuffer[mFFTSize/2].r; // Packed Nyquist
119 0 : av_rdft_calc(mAvIRDFT, aDataOut);
120 : }
121 : #else
122 : #ifdef BUILD_ARM_NEON
123 : if (mozilla::supports_neon()) {
124 : omxSP_FFTInv_CCSToR_F32_Sfs_unscaled(mOutputBuffer.Elements()->f, aDataOut, mOmxIFFT);
125 : } else
126 : #endif
127 : {
128 : kiss_fftri(mKissIFFT, &(mOutputBuffer.Elements()->c), aDataOut);
129 : }
130 : #endif
131 0 : }
132 :
133 0 : void Multiply(const FFTBlock& aFrame)
134 : {
135 0 : uint32_t halfSize = mFFTSize / 2;
136 : // DFTs are not packed.
137 0 : MOZ_ASSERT(mOutputBuffer[0].i == 0);
138 0 : MOZ_ASSERT(aFrame.mOutputBuffer[0].i == 0);
139 :
140 0 : BufferComplexMultiply(mOutputBuffer.Elements()->f,
141 0 : aFrame.mOutputBuffer.Elements()->f,
142 0 : mOutputBuffer.Elements()->f,
143 0 : halfSize);
144 0 : mOutputBuffer[halfSize].r *= aFrame.mOutputBuffer[halfSize].r;
145 : // This would have been set to NaN if either real component was NaN.
146 0 : mOutputBuffer[0].i = 0.0f;
147 0 : }
148 :
149 : // Perform a forward FFT on |aData|, assuming zeros after dataSize samples,
150 : // and pre-scale the generated internal frequency domain coefficients so
151 : // that GetInverseWithoutScaling() can be used to transform to the time
152 : // domain. This is useful for convolution kernels.
153 0 : void PadAndMakeScaledDFT(const float* aData, size_t dataSize)
154 : {
155 0 : MOZ_ASSERT(dataSize <= FFTSize());
156 0 : AlignedTArray<float> paddedData;
157 0 : paddedData.SetLength(FFTSize());
158 0 : AudioBufferCopyWithScale(aData, 1.0f / FFTSize(),
159 0 : paddedData.Elements(), dataSize);
160 0 : PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize);
161 0 : PerformFFT(paddedData.Elements());
162 0 : }
163 :
164 0 : void SetFFTSize(uint32_t aSize)
165 : {
166 0 : mFFTSize = aSize;
167 0 : mOutputBuffer.SetLength(aSize / 2 + 1);
168 0 : PodZero(mOutputBuffer.Elements(), aSize / 2 + 1);
169 0 : Clear();
170 0 : }
171 :
172 : // Return the average group delay and removes this from the frequency data.
173 : double ExtractAverageGroupDelay();
174 :
175 0 : uint32_t FFTSize() const
176 : {
177 0 : return mFFTSize;
178 : }
179 : float RealData(uint32_t aIndex) const
180 : {
181 : return mOutputBuffer[aIndex].r;
182 : }
183 0 : float& RealData(uint32_t aIndex)
184 : {
185 0 : return mOutputBuffer[aIndex].r;
186 : }
187 : float ImagData(uint32_t aIndex) const
188 : {
189 : return mOutputBuffer[aIndex].i;
190 : }
191 0 : float& ImagData(uint32_t aIndex)
192 : {
193 0 : return mOutputBuffer[aIndex].i;
194 : }
195 :
196 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
197 : {
198 0 : size_t amount = 0;
199 : #if defined(MOZ_LIBAV_FFT)
200 0 : amount += aMallocSizeOf(mAvRDFT);
201 0 : amount += aMallocSizeOf(mAvIRDFT);
202 : #else
203 : amount += aMallocSizeOf(mKissFFT);
204 : amount += aMallocSizeOf(mKissIFFT);
205 : #endif
206 0 : amount += mOutputBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
207 0 : return amount;
208 : }
209 :
210 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
211 : {
212 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
213 : }
214 :
215 : private:
216 : FFTBlock(const FFTBlock& other) = delete;
217 : void operator=(const FFTBlock& other) = delete;
218 :
219 0 : void EnsureFFT()
220 : {
221 : #if defined(MOZ_LIBAV_FFT)
222 0 : if (!mAvRDFT) {
223 0 : mAvRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, DFT_R2C);
224 : }
225 : #else
226 : #ifdef BUILD_ARM_NEON
227 : if (mozilla::supports_neon()) {
228 : if (!mOmxFFT) {
229 : mOmxFFT = createOmxFFT(mFFTSize);
230 : }
231 : } else
232 : #endif
233 : {
234 : if (!mKissFFT) {
235 : mKissFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
236 : }
237 : }
238 : #endif
239 0 : }
240 0 : void EnsureIFFT()
241 : {
242 : #if defined(MOZ_LIBAV_FFT)
243 0 : if (!mAvIRDFT) {
244 0 : mAvIRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, IDFT_C2R);
245 : }
246 : #else
247 : #ifdef BUILD_ARM_NEON
248 : if (mozilla::supports_neon()) {
249 : if (!mOmxIFFT) {
250 : mOmxIFFT = createOmxFFT(mFFTSize);
251 : }
252 : } else
253 : #endif
254 : {
255 : if (!mKissIFFT) {
256 : mKissIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
257 : }
258 : }
259 : #endif
260 0 : }
261 :
262 : #ifdef BUILD_ARM_NEON
263 : static OMXFFTSpec_R_F32* createOmxFFT(uint32_t aFFTSize)
264 : {
265 : MOZ_ASSERT((aFFTSize & (aFFTSize-1)) == 0);
266 : OMX_INT bufSize;
267 : OMX_INT order = log((double)aFFTSize)/M_LN2;
268 : MOZ_ASSERT(aFFTSize>>order == 1);
269 : OMXResult status = omxSP_FFTGetBufSize_R_F32(order, &bufSize);
270 : if (status == OMX_Sts_NoErr) {
271 : OMXFFTSpec_R_F32* context = static_cast<OMXFFTSpec_R_F32*>(malloc(bufSize));
272 : if (omxSP_FFTInit_R_F32(context, order) != OMX_Sts_NoErr) {
273 : return nullptr;
274 : }
275 : return context;
276 : }
277 : return nullptr;
278 : }
279 : #endif
280 :
281 0 : void Clear()
282 : {
283 : #if defined(MOZ_LIBAV_FFT)
284 0 : av_rdft_end(mAvRDFT);
285 0 : av_rdft_end(mAvIRDFT);
286 0 : mAvRDFT = mAvIRDFT = nullptr;
287 : #else
288 : #ifdef BUILD_ARM_NEON
289 : free(mOmxFFT);
290 : free(mOmxIFFT);
291 : mOmxFFT = mOmxIFFT = nullptr;
292 : #endif
293 : free(mKissFFT);
294 : free(mKissIFFT);
295 : mKissFFT = mKissIFFT = nullptr;
296 : #endif
297 0 : }
298 : void AddConstantGroupDelay(double sampleFrameDelay);
299 : void InterpolateFrequencyComponents(const FFTBlock& block0,
300 : const FFTBlock& block1, double interp);
301 : #if defined(MOZ_LIBAV_FFT)
302 : RDFTContext *mAvRDFT;
303 : RDFTContext *mAvIRDFT;
304 : #else
305 : kiss_fftr_cfg mKissFFT;
306 : kiss_fftr_cfg mKissIFFT;
307 : #ifdef BUILD_ARM_NEON
308 : OMXFFTSpec_R_F32* mOmxFFT;
309 : OMXFFTSpec_R_F32* mOmxIFFT;
310 : #endif
311 : #endif
312 : AlignedTArray<ComplexU> mOutputBuffer;
313 : uint32_t mFFTSize;
314 : };
315 :
316 : } // namespace mozilla
317 :
318 : #endif
319 :
|