1/*
2 * Copyright (C) 2010 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 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29// Mac OS X - specific FFTFrame implementation
30
31#include "config.h"
32
33#if ENABLE(WEB_AUDIO)
34
35#if OS(DARWIN) && !USE(WEBAUDIO_FFMPEG)
36
37#include "FFTFrame.h"
38
39#include "VectorMath.h"
40
41namespace WebCore {
42
43const int kMaxFFTPow2Size = 24;
44
45FFTSetup* FFTFrame::fftSetups = 0;
46
47// Normal constructor: allocates for a given fftSize
48FFTFrame::FFTFrame(unsigned fftSize)
49    : m_realData(fftSize)
50    , m_imagData(fftSize)
51{
52    m_FFTSize = fftSize;
53    m_log2FFTSize = static_cast<unsigned>(log2(fftSize));
54
55    // We only allow power of two
56    ASSERT(1UL << m_log2FFTSize == m_FFTSize);
57
58    // Lazily create and share fftSetup with other frames
59    m_FFTSetup = fftSetupForSize(fftSize);
60
61    // Setup frame data
62    m_frame.realp = m_realData.data();
63    m_frame.imagp = m_imagData.data();
64}
65
66// Creates a blank/empty frame (interpolate() must later be called)
67FFTFrame::FFTFrame()
68    : m_realData(0)
69    , m_imagData(0)
70{
71    // Later will be set to correct values when interpolate() is called
72    m_frame.realp = 0;
73    m_frame.imagp = 0;
74
75    m_FFTSize = 0;
76    m_log2FFTSize = 0;
77}
78
79// Copy constructor
80FFTFrame::FFTFrame(const FFTFrame& frame)
81    : m_FFTSize(frame.m_FFTSize)
82    , m_log2FFTSize(frame.m_log2FFTSize)
83    , m_FFTSetup(frame.m_FFTSetup)
84    , m_realData(frame.m_FFTSize)
85    , m_imagData(frame.m_FFTSize)
86{
87    // Setup frame data
88    m_frame.realp = m_realData.data();
89    m_frame.imagp = m_imagData.data();
90
91    // Copy/setup frame data
92    unsigned nbytes = sizeof(float) * m_FFTSize;
93    memcpy(realData(), frame.m_frame.realp, nbytes);
94    memcpy(imagData(), frame.m_frame.imagp, nbytes);
95}
96
97FFTFrame::~FFTFrame()
98{
99}
100
101void FFTFrame::multiply(const FFTFrame& frame)
102{
103    FFTFrame& frame1 = *this;
104    const FFTFrame& frame2 = frame;
105
106    float* realP1 = frame1.realData();
107    float* imagP1 = frame1.imagData();
108    const float* realP2 = frame2.realData();
109    const float* imagP2 = frame2.imagData();
110
111    unsigned halfSize = m_FFTSize / 2;
112    float real0 = realP1[0];
113    float imag0 = imagP1[0];
114
115    // Complex multiply
116    VectorMath::zvmul(realP1, imagP1, realP2, imagP2, realP1, imagP1, halfSize);
117
118    // Multiply the packed DC/nyquist component
119    realP1[0] = real0 * realP2[0];
120    imagP1[0] = imag0 * imagP2[0];
121
122    // Scale accounts for vecLib's peculiar scaling
123    // This ensures the right scaling all the way back to inverse FFT
124    float scale = 0.5f;
125
126    VectorMath::vsmul(realP1, 1, &scale, realP1, 1, halfSize);
127    VectorMath::vsmul(imagP1, 1, &scale, imagP1, 1, halfSize);
128}
129
130void FFTFrame::doFFT(const float* data)
131{
132    vDSP_ctoz((DSPComplex*)data, 2, &m_frame, 1, m_FFTSize / 2);
133    vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_FORWARD);
134}
135
136void FFTFrame::doInverseFFT(float* data)
137{
138    vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_INVERSE);
139    vDSP_ztoc(&m_frame, 1, (DSPComplex*)data, 2, m_FFTSize / 2);
140
141    // Do final scaling so that x == IFFT(FFT(x))
142    float scale = 0.5f / m_FFTSize;
143    vDSP_vsmul(data, 1, &scale, data, 1, m_FFTSize);
144}
145
146FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize)
147{
148    if (!fftSetups) {
149        fftSetups = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size);
150        memset(fftSetups, 0, sizeof(FFTSetup) * kMaxFFTPow2Size);
151    }
152
153    int pow2size = static_cast<int>(log2(fftSize));
154    ASSERT(pow2size < kMaxFFTPow2Size);
155    if (!fftSetups[pow2size])
156        fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2);
157
158    return fftSetups[pow2size];
159}
160
161void FFTFrame::initialize()
162{
163}
164
165void FFTFrame::cleanup()
166{
167    if (!fftSetups)
168        return;
169
170    for (int i = 0; i < kMaxFFTPow2Size; ++i) {
171        if (fftSetups[i])
172            vDSP_destroy_fftsetup(fftSetups[i]);
173    }
174
175    free(fftSetups);
176    fftSetups = 0;
177}
178
179float* FFTFrame::realData() const
180{
181    return m_frame.realp;
182}
183
184float* FFTFrame::imagData() const
185{
186    return m_frame.imagp;
187}
188
189} // namespace WebCore
190
191#endif // #if OS(DARWIN) && !USE(WEBAUDIO_FFMPEG)
192
193#endif // ENABLE(WEB_AUDIO)
194