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 "platform/audio/FFTFrame.h"
36
37 #include "platform/audio/VectorMath.h"
38
39 extern "C" {
40 #include <libavcodec/avfft.h>
41 }
42
43 #include "wtf/MathExtras.h"
44
45 namespace blink {
46
47 #if ENABLE(ASSERT)
48 const int kMaxFFTPow2Size = 24;
49 #endif
50
51 // Normal constructor: allocates for a given fftSize.
FFTFrame(unsigned fftSize)52 FFTFrame::FFTFrame(unsigned fftSize)
53 : m_FFTSize(fftSize)
54 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
55 , m_realData(fftSize / 2)
56 , m_imagData(fftSize / 2)
57 , m_forwardContext(0)
58 , m_inverseContext(0)
59 , m_complexData(fftSize)
60 {
61 // We only allow power of two.
62 ASSERT(1UL << m_log2FFTSize == m_FFTSize);
63
64 m_forwardContext = contextForSize(fftSize, DFT_R2C);
65 m_inverseContext = contextForSize(fftSize, IDFT_C2R);
66 }
67
68 // Creates a blank/empty frame (interpolate() must later be called).
FFTFrame()69 FFTFrame::FFTFrame()
70 : m_FFTSize(0)
71 , m_log2FFTSize(0)
72 , m_forwardContext(0)
73 , m_inverseContext(0)
74 {
75 }
76
77 // Copy constructor.
FFTFrame(const FFTFrame & frame)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(0)
84 , m_inverseContext(0)
85 , m_complexData(frame.m_FFTSize)
86 {
87 m_forwardContext = contextForSize(m_FFTSize, DFT_R2C);
88 m_inverseContext = contextForSize(m_FFTSize, IDFT_C2R);
89
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);
94 }
95
initialize()96 void FFTFrame::initialize()
97 {
98 }
99
cleanup()100 void FFTFrame::cleanup()
101 {
102 }
103
~FFTFrame()104 FFTFrame::~FFTFrame()
105 {
106 av_rdft_end(m_forwardContext);
107 av_rdft_end(m_inverseContext);
108 }
109
doFFT(const float * data)110 void FFTFrame::doFFT(const float* data)
111 {
112 // Copy since processing is in-place.
113 float* p = m_complexData.data();
114 memcpy(p, data, sizeof(float) * m_FFTSize);
115
116 // Compute Forward transform.
117 av_rdft_calc(m_forwardContext, p);
118
119 // De-interleave to separate real and complex arrays.
120 int len = m_FFTSize / 2;
121
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];
130 }
131 }
132
doInverseFFT(float * data)133 void FFTFrame::doInverseFFT(float* data)
134 {
135 // Prepare interleaved data.
136 float* interleavedData = getUpToDateComplexData();
137
138 // Compute inverse transform.
139 av_rdft_calc(m_inverseContext, interleavedData);
140
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);
146 }
147
getUpToDateComplexData()148 float* FFTFrame::getUpToDateComplexData()
149 {
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];
160 }
161 return const_cast<float*>(m_complexData.data());
162 }
163
contextForSize(unsigned fftSize,int trans)164 RDFTContext* FFTFrame::contextForSize(unsigned fftSize, int trans)
165 {
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.
169 ASSERT(fftSize);
170 int pow2size = static_cast<int>(log2(fftSize));
171 ASSERT(pow2size < kMaxFFTPow2Size);
172
173 RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans);
174 return context;
175 }
176
177 } // namespace blink
178
179 #endif // USE(WEBAUDIO_FFMPEG)
180
181 #endif // ENABLE(WEB_AUDIO)
182