Coverage Report

Created: 2018-09-25 14:53

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