Home | History | Annotate | Download | only in audio
      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  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 
     31 #if ENABLE(WEB_AUDIO)
     32 
     33 #include "SincResampler.h"
     34 
     35 #include <wtf/MathExtras.h>
     36 
     37 using namespace std;
     38 
     39 // Input buffer layout, dividing the total buffer into regions (r0 - r5):
     40 //
     41 // |----------------|----------------------------------------------------------------|----------------|
     42 //
     43 //                                              blockSize + kernelSize / 2
     44 //                   <-------------------------------------------------------------------------------->
     45 //                                                  r0
     46 //
     47 //   kernelSize / 2   kernelSize / 2                                 kernelSize / 2     kernelSize / 2
     48 // <---------------> <--------------->                              <---------------> <--------------->
     49 //         r1                r2                                             r3                r4
     50 //
     51 //                                              blockSize
     52 //                                     <-------------------------------------------------------------->
     53 //                                                  r5
     54 
     55 // The Algorithm:
     56 //
     57 // 1) Consume input frames into r0 (r1 is zero-initialized).
     58 // 2) Position kernel centered at start of r0 (r2) and generate output frames until kernel is centered at start of r4.
     59 //    or we've finished generating all the output frames.
     60 // 3) Copy r3 to r1 and r4 to r2.
     61 // 4) Consume input frames into r5 (zero-pad if we run out of input).
     62 // 5) Goto (2) until all of input is consumed.
     63 //
     64 // note: we're glossing over how the sub-sample handling works with m_virtualSourceIndex, etc.
     65 
     66 namespace WebCore {
     67 
     68 SincResampler::SincResampler(double scaleFactor, unsigned kernelSize, unsigned numberOfKernelOffsets)
     69     : m_scaleFactor(scaleFactor)
     70     , m_kernelSize(kernelSize)
     71     , m_numberOfKernelOffsets(numberOfKernelOffsets)
     72     , m_kernelStorage(m_kernelSize * (m_numberOfKernelOffsets + 1))
     73     , m_virtualSourceIndex(0.0)
     74     , m_blockSize(512)
     75     , m_inputBuffer(m_blockSize + m_kernelSize) // See input buffer layout above.
     76     , m_source(0)
     77     , m_sourceFramesAvailable(0)
     78 {
     79     initializeKernel();
     80 }
     81 
     82 void SincResampler::initializeKernel()
     83 {
     84     // Blackman window parameters.
     85     double alpha = 0.16;
     86     double a0 = 0.5 * (1.0 - alpha);
     87     double a1 = 0.5;
     88     double a2 = 0.5 * alpha;
     89 
     90     // sincScaleFactor is basically the normalized cutoff frequency of the low-pass filter.
     91     double sincScaleFactor = m_scaleFactor > 1.0 ? 1.0 / m_scaleFactor : 1.0;
     92 
     93     // The sinc function is an idealized brick-wall filter, but since we're windowing it the
     94     // transition from pass to stop does not happen right away. So we should adjust the
     95     // lowpass filter cutoff slightly downward to avoid some aliasing at the very high-end.
     96     // FIXME: this value is empirical and to be more exact should vary depending on m_kernelSize.
     97     sincScaleFactor *= 0.9;
     98 
     99     int n = m_kernelSize;
    100     int halfSize = n / 2;
    101 
    102     // Generates a set of windowed sinc() kernels.
    103     // We generate a range of sub-sample offsets from 0.0 to 1.0.
    104     for (unsigned offsetIndex = 0; offsetIndex <= m_numberOfKernelOffsets; ++offsetIndex) {
    105         double subsampleOffset = static_cast<double>(offsetIndex) / m_numberOfKernelOffsets;
    106 
    107         for (int i = 0; i < n; ++i) {
    108             // Compute the sinc() with offset.
    109             double s = sincScaleFactor * piDouble * (i - halfSize - subsampleOffset);
    110             double sinc = !s ? 1.0 : sin(s) / s;
    111             sinc *= sincScaleFactor;
    112 
    113             // Compute Blackman window, matching the offset of the sinc().
    114             double x = (i - subsampleOffset) / n;
    115             double window = a0 - a1 * cos(2.0 * piDouble * x) + a2 * cos(4.0 * piDouble * x);
    116 
    117             // Window the sinc() function and store at the correct offset.
    118             m_kernelStorage[i + offsetIndex * m_kernelSize] = sinc * window;
    119         }
    120     }
    121 }
    122 
    123 void SincResampler::consumeSource(float* buffer, unsigned numberOfSourceFrames)
    124 {
    125     ASSERT(m_source);
    126     if (!m_source)
    127         return;
    128 
    129     // Clamp to number of frames available and zero-pad.
    130     unsigned framesToCopy = min(m_sourceFramesAvailable, numberOfSourceFrames);
    131     memcpy(buffer, m_source, sizeof(float) * framesToCopy);
    132 
    133     // Zero-pad if necessary.
    134     if (framesToCopy < numberOfSourceFrames)
    135         memset(buffer + framesToCopy, 0, sizeof(float) * (numberOfSourceFrames - framesToCopy));
    136 
    137     m_sourceFramesAvailable -= framesToCopy;
    138     m_source += numberOfSourceFrames;
    139 }
    140 
    141 void SincResampler::process(float* source, float* destination, unsigned numberOfSourceFrames)
    142 {
    143     ASSERT(m_blockSize > m_kernelSize);
    144     ASSERT(m_inputBuffer.size() >= m_blockSize + m_kernelSize);
    145     ASSERT(!(m_kernelSize % 2));
    146 
    147     // Setup various region pointers in the buffer (see diagram above).
    148     float* r0 = m_inputBuffer.data() + m_kernelSize / 2;
    149     float* r1 = m_inputBuffer.data();
    150     float* r2 = r0;
    151     float* r3 = r0 + m_blockSize - m_kernelSize / 2;
    152     float* r4 = r0 + m_blockSize;
    153     float* r5 = r0 + m_kernelSize / 2;
    154 
    155     m_source = source;
    156     m_sourceFramesAvailable = numberOfSourceFrames;
    157 
    158     unsigned numberOfDestinationFrames = static_cast<unsigned>(numberOfSourceFrames / m_scaleFactor);
    159 
    160     // Step (1)
    161     // Prime the input buffer.
    162     consumeSource(r0, m_blockSize + m_kernelSize / 2);
    163 
    164     // Step (2)
    165     m_virtualSourceIndex = 0;
    166 
    167     while (numberOfDestinationFrames) {
    168         while (m_virtualSourceIndex < m_blockSize) {
    169             // m_virtualSourceIndex lies in between two kernel offsets so figure out what they are.
    170             int sourceIndexI = static_cast<int>(m_virtualSourceIndex);
    171             double subsampleRemainder = m_virtualSourceIndex - sourceIndexI;
    172 
    173             double virtualOffsetIndex = subsampleRemainder * m_numberOfKernelOffsets;
    174             int offsetIndex = static_cast<int>(virtualOffsetIndex);
    175 
    176             float* k1 = m_kernelStorage.data() + offsetIndex * m_kernelSize;
    177             float* k2 = k1 + m_kernelSize;
    178 
    179             // Initialize input pointer based on quantized m_virtualSourceIndex.
    180             float* inputP = r1 + sourceIndexI;
    181 
    182             // We'll compute "convolutions" for the two kernels which straddle m_virtualSourceIndex
    183             float sum1 = 0;
    184             float sum2 = 0;
    185 
    186             // Figure out how much to weight each kernel's "convolution".
    187             double kernelInterpolationFactor = virtualOffsetIndex - offsetIndex;
    188 
    189             // Generate a single output sample.
    190             int n = m_kernelSize;
    191 
    192             // FIXME: add SIMD optimizations for the following. The scalar code-path can probably also be optimized better.
    193 
    194 #define CONVOLVE_ONE_SAMPLE      \
    195             input = *inputP++;   \
    196             sum1 += input * *k1; \
    197             sum2 += input * *k2; \
    198             ++k1;                \
    199             ++k2;
    200 
    201             {
    202                 float input;
    203 
    204                 // Optimize size 32 and size 64 kernels by unrolling the while loop.
    205                 // A 20 - 30% speed improvement was measured in some cases by using this approach.
    206 
    207                 if (n == 32) {
    208                     CONVOLVE_ONE_SAMPLE // 1
    209                     CONVOLVE_ONE_SAMPLE // 2
    210                     CONVOLVE_ONE_SAMPLE // 3
    211                     CONVOLVE_ONE_SAMPLE // 4
    212                     CONVOLVE_ONE_SAMPLE // 5
    213                     CONVOLVE_ONE_SAMPLE // 6
    214                     CONVOLVE_ONE_SAMPLE // 7
    215                     CONVOLVE_ONE_SAMPLE // 8
    216                     CONVOLVE_ONE_SAMPLE // 9
    217                     CONVOLVE_ONE_SAMPLE // 10
    218                     CONVOLVE_ONE_SAMPLE // 11
    219                     CONVOLVE_ONE_SAMPLE // 12
    220                     CONVOLVE_ONE_SAMPLE // 13
    221                     CONVOLVE_ONE_SAMPLE // 14
    222                     CONVOLVE_ONE_SAMPLE // 15
    223                     CONVOLVE_ONE_SAMPLE // 16
    224                     CONVOLVE_ONE_SAMPLE // 17
    225                     CONVOLVE_ONE_SAMPLE // 18
    226                     CONVOLVE_ONE_SAMPLE // 19
    227                     CONVOLVE_ONE_SAMPLE // 20
    228                     CONVOLVE_ONE_SAMPLE // 21
    229                     CONVOLVE_ONE_SAMPLE // 22
    230                     CONVOLVE_ONE_SAMPLE // 23
    231                     CONVOLVE_ONE_SAMPLE // 24
    232                     CONVOLVE_ONE_SAMPLE // 25
    233                     CONVOLVE_ONE_SAMPLE // 26
    234                     CONVOLVE_ONE_SAMPLE // 27
    235                     CONVOLVE_ONE_SAMPLE // 28
    236                     CONVOLVE_ONE_SAMPLE // 29
    237                     CONVOLVE_ONE_SAMPLE // 30
    238                     CONVOLVE_ONE_SAMPLE // 31
    239                     CONVOLVE_ONE_SAMPLE // 32
    240                 } else if (n == 64) {
    241                     CONVOLVE_ONE_SAMPLE // 1
    242                     CONVOLVE_ONE_SAMPLE // 2
    243                     CONVOLVE_ONE_SAMPLE // 3
    244                     CONVOLVE_ONE_SAMPLE // 4
    245                     CONVOLVE_ONE_SAMPLE // 5
    246                     CONVOLVE_ONE_SAMPLE // 6
    247                     CONVOLVE_ONE_SAMPLE // 7
    248                     CONVOLVE_ONE_SAMPLE // 8
    249                     CONVOLVE_ONE_SAMPLE // 9
    250                     CONVOLVE_ONE_SAMPLE // 10
    251                     CONVOLVE_ONE_SAMPLE // 11
    252                     CONVOLVE_ONE_SAMPLE // 12
    253                     CONVOLVE_ONE_SAMPLE // 13
    254                     CONVOLVE_ONE_SAMPLE // 14
    255                     CONVOLVE_ONE_SAMPLE // 15
    256                     CONVOLVE_ONE_SAMPLE // 16
    257                     CONVOLVE_ONE_SAMPLE // 17
    258                     CONVOLVE_ONE_SAMPLE // 18
    259                     CONVOLVE_ONE_SAMPLE // 19
    260                     CONVOLVE_ONE_SAMPLE // 20
    261                     CONVOLVE_ONE_SAMPLE // 21
    262                     CONVOLVE_ONE_SAMPLE // 22
    263                     CONVOLVE_ONE_SAMPLE // 23
    264                     CONVOLVE_ONE_SAMPLE // 24
    265                     CONVOLVE_ONE_SAMPLE // 25
    266                     CONVOLVE_ONE_SAMPLE // 26
    267                     CONVOLVE_ONE_SAMPLE // 27
    268                     CONVOLVE_ONE_SAMPLE // 28
    269                     CONVOLVE_ONE_SAMPLE // 29
    270                     CONVOLVE_ONE_SAMPLE // 30
    271                     CONVOLVE_ONE_SAMPLE // 31
    272                     CONVOLVE_ONE_SAMPLE // 32
    273                     CONVOLVE_ONE_SAMPLE // 33
    274                     CONVOLVE_ONE_SAMPLE // 34
    275                     CONVOLVE_ONE_SAMPLE // 35
    276                     CONVOLVE_ONE_SAMPLE // 36
    277                     CONVOLVE_ONE_SAMPLE // 37
    278                     CONVOLVE_ONE_SAMPLE // 38
    279                     CONVOLVE_ONE_SAMPLE // 39
    280                     CONVOLVE_ONE_SAMPLE // 40
    281                     CONVOLVE_ONE_SAMPLE // 41
    282                     CONVOLVE_ONE_SAMPLE // 42
    283                     CONVOLVE_ONE_SAMPLE // 43
    284                     CONVOLVE_ONE_SAMPLE // 44
    285                     CONVOLVE_ONE_SAMPLE // 45
    286                     CONVOLVE_ONE_SAMPLE // 46
    287                     CONVOLVE_ONE_SAMPLE // 47
    288                     CONVOLVE_ONE_SAMPLE // 48
    289                     CONVOLVE_ONE_SAMPLE // 49
    290                     CONVOLVE_ONE_SAMPLE // 50
    291                     CONVOLVE_ONE_SAMPLE // 51
    292                     CONVOLVE_ONE_SAMPLE // 52
    293                     CONVOLVE_ONE_SAMPLE // 53
    294                     CONVOLVE_ONE_SAMPLE // 54
    295                     CONVOLVE_ONE_SAMPLE // 55
    296                     CONVOLVE_ONE_SAMPLE // 56
    297                     CONVOLVE_ONE_SAMPLE // 57
    298                     CONVOLVE_ONE_SAMPLE // 58
    299                     CONVOLVE_ONE_SAMPLE // 59
    300                     CONVOLVE_ONE_SAMPLE // 60
    301                     CONVOLVE_ONE_SAMPLE // 61
    302                     CONVOLVE_ONE_SAMPLE // 62
    303                     CONVOLVE_ONE_SAMPLE // 63
    304                     CONVOLVE_ONE_SAMPLE // 64
    305                 } else {
    306                     while (n--) {
    307                         // Non-optimized using actual while loop.
    308                         CONVOLVE_ONE_SAMPLE
    309                     }
    310                 }
    311             }
    312 
    313             // Linearly interpolate the two "convolutions".
    314             double result = (1.0 - kernelInterpolationFactor) * sum1 + kernelInterpolationFactor * sum2;
    315 
    316             *destination++ = result;
    317 
    318             --numberOfDestinationFrames;
    319             if (!numberOfDestinationFrames)
    320                 return;
    321 
    322             // Advance the virtual index.
    323             m_virtualSourceIndex += m_scaleFactor;
    324         }
    325 
    326         // Wrap back around to the start.
    327         m_virtualSourceIndex -= m_blockSize;
    328 
    329         // Step (3) Copy r3 to r1 and r4 to r2.
    330         // This wraps the last input frames back to the start of the buffer.
    331         memcpy(r1, r3, sizeof(float) * (m_kernelSize / 2));
    332         memcpy(r2, r4, sizeof(float) * (m_kernelSize / 2));
    333 
    334         // Step (4)
    335         // Refresh the buffer with more input.
    336         consumeSource(r5, m_blockSize);
    337     }
    338 }
    339 
    340 } // namespace WebCore
    341 
    342 #endif // ENABLE(WEB_AUDIO)
    343