2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // FFTFrame implementation using FFmpeg's RDFT algorithm,
27 // suitable for use on Windows and Linux.
33 #if USE(WEBAUDIO_FFMPEG)
37 #include "VectorMath.h"
40 #include <libavcodec/avfft.h>
43 #include <wtf/MathExtras.h>
47 const int kMaxFFTPow2Size = 24;
49 // Normal constructor: allocates for a given fftSize.
50 FFTFrame::FFTFrame(unsigned fftSize)
52 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
55 , m_complexData(fftSize)
56 , m_realData(fftSize / 2)
57 , m_imagData(fftSize / 2)
59 // We only allow power of two.
60 ASSERT(1UL << m_log2FFTSize == m_FFTSize);
62 m_forwardContext = contextForSize(fftSize, DFT_R2C);
63 m_inverseContext = contextForSize(fftSize, IDFT_C2R);
66 // Creates a blank/empty frame (interpolate() must later be called).
76 FFTFrame::FFTFrame(const FFTFrame& frame)
77 : m_FFTSize(frame.m_FFTSize)
78 , m_log2FFTSize(frame.m_log2FFTSize)
81 , m_complexData(frame.m_FFTSize)
82 , m_realData(frame.m_FFTSize / 2)
83 , m_imagData(frame.m_FFTSize / 2)
85 m_forwardContext = contextForSize(m_FFTSize, DFT_R2C);
86 m_inverseContext = contextForSize(m_FFTSize, IDFT_C2R);
88 // Copy/setup frame data.
89 unsigned nbytes = sizeof(float) * (m_FFTSize / 2);
90 memcpy(realData(), frame.realData(), nbytes);
91 memcpy(imagData(), frame.imagData(), nbytes);
94 void FFTFrame::initialize()
98 void FFTFrame::cleanup()
102 FFTFrame::~FFTFrame()
104 av_rdft_end(m_forwardContext);
105 av_rdft_end(m_inverseContext);
108 void FFTFrame::multiply(const FFTFrame& frame)
110 FFTFrame& frame1 = *this;
111 FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
113 float* realP1 = frame1.realData();
114 float* imagP1 = frame1.imagData();
115 const float* realP2 = frame2.realData();
116 const float* imagP2 = frame2.imagData();
118 // Scale accounts the peculiar scaling of vecLib on the Mac.
119 // This ensures the right scaling all the way back to inverse FFT.
120 // FIXME: if we change the scaling on the Mac then this scale
121 // factor will need to change too.
124 // Multiply the packed DC/nyquist component
125 realP1[0] *= scale * realP2[0];
126 imagP1[0] *= scale * imagP2[0];
128 // Complex multiplication. If this loop turns out to be hot then
129 // we should use SSE or other intrinsics to accelerate it.
130 unsigned halfSize = fftSize() / 2;
132 for (unsigned i = 1; i < halfSize; ++i) {
133 float realResult = realP1[i] * realP2[i] - imagP1[i] * imagP2[i];
134 float imagResult = realP1[i] * imagP2[i] + imagP1[i] * realP2[i];
136 realP1[i] = scale * realResult;
137 imagP1[i] = scale * imagResult;
141 void FFTFrame::doFFT(float* data)
143 // Copy since processing is in-place.
144 float* p = m_complexData.data();
145 memcpy(p, data, sizeof(float) * m_FFTSize);
147 // Compute Forward transform.
148 av_rdft_calc(m_forwardContext, p);
150 // De-interleave to separate real and complex arrays.
151 int len = m_FFTSize / 2;
153 // FIXME: see above comment in multiply() about scaling.
154 const float scale = 2.0f;
156 for (int i = 0; i < len; ++i) {
157 int baseComplexIndex = 2 * i;
158 // m_realData[0] is the DC component and m_imagData[0] is the nyquist component
159 // since the interleaved complex data is packed.
160 m_realData[i] = scale * p[baseComplexIndex];
161 m_imagData[i] = scale * p[baseComplexIndex + 1];
165 void FFTFrame::doInverseFFT(float* data)
167 // Prepare interleaved data.
168 float* interleavedData = getUpToDateComplexData();
170 // Compute inverse transform.
171 av_rdft_calc(m_inverseContext, interleavedData);
173 // Scale so that a forward then inverse FFT yields exactly the original data.
174 const float scale = 1.0 / m_FFTSize;
175 VectorMath::vsmul(interleavedData, 1, &scale, data, 1, m_FFTSize);
178 float* FFTFrame::realData() const
180 return const_cast<float*>(m_realData.data());
183 float* FFTFrame::imagData() const
185 return const_cast<float*>(m_imagData.data());
188 float* FFTFrame::getUpToDateComplexData()
190 // FIXME: if we can't completely get rid of this method, SSE
191 // optimization could be considered if it shows up hot on profiles.
192 int len = m_FFTSize / 2;
193 for (int i = 0; i < len; ++i) {
194 int baseComplexIndex = 2 * i;
195 m_complexData[baseComplexIndex] = m_realData[i];
196 m_complexData[baseComplexIndex + 1] = m_imagData[i];
198 return const_cast<float*>(m_complexData.data());
201 RDFTContext* FFTFrame::contextForSize(unsigned fftSize, int trans)
203 // FIXME: This is non-optimal. Ideally, we'd like to share the contexts for FFTFrames of the same size.
204 // But FFmpeg's RDFT uses a scratch buffer inside the context and so they are not thread-safe.
205 // We could improve this by sharing the FFTFrames on a per-thread basis.
207 int pow2size = static_cast<int>(log2(fftSize));
208 ASSERT(pow2size < kMaxFFTPow2Size);
210 RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans);
214 } // namespace WebCore
216 #endif // !OS(DARWIN) && USE(WEBAUDIO_FFMPEG)
218 #endif // ENABLE(WEB_AUDIO)