2011-03-24 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Mar 2011 02:13:36 +0000 (02:13 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Mar 2011 02:13:36 +0000 (02:13 +0000)
        Reviewed by Kenneth Russell.

        web audio: Properly sample-rate convert audio assets in chromium port
        https://bugs.webkit.org/show_bug.cgi?id=56980

        No new tests since audio API is not yet implemented.

        * WebCore.gypi:
        * platform/audio/AudioBus.cpp:
        (WebCore::AudioBus::createBySampleRateConverting):
        (WebCore::AudioBus::createByMixingToMono):
        * platform/audio/AudioBus.h:
        * platform/audio/HRTFKernel.cpp:
        (WebCore::extractAverageGroupDelay):
        (WebCore::HRTFKernel::HRTFKernel):
        * platform/audio/SincResampler.cpp:
        * platform/audio/chromium/AudioBusChromium.cpp:
        (WebCore::AudioBus::loadPlatformResource):
        (WebCore::createBusFromInMemoryAudioFile):

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

Source/WebCore/ChangeLog
Source/WebCore/WebCore.gypi
Source/WebCore/platform/audio/AudioBus.cpp
Source/WebCore/platform/audio/AudioBus.h
Source/WebCore/platform/audio/HRTFKernel.cpp
Source/WebCore/platform/audio/SincResampler.cpp
Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp

index 78cb88ac10c4642b9d077d7136a68f44dc2faf9f..96fe26f9e25b6e7ddf1c661c11cf185cfc6c13a3 100644 (file)
@@ -1,3 +1,25 @@
+2011-03-24  Chris Rogers  <crogers@google.com>
+
+        Reviewed by Kenneth Russell.
+
+        web audio: Properly sample-rate convert audio assets in chromium port
+        https://bugs.webkit.org/show_bug.cgi?id=56980
+
+        No new tests since audio API is not yet implemented.
+
+        * WebCore.gypi:
+        * platform/audio/AudioBus.cpp:
+        (WebCore::AudioBus::createBySampleRateConverting):
+        (WebCore::AudioBus::createByMixingToMono):
+        * platform/audio/AudioBus.h:
+        * platform/audio/HRTFKernel.cpp:
+        (WebCore::extractAverageGroupDelay):
+        (WebCore::HRTFKernel::HRTFKernel):
+        * platform/audio/SincResampler.cpp:
+        * platform/audio/chromium/AudioBusChromium.cpp:
+        (WebCore::AudioBus::loadPlatformResource):
+        (WebCore::createBusFromInMemoryAudioFile):
+
 2011-03-24  Rik Cabanier  <cabanier@adobe.com>
 
         Reviewed by David Hyatt.
index 91882ffedb8e5366453b0aa81b6d7a6ac5e559cd..a8b75dc0d6be41bb0a40d796246d3c18f4c60197 100644 (file)
             'platform/audio/ReverbConvolverStage.h',
             'platform/audio/ReverbInputBuffer.cpp',
             'platform/audio/ReverbInputBuffer.h',
+            'platform/audio/SincResampler.cpp',
+            'platform/audio/SincResampler.h',
             'platform/audio/VectorMath.cpp',
             'platform/audio/VectorMath.h',
             'platform/audio/chromium/AudioBusChromium.cpp',
index dd4746d483f3b1e5a258028fb18f773916c3f204..6f744713ace776a3b4ec9671753cd6647273c6b2 100644 (file)
@@ -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)
index 888f6bf89acee077971eb7e1f254c34d1bde6ed7..1943c0d164a46670ccc100b568db3bb5c14c7f46 100644 (file)
@@ -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);
 
index 22d4b12ecc4cf7c86868c612ca545b5ba749a2e7..9db35ba65d647f6f34fab2e96da43304991b362f 100644 (file)
@@ -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();
index f2e53eaf061d3eb664c9a1eb942e96d9d3463d9e..e6f34b403736f819a964ecbb99a03c6aa133bbb8 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "config.h"
 
+#if ENABLE(WEB_AUDIO)
+
 #include "SincResampler.h"
 
 #include <wtf/MathExtras.h>
@@ -336,3 +338,5 @@ void SincResampler::process(float* source, float* destination, unsigned numberOf
 }
 
 } // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
index de44b1c5b73a6691c19a93b00993b8c516e3a706..f34dc773dc492351d8a17755a8ee767dff382c31 100644 (file)
@@ -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