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 "core/platform/audio/FFTFrame.h" 36 37 #include "core/platform/audio/VectorMath.h" 38 39 extern "C" { 40 #include <libavcodec/avfft.h> 41 } 42 43 #include "wtf/MathExtras.h" 44 45 namespace { 46 47 struct FFTComplexProxy { 48 int16_t re; 49 int16_t im; 50 }; 51 52 struct FFTContextProxy { 53 int nbits; 54 int inverse; 55 uint16_t* revtab; 56 FFTComplexProxy* tmpBuf; 57 int mdctSize; 58 int mdctBits; 59 void* tcos; 60 void* tsin; 61 void (*fftPermute)(); 62 void (*fftCalc)(); 63 void (*imdctCalc)(); 64 void (*imdctHalf)(); 65 void (*mdctCalc)(); 66 void (*mdctCalcw)(); 67 int fftPermutation; 68 int mdctPermutation; 69 }; 70 71 struct RDFTContextProxy { 72 int nbits; 73 int inverse; 74 int signConvention; 75 const void* tcos; 76 const void* tsin; 77 FFTContextProxy fft; 78 void (*rdft_calc)(); 79 }; 80 81 } 82 83 namespace WebCore { 84 85 const int kMaxFFTPow2Size = 24; 86 87 // Normal constructor: allocates for a given fftSize. 88 FFTFrame::FFTFrame(unsigned fftSize) 89 : m_FFTSize(fftSize) 90 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize))) 91 , m_forwardContext(0) 92 , m_inverseContext(0) 93 , m_complexData(fftSize) 94 , m_realData(fftSize / 2) 95 , m_imagData(fftSize / 2) 96 { 97 // We only allow power of two. 98 ASSERT(1UL << m_log2FFTSize == m_FFTSize); 99 100 m_forwardContext = contextForSize(fftSize, DFT_R2C); 101 m_inverseContext = contextForSize(fftSize, IDFT_C2R); 102 } 103 104 // Creates a blank/empty frame (interpolate() must later be called). 105 FFTFrame::FFTFrame() 106 : m_FFTSize(0) 107 , m_log2FFTSize(0) 108 , m_forwardContext(0) 109 , m_inverseContext(0) 110 { 111 } 112 113 // Copy constructor. 114 FFTFrame::FFTFrame(const FFTFrame& frame) 115 : m_FFTSize(frame.m_FFTSize) 116 , m_log2FFTSize(frame.m_log2FFTSize) 117 , m_forwardContext(0) 118 , m_inverseContext(0) 119 , m_complexData(frame.m_FFTSize) 120 , m_realData(frame.m_FFTSize / 2) 121 , m_imagData(frame.m_FFTSize / 2) 122 { 123 m_forwardContext = contextForSize(m_FFTSize, DFT_R2C); 124 m_inverseContext = contextForSize(m_FFTSize, IDFT_C2R); 125 126 // Copy/setup frame data. 127 unsigned nbytes = sizeof(float) * (m_FFTSize / 2); 128 memcpy(realData(), frame.realData(), nbytes); 129 memcpy(imagData(), frame.imagData(), nbytes); 130 } 131 132 void FFTFrame::initialize() 133 { 134 } 135 136 void FFTFrame::cleanup() 137 { 138 } 139 140 FFTFrame::~FFTFrame() 141 { 142 av_rdft_end(m_forwardContext); 143 av_rdft_end(m_inverseContext); 144 } 145 146 void FFTFrame::multiply(const FFTFrame& frame) 147 { 148 FFTFrame& frame1 = *this; 149 FFTFrame& frame2 = const_cast<FFTFrame&>(frame); 150 151 float* realP1 = frame1.realData(); 152 float* imagP1 = frame1.imagData(); 153 const float* realP2 = frame2.realData(); 154 const float* imagP2 = frame2.imagData(); 155 156 unsigned halfSize = fftSize() / 2; 157 float real0 = realP1[0]; 158 float imag0 = imagP1[0]; 159 160 VectorMath::zvmul(realP1, imagP1, realP2, imagP2, realP1, imagP1, halfSize); 161 162 // Multiply the packed DC/nyquist component 163 realP1[0] = real0 * realP2[0]; 164 imagP1[0] = imag0 * imagP2[0]; 165 166 // Scale accounts the peculiar scaling of vecLib on the Mac. 167 // This ensures the right scaling all the way back to inverse FFT. 168 // FIXME: if we change the scaling on the Mac then this scale 169 // factor will need to change too. 170 float scale = 0.5f; 171 172 VectorMath::vsmul(realP1, 1, &scale, realP1, 1, halfSize); 173 VectorMath::vsmul(imagP1, 1, &scale, imagP1, 1, halfSize); 174 } 175 176 void FFTFrame::doFFT(const float* data) 177 { 178 // Copy since processing is in-place. 179 float* p = m_complexData.data(); 180 memcpy(p, data, sizeof(float) * m_FFTSize); 181 182 // Compute Forward transform. 183 av_rdft_calc(m_forwardContext, p); 184 185 // De-interleave to separate real and complex arrays. 186 int len = m_FFTSize / 2; 187 188 // FIXME: see above comment in multiply() about scaling. 189 const float scale = 2.0f; 190 191 for (int i = 0; i < len; ++i) { 192 int baseComplexIndex = 2 * i; 193 // m_realData[0] is the DC component and m_imagData[0] is the nyquist component 194 // since the interleaved complex data is packed. 195 m_realData[i] = scale * p[baseComplexIndex]; 196 m_imagData[i] = scale * p[baseComplexIndex + 1]; 197 } 198 } 199 200 void FFTFrame::doInverseFFT(float* data) 201 { 202 // Prepare interleaved data. 203 float* interleavedData = getUpToDateComplexData(); 204 205 // Compute inverse transform. 206 av_rdft_calc(m_inverseContext, interleavedData); 207 208 // Scale so that a forward then inverse FFT yields exactly the original data. 209 const float scale = 1.0 / m_FFTSize; 210 VectorMath::vsmul(interleavedData, 1, &scale, data, 1, m_FFTSize); 211 } 212 213 float* FFTFrame::realData() const 214 { 215 return const_cast<float*>(m_realData.data()); 216 } 217 218 float* FFTFrame::imagData() const 219 { 220 return const_cast<float*>(m_imagData.data()); 221 } 222 223 float* FFTFrame::getUpToDateComplexData() 224 { 225 // FIXME: if we can't completely get rid of this method, SSE 226 // optimization could be considered if it shows up hot on profiles. 227 int len = m_FFTSize / 2; 228 for (int i = 0; i < len; ++i) { 229 int baseComplexIndex = 2 * i; 230 m_complexData[baseComplexIndex] = m_realData[i]; 231 m_complexData[baseComplexIndex + 1] = m_imagData[i]; 232 } 233 return const_cast<float*>(m_complexData.data()); 234 } 235 236 RDFTContext* FFTFrame::contextForSize(unsigned fftSize, int trans) 237 { 238 // FIXME: This is non-optimal. Ideally, we'd like to share the contexts for FFTFrames of the same size. 239 // But FFmpeg's RDFT uses a scratch buffer inside the context and so they are not thread-safe. 240 // We could improve this by sharing the FFTFrames on a per-thread basis. 241 ASSERT(fftSize); 242 int pow2size = static_cast<int>(log2(fftSize)); 243 ASSERT(pow2size < kMaxFFTPow2Size); 244 245 RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans); 246 return context; 247 } 248 249 } // namespace WebCore 250 251 #endif // !OS(DARWIN) && USE(WEBAUDIO_FFMPEG) 252 253 #endif // ENABLE(WEB_AUDIO) 254