Home | History | Annotate | Download | only in ffmpeg
      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