Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #ifndef FFTBlock_h_
8 #define FFTBlock_h_
10 #include "nsTArray.h"
11 #include "AudioNodeEngine.h"
12 #include "kiss_fft/kiss_fftr.h"
14 namespace mozilla {
16 // This class defines an FFT block, loosely modeled after Blink's FFTFrame
17 // class to make sharing code with Blink easy.
18 // Currently it's implemented on top of KissFFT on all platforms.
19 class FFTBlock {
20 public:
21 explicit FFTBlock(uint32_t aFFTSize)
22 : mFFT(nullptr)
23 , mIFFT(nullptr)
24 , mFFTSize(aFFTSize)
25 {
26 MOZ_COUNT_CTOR(FFTBlock);
27 mOutputBuffer.SetLength(aFFTSize / 2 + 1);
28 PodZero(mOutputBuffer.Elements(), aFFTSize / 2 + 1);
29 }
30 ~FFTBlock()
31 {
32 MOZ_COUNT_DTOR(FFTBlock);
33 Clear();
34 }
36 // Return a new FFTBlock with frequency components interpolated between
37 // |block0| and |block1| with |interp| between 0.0 and 1.0.
38 static FFTBlock*
39 CreateInterpolatedBlock(const FFTBlock& block0,
40 const FFTBlock& block1, double interp);
42 // Transform FFTSize() points of aData and store the result internally.
43 void PerformFFT(const float* aData)
44 {
45 EnsureFFT();
46 kiss_fftr(mFFT, aData, mOutputBuffer.Elements());
47 }
48 // Inverse-transform internal data and store the resulting FFTSize()
49 // points in aData.
50 void GetInverse(float* aDataOut)
51 {
52 GetInverseWithoutScaling(aDataOut);
53 AudioBufferInPlaceScale(aDataOut, 1.0f / mFFTSize, mFFTSize);
54 }
55 // Inverse-transform internal frequency data and store the resulting
56 // FFTSize() points in |aDataOut|. If frequency data has not already been
57 // scaled, then the output will need scaling by 1/FFTSize().
58 void GetInverseWithoutScaling(float* aDataOut)
59 {
60 EnsureIFFT();
61 kiss_fftri(mIFFT, mOutputBuffer.Elements(), aDataOut);
62 }
63 // Inverse-transform the FFTSize()/2+1 points of data in each
64 // of aRealDataIn and aImagDataIn and store the resulting
65 // FFTSize() points in aRealDataOut.
66 void PerformInverseFFT(float* aRealDataIn,
67 float *aImagDataIn,
68 float *aRealDataOut)
69 {
70 EnsureIFFT();
71 const uint32_t inputSize = mFFTSize / 2 + 1;
72 nsTArray<kiss_fft_cpx> inputBuffer;
73 inputBuffer.SetLength(inputSize);
74 for (uint32_t i = 0; i < inputSize; ++i) {
75 inputBuffer[i].r = aRealDataIn[i];
76 inputBuffer[i].i = aImagDataIn[i];
77 }
78 kiss_fftri(mIFFT, inputBuffer.Elements(), aRealDataOut);
79 for (uint32_t i = 0; i < mFFTSize; ++i) {
80 aRealDataOut[i] /= mFFTSize;
81 }
82 }
84 void Multiply(const FFTBlock& aFrame)
85 {
86 BufferComplexMultiply(reinterpret_cast<const float*>(mOutputBuffer.Elements()),
87 reinterpret_cast<const float*>(aFrame.mOutputBuffer.Elements()),
88 reinterpret_cast<float*>(mOutputBuffer.Elements()),
89 mFFTSize / 2 + 1);
90 }
92 // Perform a forward FFT on |aData|, assuming zeros after dataSize samples,
93 // and pre-scale the generated internal frequency domain coefficients so
94 // that GetInverseWithoutScaling() can be used to transform to the time
95 // domain. This is useful for convolution kernels.
96 void PadAndMakeScaledDFT(const float* aData, size_t dataSize)
97 {
98 MOZ_ASSERT(dataSize <= FFTSize());
99 nsTArray<float> paddedData;
100 paddedData.SetLength(FFTSize());
101 AudioBufferCopyWithScale(aData, 1.0f / FFTSize(),
102 paddedData.Elements(), dataSize);
103 PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize);
104 PerformFFT(paddedData.Elements());
105 }
107 void SetFFTSize(uint32_t aSize)
108 {
109 mFFTSize = aSize;
110 mOutputBuffer.SetLength(aSize / 2 + 1);
111 PodZero(mOutputBuffer.Elements(), aSize / 2 + 1);
112 Clear();
113 }
115 // Return the average group delay and removes this from the frequency data.
116 double ExtractAverageGroupDelay();
118 uint32_t FFTSize() const
119 {
120 return mFFTSize;
121 }
122 float RealData(uint32_t aIndex) const
123 {
124 return mOutputBuffer[aIndex].r;
125 }
126 float ImagData(uint32_t aIndex) const
127 {
128 return mOutputBuffer[aIndex].i;
129 }
131 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
132 {
133 size_t amount = 0;
134 amount += aMallocSizeOf(mFFT);
135 amount += aMallocSizeOf(mIFFT);
136 amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
137 return amount;
138 }
140 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
141 {
142 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
143 }
145 private:
146 FFTBlock(const FFTBlock& other) MOZ_DELETE;
147 void operator=(const FFTBlock& other) MOZ_DELETE;
149 void EnsureFFT()
150 {
151 if (!mFFT) {
152 mFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
153 }
154 }
155 void EnsureIFFT()
156 {
157 if (!mIFFT) {
158 mIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
159 }
160 }
161 void Clear()
162 {
163 free(mFFT);
164 free(mIFFT);
165 mFFT = mIFFT = nullptr;
166 }
167 void AddConstantGroupDelay(double sampleFrameDelay);
168 void InterpolateFrequencyComponents(const FFTBlock& block0,
169 const FFTBlock& block1, double interp);
171 kiss_fftr_cfg mFFT, mIFFT;
172 nsTArray<kiss_fft_cpx> mOutputBuffer;
173 uint32_t mFFTSize;
174 };
176 }
178 #endif