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 "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