summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/audio/SincResampler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/audio/SincResampler.cpp')
-rw-r--r--Source/WebCore/platform/audio/SincResampler.cpp342
1 files changed, 342 insertions, 0 deletions
diff --git a/Source/WebCore/platform/audio/SincResampler.cpp b/Source/WebCore/platform/audio/SincResampler.cpp
new file mode 100644
index 0000000..e6f34b4
--- /dev/null
+++ b/Source/WebCore/platform/audio/SincResampler.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "SincResampler.h"
+
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+// Input buffer layout, dividing the total buffer into regions (r0 - r5):
+//
+// |----------------|----------------------------------------------------------------|----------------|
+//
+// blockSize + kernelSize / 2
+// <-------------------------------------------------------------------------------->
+// r0
+//
+// kernelSize / 2 kernelSize / 2 kernelSize / 2 kernelSize / 2
+// <---------------> <---------------> <---------------> <--------------->
+// r1 r2 r3 r4
+//
+// blockSize
+// <-------------------------------------------------------------->
+// r5
+
+// The Algorithm:
+//
+// 1) Consume input frames into r0 (r1 is zero-initialized).
+// 2) Position kernel centered at start of r0 (r2) and generate output frames until kernel is centered at start of r4.
+// or we've finished generating all the output frames.
+// 3) Copy r3 to r1 and r4 to r2.
+// 4) Consume input frames into r5 (zero-pad if we run out of input).
+// 5) Goto (2) until all of input is consumed.
+//
+// note: we're glossing over how the sub-sample handling works with m_virtualSourceIndex, etc.
+
+namespace WebCore {
+
+SincResampler::SincResampler(double scaleFactor, unsigned kernelSize, unsigned numberOfKernelOffsets)
+ : m_scaleFactor(scaleFactor)
+ , m_kernelSize(kernelSize)
+ , m_numberOfKernelOffsets(numberOfKernelOffsets)
+ , m_kernelStorage(m_kernelSize * (m_numberOfKernelOffsets + 1))
+ , m_virtualSourceIndex(0.0)
+ , m_blockSize(512)
+ , m_inputBuffer(m_blockSize + m_kernelSize) // See input buffer layout above.
+ , m_source(0)
+ , m_sourceFramesAvailable(0)
+{
+ initializeKernel();
+}
+
+void SincResampler::initializeKernel()
+{
+ // Blackman window parameters.
+ double alpha = 0.16;
+ double a0 = 0.5 * (1.0 - alpha);
+ double a1 = 0.5;
+ double a2 = 0.5 * alpha;
+
+ // sincScaleFactor is basically the normalized cutoff frequency of the low-pass filter.
+ double sincScaleFactor = m_scaleFactor > 1.0 ? 1.0 / m_scaleFactor : 1.0;
+
+ // The sinc function is an idealized brick-wall filter, but since we're windowing it the
+ // transition from pass to stop does not happen right away. So we should adjust the
+ // lowpass filter cutoff slightly downward to avoid some aliasing at the very high-end.
+ // FIXME: this value is empirical and to be more exact should vary depending on m_kernelSize.
+ sincScaleFactor *= 0.9;
+
+ int n = m_kernelSize;
+ int halfSize = n / 2;
+
+ // Generates a set of windowed sinc() kernels.
+ // We generate a range of sub-sample offsets from 0.0 to 1.0.
+ for (unsigned offsetIndex = 0; offsetIndex <= m_numberOfKernelOffsets; ++offsetIndex) {
+ double subsampleOffset = static_cast<double>(offsetIndex) / m_numberOfKernelOffsets;
+
+ for (int i = 0; i < n; ++i) {
+ // Compute the sinc() with offset.
+ double s = sincScaleFactor * piDouble * (i - halfSize - subsampleOffset);
+ double sinc = !s ? 1.0 : sin(s) / s;
+ sinc *= sincScaleFactor;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ double x = (i - subsampleOffset) / n;
+ double window = a0 - a1 * cos(2.0 * piDouble * x) + a2 * cos(4.0 * piDouble * x);
+
+ // Window the sinc() function and store at the correct offset.
+ m_kernelStorage[i + offsetIndex * m_kernelSize] = sinc * window;
+ }
+ }
+}
+
+void SincResampler::consumeSource(float* buffer, unsigned numberOfSourceFrames)
+{
+ ASSERT(m_source);
+ if (!m_source)
+ return;
+
+ // Clamp to number of frames available and zero-pad.
+ unsigned framesToCopy = min(m_sourceFramesAvailable, numberOfSourceFrames);
+ memcpy(buffer, m_source, sizeof(float) * framesToCopy);
+
+ // Zero-pad if necessary.
+ if (framesToCopy < numberOfSourceFrames)
+ memset(buffer + framesToCopy, 0, sizeof(float) * (numberOfSourceFrames - framesToCopy));
+
+ m_sourceFramesAvailable -= framesToCopy;
+ m_source += numberOfSourceFrames;
+}
+
+void SincResampler::process(float* source, float* destination, unsigned numberOfSourceFrames)
+{
+ ASSERT(m_blockSize > m_kernelSize);
+ ASSERT(m_inputBuffer.size() >= m_blockSize + m_kernelSize);
+ ASSERT(!(m_kernelSize % 2));
+
+ // Setup various region pointers in the buffer (see diagram above).
+ float* r0 = m_inputBuffer.data() + m_kernelSize / 2;
+ float* r1 = m_inputBuffer.data();
+ float* r2 = r0;
+ float* r3 = r0 + m_blockSize - m_kernelSize / 2;
+ float* r4 = r0 + m_blockSize;
+ float* r5 = r0 + m_kernelSize / 2;
+
+ m_source = source;
+ m_sourceFramesAvailable = numberOfSourceFrames;
+
+ unsigned numberOfDestinationFrames = static_cast<unsigned>(numberOfSourceFrames / m_scaleFactor);
+
+ // Step (1)
+ // Prime the input buffer.
+ consumeSource(r0, m_blockSize + m_kernelSize / 2);
+
+ // Step (2)
+ m_virtualSourceIndex = 0;
+
+ while (numberOfDestinationFrames) {
+ while (m_virtualSourceIndex < m_blockSize) {
+ // m_virtualSourceIndex lies in between two kernel offsets so figure out what they are.
+ int sourceIndexI = static_cast<int>(m_virtualSourceIndex);
+ double subsampleRemainder = m_virtualSourceIndex - sourceIndexI;
+
+ double virtualOffsetIndex = subsampleRemainder * m_numberOfKernelOffsets;
+ int offsetIndex = static_cast<int>(virtualOffsetIndex);
+
+ float* k1 = m_kernelStorage.data() + offsetIndex * m_kernelSize;
+ float* k2 = k1 + m_kernelSize;
+
+ // Initialize input pointer based on quantized m_virtualSourceIndex.
+ float* inputP = r1 + sourceIndexI;
+
+ // We'll compute "convolutions" for the two kernels which straddle m_virtualSourceIndex
+ float sum1 = 0;
+ float sum2 = 0;
+
+ // Figure out how much to weight each kernel's "convolution".
+ double kernelInterpolationFactor = virtualOffsetIndex - offsetIndex;
+
+ // Generate a single output sample.
+ int n = m_kernelSize;
+
+ // FIXME: add SIMD optimizations for the following. The scalar code-path can probably also be optimized better.
+
+#define CONVOLVE_ONE_SAMPLE \
+ input = *inputP++; \
+ sum1 += input * *k1; \
+ sum2 += input * *k2; \
+ ++k1; \
+ ++k2;
+
+ {
+ float input;
+
+ // Optimize size 32 and size 64 kernels by unrolling the while loop.
+ // A 20 - 30% speed improvement was measured in some cases by using this approach.
+
+ if (n == 32) {
+ CONVOLVE_ONE_SAMPLE // 1
+ CONVOLVE_ONE_SAMPLE // 2
+ CONVOLVE_ONE_SAMPLE // 3
+ CONVOLVE_ONE_SAMPLE // 4
+ CONVOLVE_ONE_SAMPLE // 5
+ CONVOLVE_ONE_SAMPLE // 6
+ CONVOLVE_ONE_SAMPLE // 7
+ CONVOLVE_ONE_SAMPLE // 8
+ CONVOLVE_ONE_SAMPLE // 9
+ CONVOLVE_ONE_SAMPLE // 10
+ CONVOLVE_ONE_SAMPLE // 11
+ CONVOLVE_ONE_SAMPLE // 12
+ CONVOLVE_ONE_SAMPLE // 13
+ CONVOLVE_ONE_SAMPLE // 14
+ CONVOLVE_ONE_SAMPLE // 15
+ CONVOLVE_ONE_SAMPLE // 16
+ CONVOLVE_ONE_SAMPLE // 17
+ CONVOLVE_ONE_SAMPLE // 18
+ CONVOLVE_ONE_SAMPLE // 19
+ CONVOLVE_ONE_SAMPLE // 20
+ CONVOLVE_ONE_SAMPLE // 21
+ CONVOLVE_ONE_SAMPLE // 22
+ CONVOLVE_ONE_SAMPLE // 23
+ CONVOLVE_ONE_SAMPLE // 24
+ CONVOLVE_ONE_SAMPLE // 25
+ CONVOLVE_ONE_SAMPLE // 26
+ CONVOLVE_ONE_SAMPLE // 27
+ CONVOLVE_ONE_SAMPLE // 28
+ CONVOLVE_ONE_SAMPLE // 29
+ CONVOLVE_ONE_SAMPLE // 30
+ CONVOLVE_ONE_SAMPLE // 31
+ CONVOLVE_ONE_SAMPLE // 32
+ } else if (n == 64) {
+ CONVOLVE_ONE_SAMPLE // 1
+ CONVOLVE_ONE_SAMPLE // 2
+ CONVOLVE_ONE_SAMPLE // 3
+ CONVOLVE_ONE_SAMPLE // 4
+ CONVOLVE_ONE_SAMPLE // 5
+ CONVOLVE_ONE_SAMPLE // 6
+ CONVOLVE_ONE_SAMPLE // 7
+ CONVOLVE_ONE_SAMPLE // 8
+ CONVOLVE_ONE_SAMPLE // 9
+ CONVOLVE_ONE_SAMPLE // 10
+ CONVOLVE_ONE_SAMPLE // 11
+ CONVOLVE_ONE_SAMPLE // 12
+ CONVOLVE_ONE_SAMPLE // 13
+ CONVOLVE_ONE_SAMPLE // 14
+ CONVOLVE_ONE_SAMPLE // 15
+ CONVOLVE_ONE_SAMPLE // 16
+ CONVOLVE_ONE_SAMPLE // 17
+ CONVOLVE_ONE_SAMPLE // 18
+ CONVOLVE_ONE_SAMPLE // 19
+ CONVOLVE_ONE_SAMPLE // 20
+ CONVOLVE_ONE_SAMPLE // 21
+ CONVOLVE_ONE_SAMPLE // 22
+ CONVOLVE_ONE_SAMPLE // 23
+ CONVOLVE_ONE_SAMPLE // 24
+ CONVOLVE_ONE_SAMPLE // 25
+ CONVOLVE_ONE_SAMPLE // 26
+ CONVOLVE_ONE_SAMPLE // 27
+ CONVOLVE_ONE_SAMPLE // 28
+ CONVOLVE_ONE_SAMPLE // 29
+ CONVOLVE_ONE_SAMPLE // 30
+ CONVOLVE_ONE_SAMPLE // 31
+ CONVOLVE_ONE_SAMPLE // 32
+ CONVOLVE_ONE_SAMPLE // 33
+ CONVOLVE_ONE_SAMPLE // 34
+ CONVOLVE_ONE_SAMPLE // 35
+ CONVOLVE_ONE_SAMPLE // 36
+ CONVOLVE_ONE_SAMPLE // 37
+ CONVOLVE_ONE_SAMPLE // 38
+ CONVOLVE_ONE_SAMPLE // 39
+ CONVOLVE_ONE_SAMPLE // 40
+ CONVOLVE_ONE_SAMPLE // 41
+ CONVOLVE_ONE_SAMPLE // 42
+ CONVOLVE_ONE_SAMPLE // 43
+ CONVOLVE_ONE_SAMPLE // 44
+ CONVOLVE_ONE_SAMPLE // 45
+ CONVOLVE_ONE_SAMPLE // 46
+ CONVOLVE_ONE_SAMPLE // 47
+ CONVOLVE_ONE_SAMPLE // 48
+ CONVOLVE_ONE_SAMPLE // 49
+ CONVOLVE_ONE_SAMPLE // 50
+ CONVOLVE_ONE_SAMPLE // 51
+ CONVOLVE_ONE_SAMPLE // 52
+ CONVOLVE_ONE_SAMPLE // 53
+ CONVOLVE_ONE_SAMPLE // 54
+ CONVOLVE_ONE_SAMPLE // 55
+ CONVOLVE_ONE_SAMPLE // 56
+ CONVOLVE_ONE_SAMPLE // 57
+ CONVOLVE_ONE_SAMPLE // 58
+ CONVOLVE_ONE_SAMPLE // 59
+ CONVOLVE_ONE_SAMPLE // 60
+ CONVOLVE_ONE_SAMPLE // 61
+ CONVOLVE_ONE_SAMPLE // 62
+ CONVOLVE_ONE_SAMPLE // 63
+ CONVOLVE_ONE_SAMPLE // 64
+ } else {
+ while (n--) {
+ // Non-optimized using actual while loop.
+ CONVOLVE_ONE_SAMPLE
+ }
+ }
+ }
+
+ // Linearly interpolate the two "convolutions".
+ double result = (1.0 - kernelInterpolationFactor) * sum1 + kernelInterpolationFactor * sum2;
+
+ *destination++ = result;
+
+ --numberOfDestinationFrames;
+ if (!numberOfDestinationFrames)
+ return;
+
+ // Advance the virtual index.
+ m_virtualSourceIndex += m_scaleFactor;
+ }
+
+ // Wrap back around to the start.
+ m_virtualSourceIndex -= m_blockSize;
+
+ // Step (3) Copy r3 to r1 and r4 to r2.
+ // This wraps the last input frames back to the start of the buffer.
+ memcpy(r1, r3, sizeof(float) * (m_kernelSize / 2));
+ memcpy(r2, r4, sizeof(float) * (m_kernelSize / 2));
+
+ // Step (4)
+ // Refresh the buffer with more input.
+ consumeSource(r5, m_blockSize);
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)