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 WebCore { 46 47 #if ASSERT_ENABLED 48 const int kMaxFFTPow2Size = 24; 49 #endif 50 51 // Normal constructor: allocates for a given fftSize. 52 FFTFrame::FFTFrame(unsigned fftSize) 53 : m_FFTSize(fftSize) 54 , m_log2FFTSize(static_cast<unsigned>(log2(fftSize))) 55 , m_forwardContext(0) 56 , m_inverseContext(0) 57 , m_complexData(fftSize) 58 , m_realData(fftSize / 2) 59 , m_imagData(fftSize / 2) 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). 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. 78 FFTFrame::FFTFrame(const FFTFrame& frame) 79 : m_FFTSize(frame.m_FFTSize) 80 , m_log2FFTSize(frame.m_log2FFTSize) 81 , m_forwardContext(0) 82 , m_inverseContext(0) 83 , m_complexData(frame.m_FFTSize) 84 , m_realData(frame.m_FFTSize / 2) 85 , m_imagData(frame.m_FFTSize / 2) 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 96 void FFTFrame::initialize() 97 { 98 } 99 100 void FFTFrame::cleanup() 101 { 102 } 103 104 FFTFrame::~FFTFrame() 105 { 106 av_rdft_end(m_forwardContext); 107 av_rdft_end(m_inverseContext); 108 } 109 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 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 148 float* FFTFrame::realData() const 149 { 150 return const_cast<float*>(m_realData.data()); 151 } 152 153 float* FFTFrame::imagData() const 154 { 155 return const_cast<float*>(m_imagData.data()); 156 } 157 158 float* FFTFrame::getUpToDateComplexData() 159 { 160 // FIXME: if we can't completely get rid of this method, SSE 161 // optimization could be considered if it shows up hot on profiles. 162 int len = m_FFTSize / 2; 163 const float* real = m_realData.data(); 164 const float* imag = m_imagData.data(); 165 float* c = m_complexData.data(); 166 for (int i = 0; i < len; ++i) { 167 int baseComplexIndex = 2 * i; 168 c[baseComplexIndex] = real[i]; 169 c[baseComplexIndex + 1] = imag[i]; 170 } 171 return const_cast<float*>(m_complexData.data()); 172 } 173 174 RDFTContext* FFTFrame::contextForSize(unsigned fftSize, int trans) 175 { 176 // FIXME: This is non-optimal. Ideally, we'd like to share the contexts for FFTFrames of the same size. 177 // But FFmpeg's RDFT uses a scratch buffer inside the context and so they are not thread-safe. 178 // We could improve this by sharing the FFTFrames on a per-thread basis. 179 ASSERT(fftSize); 180 int pow2size = static_cast<int>(log2(fftSize)); 181 ASSERT(pow2size < kMaxFFTPow2Size); 182 183 RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans); 184 return context; 185 } 186 187 } // namespace WebCore 188 189 #endif // USE(WEBAUDIO_FFMPEG) 190 191 #endif // ENABLE(WEB_AUDIO) 192