summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/audio
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-25 19:08:45 +0100
committerSteve Block <steveblock@google.com>2011-06-08 13:51:31 +0100
commit2bde8e466a4451c7319e3a072d118917957d6554 (patch)
tree28f4a1b869a513e565c7760d0e6a06e7cf1fe95a /Source/WebCore/platform/audio
parent6939c99b71d9372d14a0c74a772108052e8c48c8 (diff)
downloadexternal_webkit-2bde8e466a4451c7319e3a072d118917957d6554.zip
external_webkit-2bde8e466a4451c7319e3a072d118917957d6554.tar.gz
external_webkit-2bde8e466a4451c7319e3a072d118917957d6554.tar.bz2
Merge WebKit at r82507: Initial merge by git
Change-Id: I60ce9d780725b58b45e54165733a8ffee23b683e
Diffstat (limited to 'Source/WebCore/platform/audio')
-rw-r--r--Source/WebCore/platform/audio/AudioBus.cpp87
-rw-r--r--Source/WebCore/platform/audio/AudioBus.h12
-rw-r--r--Source/WebCore/platform/audio/HRTFKernel.cpp13
-rw-r--r--Source/WebCore/platform/audio/SincResampler.cpp342
-rw-r--r--Source/WebCore/platform/audio/SincResampler.h79
-rw-r--r--Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp28
-rw-r--r--Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp4
7 files changed, 547 insertions, 18 deletions
diff --git a/Source/WebCore/platform/audio/AudioBus.cpp b/Source/WebCore/platform/audio/AudioBus.cpp
index dd4746d..6f74471 100644
--- a/Source/WebCore/platform/audio/AudioBus.cpp
+++ b/Source/WebCore/platform/audio/AudioBus.cpp
@@ -32,6 +32,9 @@
#include "AudioBus.h"
+#if !PLATFORM(MAC)
+#include "SincResampler.h"
+#endif
#include "VectorMath.h"
#include <algorithm>
#include <assert.h>
@@ -360,6 +363,90 @@ void AudioBus::sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, d
processWithGainFrom(sourceBus, lastMixGain, targetGain, true);
}
+#if !PLATFORM(MAC)
+PassOwnPtr<AudioBus> AudioBus::createBySampleRateConverting(AudioBus* sourceBus, bool mixToMono, double newSampleRate)
+{
+ // sourceBus's sample-rate must be known.
+ ASSERT(sourceBus && sourceBus->sampleRate());
+ if (!sourceBus || !sourceBus->sampleRate())
+ return 0;
+
+ double sourceSampleRate = sourceBus->sampleRate();
+ double destinationSampleRate = newSampleRate;
+ unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
+
+ if (numberOfSourceChannels == 1)
+ mixToMono = false; // already mono
+
+ if (sourceSampleRate == destinationSampleRate) {
+ // No sample-rate conversion is necessary.
+ if (mixToMono)
+ return AudioBus::createByMixingToMono(sourceBus);
+
+ // Return exact copy.
+ return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
+ }
+
+ // First, mix to mono (if necessary) then sample-rate convert.
+ AudioBus* resamplerSourceBus;
+ OwnPtr<AudioBus> mixedMonoBus;
+ if (mixToMono) {
+ mixedMonoBus = AudioBus::createByMixingToMono(sourceBus);
+ resamplerSourceBus = mixedMonoBus.get();
+ } else {
+ // Directly resample without down-mixing.
+ resamplerSourceBus = sourceBus;
+ }
+
+ // Calculate destination length based on the sample-rates.
+ double sampleRateRatio = sourceSampleRate / destinationSampleRate;
+ int sourceLength = resamplerSourceBus->length();
+ int destinationLength = sourceLength / sampleRateRatio;
+
+ // Create destination bus with same number of channels.
+ unsigned numberOfDestinationChannels = resamplerSourceBus->numberOfChannels();
+ OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(numberOfDestinationChannels, destinationLength)));
+
+ // Sample-rate convert each channel.
+ for (unsigned i = 0; i < numberOfDestinationChannels; ++i) {
+ float* source = resamplerSourceBus->channel(i)->data();
+ float* destination = destinationBus->channel(i)->data();
+
+ SincResampler resampler(sampleRateRatio);
+ resampler.process(source, destination, sourceLength);
+ }
+
+ return destinationBus.release();
+}
+#endif // !PLATFORM(MAC)
+
+PassOwnPtr<AudioBus> AudioBus::createByMixingToMono(AudioBus* sourceBus)
+{
+ switch (sourceBus->numberOfChannels()) {
+ case 1:
+ // Simply create an exact copy.
+ return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
+ case 2:
+ {
+ unsigned n = sourceBus->length();
+ OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(1, n)));
+
+ float* sourceL = sourceBus->channel(0)->data();
+ float* sourceR = sourceBus->channel(1)->data();
+ float* destination = destinationBus->channel(0)->data();
+
+ // Do the mono mixdown.
+ for (unsigned i = 0; i < n; ++i)
+ destination[i] = 0.5 * (sourceL[i] + sourceR[i]);
+
+ return destinationBus.release();
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
} // WebCore
#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/platform/audio/AudioBus.h b/Source/WebCore/platform/audio/AudioBus.h
index 888f6bf..1943c0d 100644
--- a/Source/WebCore/platform/audio/AudioBus.h
+++ b/Source/WebCore/platform/audio/AudioBus.h
@@ -89,6 +89,18 @@ public:
// 0 may be returned if the range does not fit in the sourceBuffer
static PassOwnPtr<AudioBus> createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame);
+
+#if !PLATFORM(MAC)
+ // Creates a new AudioBus by sample-rate converting sourceBus to the newSampleRate.
+ // setSampleRate() must have been previously called on sourceBus.
+ // Note: sample-rate conversion is already handled in the file-reading code for the mac port, so we don't need this.
+ static PassOwnPtr<AudioBus> createBySampleRateConverting(AudioBus* sourceBus, bool mixToMono, double newSampleRate);
+#endif
+
+ // Creates a new AudioBus by mixing all the channels down to mono.
+ // If sourceBus is already mono, then the returned AudioBus will simply be a copy.
+ static PassOwnPtr<AudioBus> createByMixingToMono(AudioBus* sourceBus);
+
// Scales all samples by the same amount.
void scale(double scale);
diff --git a/Source/WebCore/platform/audio/HRTFKernel.cpp b/Source/WebCore/platform/audio/HRTFKernel.cpp
index 22d4b12..9db35ba 100644
--- a/Source/WebCore/platform/audio/HRTFKernel.cpp
+++ b/Source/WebCore/platform/audio/HRTFKernel.cpp
@@ -45,17 +45,18 @@ namespace WebCore {
// This represents the initial delay before the most energetic part of the impulse response.
// The sample-frame delay is removed from the impulseP impulse response, and this value is returned.
// the length of the passed in AudioChannel must be a power of 2.
-static double extractAverageGroupDelay(AudioChannel* channel)
+static double extractAverageGroupDelay(AudioChannel* channel, size_t analysisFFTSize)
{
ASSERT(channel);
float* impulseP = channel->data();
- size_t length = channel->length();
- // Check that length is power-of-2;
- ASSERT(1UL << static_cast<unsigned>(log2(length)) == length);
+ ASSERT(channel->length() >= analysisFFTSize);
+
+ // Check for power-of-2.
+ ASSERT(1UL << static_cast<unsigned>(log2(analysisFFTSize)) == analysisFFTSize);
- FFTFrame estimationFrame(length);
+ FFTFrame estimationFrame(analysisFFTSize);
estimationFrame.doFFT(impulseP);
double frameDelay = estimationFrame.extractAverageGroupDelay();
@@ -71,7 +72,7 @@ HRTFKernel::HRTFKernel(AudioChannel* channel, size_t fftSize, double sampleRate,
ASSERT(channel);
// Determine the leading delay (average group delay) for the response.
- m_frameDelay = extractAverageGroupDelay(channel);
+ m_frameDelay = extractAverageGroupDelay(channel, fftSize / 2);
float* impulseResponse = channel->data();
size_t responseLength = channel->length();
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)
diff --git a/Source/WebCore/platform/audio/SincResampler.h b/Source/WebCore/platform/audio/SincResampler.h
new file mode 100644
index 0000000..92adb95
--- /dev/null
+++ b/Source/WebCore/platform/audio/SincResampler.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef SincResampler_h
+#define SincResampler_h
+
+#include "AudioArray.h"
+
+namespace WebCore {
+
+// SincResampler is a high-quality sample-rate converter.
+
+class SincResampler {
+public:
+ // scaleFactor == sourceSampleRate / destinationSampleRate
+ // kernelSize can be adjusted for quality (higher is better)
+ // numberOfKernelOffsets is used for interpolation and is the number of sub-sample kernel shifts.
+ SincResampler(double scaleFactor, unsigned kernelSize = 64, unsigned numberOfKernelOffsets = 32);
+
+ // Processes numberOfSourceFrames from source to produce numberOfSourceFrames / scaleFactor frames in destination.
+ void process(float* source, float* destination, unsigned numberOfSourceFrames);
+
+ // FIXME: we can add a process() method which takes an input source callback function for streaming applications
+ // where the entire input buffer is not all available.
+
+protected:
+ void initializeKernel();
+ void consumeSource(float* buffer, unsigned numberOfSourceFrames);
+
+ double m_scaleFactor;
+ unsigned m_kernelSize;
+ unsigned m_numberOfKernelOffsets;
+
+ // m_kernelStorage has m_numberOfKernelOffsets kernels back-to-back, each of size m_kernelSize.
+ // The kernel offsets are sub-sample shifts of a windowed sinc() shifted from 0.0 to 1.0 sample.
+ AudioFloatArray m_kernelStorage;
+
+ // m_virtualSourceIndex is an index on the source input buffer with sub-sample precision.
+ // It must be double precision to avoid drift.
+ double m_virtualSourceIndex;
+
+ // This is the number of destination frames we generate per processing pass on the buffer.
+ unsigned m_blockSize;
+
+ // Source is copied into this buffer for each processing pass.
+ AudioFloatArray m_inputBuffer;
+
+ float* m_source;
+ unsigned m_sourceFramesAvailable;
+};
+
+} // namespace WebCore
+
+#endif // SincResampler_h
diff --git a/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp
index de44b1c..f34dc77 100644
--- a/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp
+++ b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp
@@ -36,22 +36,30 @@ namespace WebCore {
PassOwnPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, double sampleRate)
{
- return PlatformBridge::loadPlatformAudioResource(name, sampleRate);
+ // FIXME: the sampleRate parameter is ignored. It should be removed from the API.
+ OwnPtr<AudioBus> audioBus = PlatformBridge::loadPlatformAudioResource(name, sampleRate);
+ if (!audioBus.get())
+ return 0;
+
+ // If the bus is already at the requested sample-rate then return as is.
+ if (audioBus->sampleRate() == sampleRate)
+ return audioBus.release();
+
+ return AudioBus::createBySampleRateConverting(audioBus.get(), false, sampleRate);
}
PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate)
{
+ // FIXME: the sampleRate parameter is ignored. It should be removed from the API.
OwnPtr<AudioBus> audioBus = PlatformBridge::decodeAudioFileData(static_cast<const char*>(data), dataSize, sampleRate);
- if (audioBus.get() && audioBus->numberOfChannels() == 2 && mixToMono) {
- OwnPtr<AudioBus> monoAudioBus = adoptPtr(new AudioBus(1, audioBus->length()));
-
- // FIXME: AudioBus::copyFrom() should be able to do a downmix to mono.
- // for now simply copy the left channel.
- monoAudioBus->channel(0)->copyFrom(audioBus->channel(0));
- return monoAudioBus.release();
- }
+ if (!audioBus.get())
+ return 0;
+
+ // If the bus needs no conversion then return as is.
+ if ((!mixToMono || audioBus->numberOfChannels() == 1) && audioBus->sampleRate() == sampleRate)
+ return audioBus.release();
- return audioBus.release();
+ return AudioBus::createBySampleRateConverting(audioBus.get(), mixToMono, sampleRate);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp
index 523729f..d4ecaba 100644
--- a/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp
+++ b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp
@@ -51,8 +51,8 @@ double AudioDestination::hardwareSampleRate()
AudioDeviceID deviceID = kAudioDeviceUnknown;
UInt32 infoSize = sizeof(deviceID);
- AudioObjectPropertyAddress defaultInputDeviceAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultInputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID);
+ AudioObjectPropertyAddress defaultOutputDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID);
if (result)
return 0.0; // error