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/FastAllocBase.h>
29
30namespace {
31
32size_t unpackedFFTDataSize(unsigned fftSize)
33{
34    return fftSize / 2 + 1;
35}
36
37} // anonymous namespace
38
39namespace WebCore {
40
41// Normal constructor: allocates for a given fftSize.
42FFTFrame::FFTFrame(unsigned fftSize)
43    : m_FFTSize(fftSize)
44    , m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
45    , m_realData(unpackedFFTDataSize(m_FFTSize))
46    , m_imagData(unpackedFFTDataSize(m_FFTSize))
47{
48    m_complexData = WTF::fastNewArray<GstFFTF32Complex>(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    , m_complexData(0)
60{
61    int fftLength = gst_fft_next_fast_length(m_FFTSize);
62    m_fft = gst_fft_f32_new(fftLength, FALSE);
63    m_inverseFft = gst_fft_f32_new(fftLength, TRUE);
64}
65
66// Copy constructor.
67FFTFrame::FFTFrame(const FFTFrame& frame)
68    : m_FFTSize(frame.m_FFTSize)
69    , m_log2FFTSize(frame.m_log2FFTSize)
70    , m_realData(unpackedFFTDataSize(frame.m_FFTSize))
71    , m_imagData(unpackedFFTDataSize(frame.m_FFTSize))
72{
73    m_complexData = WTF::fastNewArray<GstFFTF32Complex>(unpackedFFTDataSize(m_FFTSize));
74
75    int fftLength = gst_fft_next_fast_length(m_FFTSize);
76    m_fft = gst_fft_f32_new(fftLength, FALSE);
77    m_inverseFft = gst_fft_f32_new(fftLength, TRUE);
78
79    // Copy/setup frame data.
80    memcpy(realData(), frame.realData(), sizeof(float) * unpackedFFTDataSize(m_FFTSize));
81    memcpy(imagData(), frame.imagData(), sizeof(float) * unpackedFFTDataSize(m_FFTSize));
82}
83
84void FFTFrame::initialize()
85{
86}
87
88void FFTFrame::cleanup()
89{
90}
91
92FFTFrame::~FFTFrame()
93{
94    if (!m_fft)
95        return;
96
97    gst_fft_f32_free(m_fft);
98    m_fft = 0;
99
100    gst_fft_f32_free(m_inverseFft);
101    m_inverseFft = 0;
102
103    WTF::fastDeleteArray(m_complexData);
104}
105
106void FFTFrame::multiply(const FFTFrame& frame)
107{
108    FFTFrame& frame1 = *this;
109    FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
110
111    float* realP1 = frame1.realData();
112    float* imagP1 = frame1.imagData();
113    const float* realP2 = frame2.realData();
114    const float* imagP2 = frame2.imagData();
115
116    size_t size = unpackedFFTDataSize(m_FFTSize);
117    VectorMath::zvmul(realP1, imagP1, realP2, imagP2, realP1, imagP1, size);
118
119    // Scale accounts the peculiar scaling of vecLib on the Mac.
120    // This ensures the right scaling all the way back to inverse FFT.
121    // FIXME: if we change the scaling on the Mac then this scale
122    // factor will need to change too.
123    float scale = 0.5f;
124
125    VectorMath::vsmul(realP1, 1, &scale, realP1, 1, size);
126    VectorMath::vsmul(imagP1, 1, &scale, imagP1, 1, size);
127}
128
129void FFTFrame::doFFT(const float* data)
130{
131    gst_fft_f32_fft(m_fft, data, m_complexData);
132
133    // Scale the frequency domain data to match vecLib's scale factor
134    // on the Mac. FIXME: if we change the definition of FFTFrame to
135    // eliminate this scale factor then this code will need to change.
136    // Also, if this loop turns out to be hot then we should use SSE
137    // or other intrinsics to accelerate it.
138    float scaleFactor = 2;
139
140    float* imagData = m_imagData.data();
141    float* realData = m_realData.data();
142    for (unsigned i = 0; i < unpackedFFTDataSize(m_FFTSize); ++i) {
143        imagData[i] = m_complexData[i].i * scaleFactor;
144        realData[i] = m_complexData[i].r * scaleFactor;
145    }
146}
147
148void FFTFrame::doInverseFFT(float* data)
149{
150    //  Merge the real and imaginary vectors to complex vector.
151    float* realData = m_realData.data();
152    float* imagData = m_imagData.data();
153
154    for (size_t i = 0; i < unpackedFFTDataSize(m_FFTSize); ++i) {
155        m_complexData[i].i = imagData[i];
156        m_complexData[i].r = realData[i];
157    }
158
159    gst_fft_f32_inverse_fft(m_inverseFft, m_complexData, data);
160
161    // Scale so that a forward then inverse FFT yields exactly the original data.
162    const float scaleFactor = 1.0 / (2 * m_FFTSize);
163    VectorMath::vsmul(data, 1, &scaleFactor, data, 1, m_FFTSize);
164}
165
166float* FFTFrame::realData() const
167{
168    return const_cast<float*>(m_realData.data());
169}
170
171float* FFTFrame::imagData() const
172{
173    return const_cast<float*>(m_imagData.data());
174}
175
176} // namespace WebCore
177
178#endif // USE(WEBAUDIO_GSTREAMER)
179