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)
35 #include "platform/audio/FFTFrame.h"
37 #include "platform/audio/VectorMath.h"
40 #include <libavcodec/avfft.h>
43 #include "wtf/MathExtras.h"
48 const int kMaxFFTPow2Size
= 24;
51 // Normal constructor: allocates for a given fftSize.
52 FFTFrame::FFTFrame(unsigned fftSize
)
54 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize
)))
55 , m_realData(fftSize
/ 2)
56 , m_imagData(fftSize
/ 2)
57 , m_forwardContext(nullptr)
58 , m_inverseContext(nullptr)
59 , m_complexData(fftSize
)
61 // We only allow power of two.
62 ASSERT(1UL << m_log2FFTSize
== m_FFTSize
);
64 m_forwardContext
= contextForSize(fftSize
, DFT_R2C
);
65 m_inverseContext
= contextForSize(fftSize
, IDFT_C2R
);
68 // Creates a blank/empty frame (interpolate() must later be called).
72 , m_forwardContext(nullptr)
73 , m_inverseContext(nullptr)
78 FFTFrame::FFTFrame(const FFTFrame
& frame
)
79 : m_FFTSize(frame
.m_FFTSize
)
80 , m_log2FFTSize(frame
.m_log2FFTSize
)
81 , m_realData(frame
.m_FFTSize
/ 2)
82 , m_imagData(frame
.m_FFTSize
/ 2)
83 , m_forwardContext(nullptr)
84 , m_inverseContext(nullptr)
85 , m_complexData(frame
.m_FFTSize
)
87 m_forwardContext
= contextForSize(m_FFTSize
, DFT_R2C
);
88 m_inverseContext
= contextForSize(m_FFTSize
, IDFT_C2R
);
90 // Copy/setup frame data.
91 unsigned nbytes
= sizeof(float) * (m_FFTSize
/ 2);
92 memcpy(realData(), frame
.realData(), nbytes
);
93 memcpy(imagData(), frame
.imagData(), nbytes
);
96 void FFTFrame::initialize()
100 void FFTFrame::cleanup()
104 FFTFrame::~FFTFrame()
106 av_rdft_end(m_forwardContext
);
107 av_rdft_end(m_inverseContext
);
110 void FFTFrame::doFFT(const float* data
)
112 // Copy since processing is in-place.
113 float* p
= m_complexData
.data();
114 memcpy(p
, data
, sizeof(float) * m_FFTSize
);
116 // Compute Forward transform.
117 av_rdft_calc(m_forwardContext
, p
);
119 // De-interleave to separate real and complex arrays.
120 int len
= m_FFTSize
/ 2;
122 float* real
= m_realData
.data();
123 float* imag
= m_imagData
.data();
124 for (int i
= 0; i
< len
; ++i
) {
125 int baseComplexIndex
= 2 * i
;
126 // m_realData[0] is the DC component and m_imagData[0] is the nyquist component
127 // since the interleaved complex data is packed.
128 real
[i
] = p
[baseComplexIndex
];
129 imag
[i
] = p
[baseComplexIndex
+ 1];
133 void FFTFrame::doInverseFFT(float* data
)
135 // Prepare interleaved data.
136 float* interleavedData
= getUpToDateComplexData();
138 // Compute inverse transform.
139 av_rdft_calc(m_inverseContext
, interleavedData
);
141 // Scale so that a forward then inverse FFT yields exactly the original data. For some reason
142 // av_rdft_calc above returns values that are half of what I expect. Hence make the scale factor
143 // twice as large to compensate for that.
144 const float scale
= 2.0 / m_FFTSize
;
145 VectorMath::vsmul(interleavedData
, 1, &scale
, data
, 1, m_FFTSize
);
148 float* FFTFrame::getUpToDateComplexData()
150 // FIXME: if we can't completely get rid of this method, SSE
151 // optimization could be considered if it shows up hot on profiles.
152 int len
= m_FFTSize
/ 2;
153 const float* real
= m_realData
.data();
154 const float* imag
= m_imagData
.data();
155 float* c
= m_complexData
.data();
156 for (int i
= 0; i
< len
; ++i
) {
157 int baseComplexIndex
= 2 * i
;
158 c
[baseComplexIndex
] = real
[i
];
159 c
[baseComplexIndex
+ 1] = imag
[i
];
161 return const_cast<float*>(m_complexData
.data());
164 RDFTContext
* FFTFrame::contextForSize(unsigned fftSize
, int trans
)
166 // FIXME: This is non-optimal. Ideally, we'd like to share the contexts for FFTFrames of the same size.
167 // But FFmpeg's RDFT uses a scratch buffer inside the context and so they are not thread-safe.
168 // We could improve this by sharing the FFTFrames on a per-thread basis.
170 int pow2size
= static_cast<int>(log2(fftSize
));
171 ASSERT(pow2size
< kMaxFFTPow2Size
);
173 RDFTContext
* context
= av_rdft_init(pow2size
, (RDFTransformType
)trans
);
179 #endif // USE(WEBAUDIO_FFMPEG)
181 #endif // ENABLE(WEB_AUDIO)