1 /* 2 * Copyright (C) 2013 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 33 #if ENABLE(WEB_AUDIO) 34 35 #include "core/platform/audio/DownSampler.h" 36 37 #include "wtf/MathExtras.h" 38 39 namespace WebCore { 40 41 DownSampler::DownSampler(size_t inputBlockSize) 42 : m_inputBlockSize(inputBlockSize) 43 , m_reducedKernel(DefaultKernelSize / 2) 44 , m_tempBuffer(inputBlockSize / 2) 45 , m_inputBuffer(inputBlockSize * 2) 46 , m_convolver(inputBlockSize / 2) // runs at 1/2 source sample-rate 47 { 48 initializeKernel(); 49 } 50 51 void DownSampler::initializeKernel() 52 { 53 // Blackman window parameters. 54 double alpha = 0.16; 55 double a0 = 0.5 * (1.0 - alpha); 56 double a1 = 0.5; 57 double a2 = 0.5 * alpha; 58 59 int n = DefaultKernelSize; 60 int halfSize = n / 2; 61 62 // Half-band filter. 63 double sincScaleFactor = 0.5; 64 65 // Compute only the odd terms because the even ones are zero, except 66 // right in the middle at halfSize, which is 0.5 and we'll handle specially during processing 67 // after doing the main convolution using m_reducedKernel. 68 for (int i = 1; i < n; i += 2) { 69 // Compute the sinc() with offset. 70 double s = sincScaleFactor * piDouble * (i - halfSize); 71 double sinc = !s ? 1.0 : sin(s) / s; 72 sinc *= sincScaleFactor; 73 74 // Compute Blackman window, matching the offset of the sinc(). 75 double x = static_cast<double>(i) / n; 76 double window = a0 - a1 * cos(2.0 * piDouble * x) + a2 * cos(4.0 * piDouble * x); 77 78 // Window the sinc() function. 79 // Then store only the odd terms in the kernel. 80 // In a sense, this is shifting forward in time by one sample-frame at the destination sample-rate. 81 m_reducedKernel[(i - 1) / 2] = sinc * window; 82 } 83 } 84 85 void DownSampler::process(const float* sourceP, float* destP, size_t sourceFramesToProcess) 86 { 87 bool isInputBlockSizeGood = sourceFramesToProcess == m_inputBlockSize; 88 ASSERT(isInputBlockSizeGood); 89 if (!isInputBlockSizeGood) 90 return; 91 92 size_t destFramesToProcess = sourceFramesToProcess / 2; 93 94 bool isTempBufferGood = destFramesToProcess == m_tempBuffer.size(); 95 ASSERT(isTempBufferGood); 96 if (!isTempBufferGood) 97 return; 98 99 bool isReducedKernelGood = m_reducedKernel.size() == DefaultKernelSize / 2; 100 ASSERT(isReducedKernelGood); 101 if (!isReducedKernelGood) 102 return; 103 104 size_t halfSize = DefaultKernelSize / 2; 105 106 // Copy source samples to 2nd half of input buffer. 107 bool isInputBufferGood = m_inputBuffer.size() == sourceFramesToProcess * 2 && halfSize <= sourceFramesToProcess; 108 ASSERT(isInputBufferGood); 109 if (!isInputBufferGood) 110 return; 111 112 float* inputP = m_inputBuffer.data() + sourceFramesToProcess; 113 memcpy(inputP, sourceP, sizeof(float) * sourceFramesToProcess); 114 115 // Copy the odd sample-frames from sourceP, delayed by one sample-frame (destination sample-rate) 116 // to match shifting forward in time in m_reducedKernel. 117 float* oddSamplesP = m_tempBuffer.data(); 118 for (unsigned i = 0; i < destFramesToProcess; ++i) 119 oddSamplesP[i] = *((inputP - 1) + i * 2); 120 121 // Actually process oddSamplesP with m_reducedKernel for efficiency. 122 // The theoretical kernel is double this size with 0 values for even terms (except center). 123 m_convolver.process(&m_reducedKernel, oddSamplesP, destP, destFramesToProcess); 124 125 // Now, account for the 0.5 term right in the middle of the kernel. 126 // This amounts to a delay-line of length halfSize (at the source sample-rate), 127 // scaled by 0.5. 128 129 // Sum into the destination. 130 for (unsigned i = 0; i < destFramesToProcess; ++i) 131 destP[i] += 0.5 * *((inputP - halfSize) + i * 2); 132 133 // Copy 2nd half of input buffer to 1st half. 134 memcpy(m_inputBuffer.data(), inputP, sizeof(float) * sourceFramesToProcess); 135 } 136 137 void DownSampler::reset() 138 { 139 m_convolver.reset(); 140 m_inputBuffer.zero(); 141 } 142 143 size_t DownSampler::latencyFrames() const 144 { 145 // Divide by two since this is a linear phase kernel and the delay is at the center of the kernel. 146 return m_reducedKernel.size() / 2; 147 } 148 149 } // namespace WebCore 150 151 #endif // ENABLE(WEB_AUDIO) 152