2010-09-22 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Sep 2010 08:10:47 +0000 (08:10 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Sep 2010 08:10:47 +0000 (08:10 +0000)
        Reviewed by James Robinson.

        Add HRTFKernel files
        https://bugs.webkit.org/show_bug.cgi?id=45863

        No new tests since audio API is not yet implemented.

        * platform/audio/HRTFKernel.cpp: Added.
        (WebCore::extractAverageGroupDelay):
        (WebCore::HRTFKernel::HRTFKernel):
        (WebCore::HRTFKernel::createImpulseResponse):
        (WebCore::HRTFKernel::createInterpolatedKernel):
        * platform/audio/HRTFKernel.h: Added.
        (WebCore::HRTFKernel::create):
        (WebCore::HRTFKernel::fftFrame):
        (WebCore::HRTFKernel::fftSize):
        (WebCore::HRTFKernel::frameDelay):
        (WebCore::HRTFKernel::sampleRate):
        (WebCore::HRTFKernel::nyquist):
        (WebCore::HRTFKernel::HRTFKernel):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@68025 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/audio/HRTFKernel.cpp [new file with mode: 0644]
WebCore/platform/audio/HRTFKernel.h [new file with mode: 0644]

index 5088eb58981714e1a5d202bcbd3547bfa7535bca..1503f1d9caf9cf3a8a4fb3103a7ea83d1a11b026 100644 (file)
@@ -1,3 +1,26 @@
+2010-09-22  Chris Rogers  <crogers@google.com>
+
+        Reviewed by James Robinson.
+
+        Add HRTFKernel files
+        https://bugs.webkit.org/show_bug.cgi?id=45863
+
+        No new tests since audio API is not yet implemented.
+
+        * platform/audio/HRTFKernel.cpp: Added.
+        (WebCore::extractAverageGroupDelay):
+        (WebCore::HRTFKernel::HRTFKernel):
+        (WebCore::HRTFKernel::createImpulseResponse):
+        (WebCore::HRTFKernel::createInterpolatedKernel):
+        * platform/audio/HRTFKernel.h: Added.
+        (WebCore::HRTFKernel::create):
+        (WebCore::HRTFKernel::fftFrame):
+        (WebCore::HRTFKernel::fftSize):
+        (WebCore::HRTFKernel::frameDelay):
+        (WebCore::HRTFKernel::sampleRate):
+        (WebCore::HRTFKernel::nyquist):
+        (WebCore::HRTFKernel::HRTFKernel):
+
 2010-09-22  Kent Tamura  <tkent@chromium.org>
 
         Reviewed by Chris Fleizach.
 2010-09-22  Kent Tamura  <tkent@chromium.org>
 
         Reviewed by Chris Fleizach.
diff --git a/WebCore/platform/audio/HRTFKernel.cpp b/WebCore/platform/audio/HRTFKernel.cpp
new file mode 100644 (file)
index 0000000..852cdbf
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 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 "HRTFKernel.h"
+
+#include "AudioChannel.h"
+#include "Biquad.h"
+#include "FFTFrame.h"
+
+using namespace std;
+
+namespace WebCore {
+
+// Takes the input AudioChannel as an input impulse response and calculates the average group delay.
+// 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)
+{
+    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);
+
+    FFTFrame estimationFrame(length);
+    estimationFrame.doFFT(impulseP);
+
+    double frameDelay = estimationFrame.extractAverageGroupDelay();
+    estimationFrame.doInverseFFT(impulseP);
+
+    return frameDelay;
+}
+
+HRTFKernel::HRTFKernel(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost)
+    : m_frameDelay(0.0)
+    , m_sampleRate(sampleRate)
+{
+    ASSERT(channel);
+
+    // Determine the leading delay (average group delay) for the response.
+    m_frameDelay = extractAverageGroupDelay(channel);
+
+    float* impulseResponse = channel->data();
+    size_t responseLength = channel->length();
+
+    if (bassBoost) {
+        // Run through some post-processing to boost the bass a little -- the HRTF's seem to be a little bass-deficient.
+        // FIXME: this post-processing should have already been applied to the HRTF file resources.  Once the files are put into this form,
+        // then this code path can be removed along with the bassBoost parameter.
+        Biquad filter;
+        filter.setLowShelfParams(700.0 / nyquist(), 6.0); // boost 6dB at 700Hz
+        filter.process(impulseResponse, impulseResponse, responseLength);
+    }
+
+    // We need to truncate to fit into 1/2 the FFT size (with zero padding) in order to do proper convolution.
+    size_t truncatedResponseLength = min(responseLength, fftSize / 2); // truncate if necessary to max impulse response length allowed by FFT
+
+    // Quick fade-out (apply window) at truncation point
+    unsigned numberOfFadeOutFrames = static_cast<unsigned>(sampleRate / 4410); // 10 sample-frames @44.1KHz sample-rate
+    ASSERT(numberOfFadeOutFrames < truncatedResponseLength);
+    if (numberOfFadeOutFrames < truncatedResponseLength) {
+        for (unsigned i = truncatedResponseLength - numberOfFadeOutFrames; i < truncatedResponseLength; ++i) {
+            float x = 1.0f - static_cast<float>(i - (truncatedResponseLength - numberOfFadeOutFrames)) / numberOfFadeOutFrames;
+            impulseResponse[i] *= x;
+        }
+    }
+
+    m_fftFrame = adoptPtr(new FFTFrame(fftSize));
+    m_fftFrame->doPaddedFFT(impulseResponse, truncatedResponseLength);
+}
+
+PassOwnPtr<AudioChannel> HRTFKernel::createImpulseResponse()
+{
+    OwnPtr<AudioChannel> channel = adoptPtr(new AudioChannel(fftSize()));
+    FFTFrame fftFrame(*m_fftFrame);
+
+    // Add leading delay back in.
+    fftFrame.addConstantGroupDelay(m_frameDelay);
+    fftFrame.doInverseFFT(channel->data());
+
+    return channel.release();
+}
+
+// Interpolates two kernels with x: 0 -> 1 and returns the result.
+PassRefPtr<HRTFKernel> HRTFKernel::createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, double x)
+{
+    ASSERT(kernel1 && kernel2);
+    if (!kernel1 || !kernel2)
+        return 0;
+    ASSERT(x >= 0.0 && x < 1.0);
+    x = min(1.0, max(0.0, x));
+    
+    double sampleRate1 = kernel1->sampleRate();
+    double sampleRate2 = kernel2->sampleRate();
+    ASSERT(sampleRate1 == sampleRate2);
+    if (sampleRate1 != sampleRate2)
+        return 0;
+    
+    double frameDelay = (1.0 - x) * kernel1->frameDelay() + x * kernel2->frameDelay();
+    
+    OwnPtr<FFTFrame> interpolatedFrame = FFTFrame::createInterpolatedFrame(*kernel1->fftFrame(), *kernel2->fftFrame(), x);
+    return HRTFKernel::create(interpolatedFrame.release(), frameDelay, sampleRate1);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/HRTFKernel.h b/WebCore/platform/audio/HRTFKernel.h
new file mode 100644 (file)
index 0000000..572a085
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 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 HRTFKernel_h
+#define HRTFKernel_h
+
+#include "FFTFrame.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioChannel;
+    
+// HRTF stands for Head-Related Transfer Function.
+// HRTFKernel is a frequency-domain representation of an impulse-response used as part of the spatialized panning system.
+// For a given azimuth / elevation angle there will be one HRTFKernel for the left ear transfer function, and one for the right ear.
+// The leading delay (average group delay) for each impulse response is extracted:
+//      m_fftFrame is the frequency-domain representation of the impulse response with the delay removed
+//      m_frameDelay is the leading delay of the original impulse response.
+class HRTFKernel : public RefCounted<HRTFKernel> {
+public:
+    // Note: this is destructive on the passed in AudioChannel.
+    // The length of channel must be a power of two.
+    static PassRefPtr<HRTFKernel> create(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost)
+    {
+        return adoptRef(new HRTFKernel(channel, fftSize, sampleRate, bassBoost));
+    }
+
+    static PassRefPtr<HRTFKernel> create(PassOwnPtr<FFTFrame> fftFrame, double frameDelay, double sampleRate)
+    {
+        return adoptRef(new HRTFKernel(fftFrame, frameDelay, sampleRate));
+    }
+
+    // Given two HRTFKernels, and an interpolation factor x: 0 -> 1, returns an interpolated HRTFKernel.
+    static PassRefPtr<HRTFKernel> createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, double x);
+  
+    FFTFrame* fftFrame() { return m_fftFrame.get(); }
+    
+    size_t fftSize() const { return m_fftFrame->fftSize(); }
+    double frameDelay() const { return m_frameDelay; }
+
+    double sampleRate() const { return m_sampleRate; }
+    double nyquist() const { return 0.5 * sampleRate(); }
+
+    // Converts back into impulse-response form.
+    PassOwnPtr<AudioChannel> createImpulseResponse();
+
+private:
+    // Note: this is destructive on the passed in AudioChannel.
+    HRTFKernel(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost);
+    
+    HRTFKernel(PassOwnPtr<FFTFrame> fftFrame, double frameDelay, double sampleRate)
+        : m_fftFrame(fftFrame)
+        , m_frameDelay(frameDelay)
+        , m_sampleRate(sampleRate)
+    {
+    }
+    
+    OwnPtr<FFTFrame> m_fftFrame;
+    double m_frameDelay;
+    double m_sampleRate;
+};
+
+typedef Vector<RefPtr<HRTFKernel> > HRTFKernelList;
+
+} // namespace WebCore
+
+#endif // HRTFKernel_h