1/*
2 * Copyright (C) 2012 Igalia S.L
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19// FFTFrame implementation using the GStreamer FFT library.
20
21#include "config.h"
22
23#if USE(WEBAUDIO_GSTREAMER)
24
25#include "FFTFrame.h"
26
27#include "VectorMath.h"
28#include <wtf/FastMalloc.h>
29#include <wtf/StdLibExtras.h>
30
31namespace {
32
33size_t unpackedFFTDataSize(unsigned fftSize)
34{
35 return fftSize / 2 + 1;
36}
37
38} // anonymous namespace
39
40namespace WebCore {
41
42// Normal constructor: allocates for a given fftSize.
43FFTFrame::FFTFrame(unsigned fftSize)
44 : m_FFTSize(fftSize)
45 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
46 , m_complexData(makeUniqueArray<GstFFTF32Complex>(unpackedFFTDataSize(m_FFTSize)))
47 , m_realData(unpackedFFTDataSize(m_FFTSize))
48 , m_imagData(unpackedFFTDataSize(m_FFTSize))
49{
50 int fftLength = gst_fft_next_fast_length(m_FFTSize);
51 m_fft = gst_fft_f32_new(fftLength, FALSE);
52 m_inverseFft = gst_fft_f32_new(fftLength, TRUE);
53}
54
55// Creates a blank/empty frame (interpolate() must later be called).
56FFTFrame::FFTFrame()
57 : m_FFTSize(0)
58 , m_log2FFTSize(0)
59{
60 int fftLength = gst_fft_next_fast_length(m_FFTSize);
61 m_fft = gst_fft_f32_new(fftLength, FALSE);
62 m_inverseFft = gst_fft_f32_new(fftLength, TRUE);
63}
64
65// Copy constructor.
66FFTFrame::FFTFrame(const FFTFrame& frame)
67 : m_FFTSize(frame.m_FFTSize)
68 , m_log2FFTSize(frame.m_log2FFTSize)
69 , m_complexData(makeUniqueArray<GstFFTF32Complex>(unpackedFFTDataSize(m_FFTSize)))
70 , m_realData(unpackedFFTDataSize(frame.m_FFTSize))
71 , m_imagData(unpackedFFTDataSize(frame.m_FFTSize))
72{
73 int fftLength = gst_fft_next_fast_length(m_FFTSize);
74 m_fft = gst_fft_f32_new(fftLength, FALSE);
75 m_inverseFft = gst_fft_f32_new(fftLength, TRUE);
76
77 // Copy/setup frame data.
78 memcpy(realData(), frame.realData(), sizeof(float) * unpackedFFTDataSize(m_FFTSize));
79 memcpy(imagData(), frame.imagData(), sizeof(float) * unpackedFFTDataSize(m_FFTSize));
80}
81
82void FFTFrame::initialize()
83{
84}
85
86void FFTFrame::cleanup()
87{
88}
89
90FFTFrame::~FFTFrame()
91{
92 if (!m_fft)
93 return;
94
95 gst_fft_f32_free(m_fft);
96 m_fft = 0;
97
98 gst_fft_f32_free(m_inverseFft);
99 m_inverseFft = 0;
100}
101
102void FFTFrame::multiply(const FFTFrame& frame)
103{
104 FFTFrame& frame1 = *this;
105 FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
106
107 float* realP1 = frame1.realData();
108 float* imagP1 = frame1.imagData();
109 const float* realP2 = frame2.realData();
110 const float* imagP2 = frame2.imagData();
111
112 size_t size = unpackedFFTDataSize(m_FFTSize);
113 VectorMath::zvmul(realP1, imagP1, realP2, imagP2, realP1, imagP1, size);
114
115 // Scale accounts the peculiar scaling of vecLib on the Mac.
116 // This ensures the right scaling all the way back to inverse FFT.
117 // FIXME: if we change the scaling on the Mac then this scale
118 // factor will need to change too.
119 float scale = 0.5f;
120
121 VectorMath::vsmul(realP1, 1, &scale, realP1, 1, size);
122 VectorMath::vsmul(imagP1, 1, &scale, imagP1, 1, size);
123}
124
125void FFTFrame::doFFT(const float* data)
126{
127 gst_fft_f32_fft(m_fft, data, m_complexData.get());
128
129 // Scale the frequency domain data to match vecLib's scale factor
130 // on the Mac. FIXME: if we change the definition of FFTFrame to
131 // eliminate this scale factor then this code will need to change.
132 // Also, if this loop turns out to be hot then we should use SSE
133 // or other intrinsics to accelerate it.
134 float scaleFactor = 2;
135
136 float* imagData = m_imagData.data();
137 float* realData = m_realData.data();
138 for (unsigned i = 0; i < unpackedFFTDataSize(m_FFTSize); ++i) {
139 imagData[i] = m_complexData[i].i * scaleFactor;
140 realData[i] = m_complexData[i].r * scaleFactor;
141 }
142}
143
144void FFTFrame::doInverseFFT(float* data)
145{
146 // Merge the real and imaginary vectors to complex vector.
147 float* realData = m_realData.data();
148 float* imagData = m_imagData.data();
149
150 for (size_t i = 0; i < unpackedFFTDataSize(m_FFTSize); ++i) {
151 m_complexData[i].i = imagData[i];
152 m_complexData[i].r = realData[i];
153 }
154
155 gst_fft_f32_inverse_fft(m_inverseFft, m_complexData.get(), data);
156
157 // Scale so that a forward then inverse FFT yields exactly the original data.
158 const float scaleFactor = 1.0 / (2 * m_FFTSize);
159 VectorMath::vsmul(data, 1, &scaleFactor, data, 1, m_FFTSize);
160}
161
162float* FFTFrame::realData() const
163{
164 return const_cast<float*>(m_realData.data());
165}
166
167float* FFTFrame::imagData() const
168{
169 return const_cast<float*>(m_imagData.data());
170}
171
172} // namespace WebCore
173
174#endif // USE(WEBAUDIO_GSTREAMER)
175