initial import
[vuplus_webkit] / Source / WebCore / platform / audio / ffmpeg / FFTFrameFFMPEG.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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.
13  *
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.
24  */
25
26 // FFTFrame implementation using FFmpeg's RDFT algorithm,
27 // suitable for use on Windows and Linux.
28
29 #include "config.h"
30
31 #if ENABLE(WEB_AUDIO)
32
33 #if USE(WEBAUDIO_FFMPEG)
34
35 #include "FFTFrame.h"
36
37 #include "VectorMath.h"
38
39 extern "C" {
40     #include <libavcodec/avfft.h>
41 }
42
43 #include <wtf/MathExtras.h>
44
45 namespace WebCore {
46
47 const int kMaxFFTPow2Size = 24;
48
49 // Normal constructor: allocates for a given fftSize.
50 FFTFrame::FFTFrame(unsigned fftSize)
51     : m_FFTSize(fftSize)
52     , m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
53     , m_forwardContext(0)
54     , m_inverseContext(0)
55     , m_complexData(fftSize)
56     , m_realData(fftSize / 2)
57     , m_imagData(fftSize / 2)
58 {
59     // We only allow power of two.
60     ASSERT(1UL << m_log2FFTSize == m_FFTSize);
61
62     m_forwardContext = contextForSize(fftSize, DFT_R2C);
63     m_inverseContext = contextForSize(fftSize, IDFT_C2R);
64 }
65
66 // Creates a blank/empty frame (interpolate() must later be called).
67 FFTFrame::FFTFrame()
68     : m_FFTSize(0)
69     , m_log2FFTSize(0)
70     , m_forwardContext(0)
71     , m_inverseContext(0)
72 {
73 }
74
75 // Copy constructor.
76 FFTFrame::FFTFrame(const FFTFrame& frame)
77     : m_FFTSize(frame.m_FFTSize)
78     , m_log2FFTSize(frame.m_log2FFTSize)
79     , m_forwardContext(0)
80     , m_inverseContext(0)
81     , m_complexData(frame.m_FFTSize)
82     , m_realData(frame.m_FFTSize / 2)
83     , m_imagData(frame.m_FFTSize / 2)
84 {
85     m_forwardContext = contextForSize(m_FFTSize, DFT_R2C);
86     m_inverseContext = contextForSize(m_FFTSize, IDFT_C2R);
87
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);
92 }
93
94 void FFTFrame::initialize()
95 {
96 }
97
98 void FFTFrame::cleanup()
99 {
100 }
101
102 FFTFrame::~FFTFrame()
103 {
104     av_rdft_end(m_forwardContext);
105     av_rdft_end(m_inverseContext);
106 }
107
108 void FFTFrame::multiply(const FFTFrame& frame)
109 {
110     FFTFrame& frame1 = *this;
111     FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
112
113     float* realP1 = frame1.realData();
114     float* imagP1 = frame1.imagData();
115     const float* realP2 = frame2.realData();
116     const float* imagP2 = frame2.imagData();
117
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.
122     float scale = 0.5f;
123
124     // Multiply the packed DC/nyquist component
125     realP1[0] *= scale * realP2[0];
126     imagP1[0] *= scale * imagP2[0];
127
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;
131
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];
135
136         realP1[i] = scale * realResult;
137         imagP1[i] = scale * imagResult;
138     }
139 }
140
141 void FFTFrame::doFFT(float* data)
142 {
143     // Copy since processing is in-place.
144     float* p = m_complexData.data();
145     memcpy(p, data, sizeof(float) * m_FFTSize);
146
147     // Compute Forward transform.
148     av_rdft_calc(m_forwardContext, p);
149
150     // De-interleave to separate real and complex arrays.
151     int len = m_FFTSize / 2;
152
153     // FIXME: see above comment in multiply() about scaling.
154     const float scale = 2.0f;
155
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];
162     }
163 }
164
165 void FFTFrame::doInverseFFT(float* data)
166 {
167     // Prepare interleaved data.
168     float* interleavedData = getUpToDateComplexData();
169
170     // Compute inverse transform.
171     av_rdft_calc(m_inverseContext, interleavedData);
172
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);
176 }
177
178 float* FFTFrame::realData() const
179 {
180     return const_cast<float*>(m_realData.data());
181 }
182
183 float* FFTFrame::imagData() const
184 {
185     return const_cast<float*>(m_imagData.data());
186 }
187
188 float* FFTFrame::getUpToDateComplexData()
189 {
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];
197     }
198     return const_cast<float*>(m_complexData.data());
199 }
200
201 RDFTContext* FFTFrame::contextForSize(unsigned fftSize, int trans)
202 {
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.
206     ASSERT(fftSize);
207     int pow2size = static_cast<int>(log2(fftSize));
208     ASSERT(pow2size < kMaxFFTPow2Size);
209
210     RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans);
211     return context;
212 }
213
214 } // namespace WebCore
215
216 #endif // !OS(DARWIN) && USE(WEBAUDIO_FFMPEG)
217
218 #endif // ENABLE(WEB_AUDIO)