diff options
Diffstat (limited to 'Source/WebCore/platform/audio')
-rw-r--r-- | Source/WebCore/platform/audio/AudioBus.cpp | 87 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/AudioBus.h | 12 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/HRTFKernel.cpp | 13 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/SincResampler.cpp | 342 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/SincResampler.h | 79 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp | 28 | ||||
-rw-r--r-- | Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp | 4 |
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 |