Title: [100210] trunk/Source/WebKit/chromium
Revision
100210
Author
commit-qu...@webkit.org
Date
2011-11-14 16:18:44 -0800 (Mon, 14 Nov 2011)

Log Message

Add buffering to handle mismatch between hardware buffer size and webaudio render size
https://bugs.webkit.org/show_bug.cgi?id=71949

Patch by Raymond Toy <r...@google.com> on 2011-11-14
Reviewed by Kenneth Russell.

* src/AudioDestinationChromium.cpp:
(WebCore::AudioDestinationChromium::AudioDestinationChromium):
Create the FIFO for buffering.
(WebCore::AudioDestinationChromium::render):  Get rid of
m_renderCountPerCallback and let the FIFO consume function produce
the appropriate nubmer of calls to the webaudio producer.  Also
remove the rounding of the callback size so we use whatever the
hardware returns.  Removed maximumCallbackBufferSize and use
fifoSize to set the FIFO size.
(WebCore::AudioDestinationChromium::FIFO::FIFO):
(WebCore::AudioDestinationChromium::FIFO::consume):
(WebCore::AudioDestinationChromium::FIFO::findWrapLengths):
(WebCore::AudioDestinationChromium::FIFO::fillBuffer):
Implementation of new FIFO class.
* src/AudioDestinationChromium.h:
(WebCore::AudioDestinationChromium::FIFO::updateIndex):
Define new FIFO class.

Modified Paths

Diff

Modified: trunk/Source/WebKit/chromium/ChangeLog (100209 => 100210)


--- trunk/Source/WebKit/chromium/ChangeLog	2011-11-15 00:16:59 UTC (rev 100209)
+++ trunk/Source/WebKit/chromium/ChangeLog	2011-11-15 00:18:44 UTC (rev 100210)
@@ -1,3 +1,28 @@
+2011-11-14  Raymond Toy  <r...@google.com>
+
+        Add buffering to handle mismatch between hardware buffer size and webaudio render size
+        https://bugs.webkit.org/show_bug.cgi?id=71949
+
+        Reviewed by Kenneth Russell.
+
+        * src/AudioDestinationChromium.cpp:
+        (WebCore::AudioDestinationChromium::AudioDestinationChromium):
+        Create the FIFO for buffering.
+        (WebCore::AudioDestinationChromium::render):  Get rid of
+        m_renderCountPerCallback and let the FIFO consume function produce
+        the appropriate nubmer of calls to the webaudio producer.  Also
+        remove the rounding of the callback size so we use whatever the
+        hardware returns.  Removed maximumCallbackBufferSize and use
+        fifoSize to set the FIFO size.
+        (WebCore::AudioDestinationChromium::FIFO::FIFO):
+        (WebCore::AudioDestinationChromium::FIFO::consume):
+        (WebCore::AudioDestinationChromium::FIFO::findWrapLengths):
+        (WebCore::AudioDestinationChromium::FIFO::fillBuffer):
+        Implementation of new FIFO class.
+        * src/AudioDestinationChromium.h:
+        (WebCore::AudioDestinationChromium::FIFO::updateIndex):
+        Define new FIFO class.
+
 2011-11-14  Adrienne Walker  <e...@google.com>
 
         [chromium] Pipe compositor commit/swap up to WebWidgetClient

Modified: trunk/Source/WebKit/chromium/src/AudioDestinationChromium.cpp (100209 => 100210)


--- trunk/Source/WebKit/chromium/src/AudioDestinationChromium.cpp	2011-11-15 00:16:59 UTC (rev 100209)
+++ trunk/Source/WebKit/chromium/src/AudioDestinationChromium.cpp	2011-11-15 00:18:44 UTC (rev 100210)
@@ -32,7 +32,6 @@
 
 #include "AudioDestinationChromium.h"
 
-#include "AudioSourceProvider.h"
 #include "WebKit.h"
 #include "WebKitPlatformSupport.h"
 
@@ -43,8 +42,8 @@
 // Buffer size at which the web audio engine will render.
 const unsigned renderBufferSize = 128;
 
-// Maximum allowed buffer size
-const size_t maximumCallbackBufferSize = 16384;
+// Size of the FIFO
+const size_t fifoSize = 8192;
 
 // FIXME: add support for multi-channel.
 const unsigned numberOfChannels = 2;
@@ -61,23 +60,23 @@
     , m_sampleRate(sampleRate)
     , m_isPlaying(false)
 {
-    // Get the minimum usable buffer size. We'll round this value up
-    // to a multiple of our render size.
-    size_t callbackSize = webKitPlatformSupport()->audioHardwareBufferSize();
+    // Use the optimal buffer size recommended by the audio backend.
+    m_callbackBufferSize = webKitPlatformSupport()->audioHardwareBufferSize();
 
-    // Figure out how many render calls per call back, rounding up if needed.
-    m_renderCountPerCallback = (callbackSize + renderBufferSize - 1) / renderBufferSize;
-
-    m_callbackBufferSize = m_renderCountPerCallback * renderBufferSize;
-
-    bool isSizeGood = m_callbackBufferSize >= renderBufferSize
-        && m_callbackBufferSize <= maximumCallbackBufferSize;
-    ASSERT(isSizeGood);
-    if (!isSizeGood)
-      return;
+    // Quick exit if the requested size is too large.
+    ASSERT(m_callbackBufferSize + renderBufferSize <= fifoSize);
+    if (m_callbackBufferSize + renderBufferSize > fifoSize)
+        return;
     
     m_audioDevice = adoptPtr(webKitPlatformSupport()->createAudioDevice(m_callbackBufferSize, numberOfChannels, sampleRate, this));
     ASSERT(m_audioDevice);
+
+    // Create a FIFO to handle the possibility of the callback size
+    // not being a multiple of the render size. If the FIFO already
+    // contains enough data, the data will be provided directly.
+    // Otherwise, the FIFO will call the provider enough times to
+    // satisfy the request for data.
+    m_fifo = adoptPtr(new FIFO(provider, numberOfChannels, fifoSize, renderBufferSize));
 }
 
 AudioDestinationChromium::~AudioDestinationChromium()
@@ -121,14 +120,130 @@
         return;
     }
 
-    // Split up the callback buffer into smaller chunks which we'll render one after the other.
-    for (unsigned i = 0; i < m_renderCountPerCallback; ++i) {
-        m_renderBus.setChannelMemory(0, audioData[0] + i * renderBufferSize, renderBufferSize);
-        m_renderBus.setChannelMemory(1, audioData[1] + i * renderBufferSize, renderBufferSize);
-        m_provider.provideInput(&m_renderBus, renderBufferSize);
+    m_renderBus.setChannelMemory(0, audioData[0], numberOfFrames);
+    m_renderBus.setChannelMemory(1, audioData[1], numberOfFrames);
+    m_fifo->consume(&m_renderBus, numberOfFrames);
+}
+
+AudioDestinationChromium::FIFO::FIFO(AudioSourceProvider& provider, unsigned numberOfChannels, size_t fifoLength, size_t providerSize)
+    : m_provider(provider)
+    , m_fifoAudioBus(numberOfChannels, fifoLength)
+    , m_fifoLength(fifoLength)
+    , m_framesInFifo(0)
+    , m_readIndex(0)
+    , m_writeIndex(0)
+    , m_providerSize(providerSize)
+    , m_tempBus(numberOfChannels, providerSize)
+{
+}
+
+void AudioDestinationChromium::FIFO::consume(AudioBus* destination, size_t framesToConsume)
+{
+    bool isGood = destination && (framesToConsume <= m_fifoLength);
+    ASSERT(isGood);
+    if (!isGood)
+        return;
+
+    if (framesToConsume > m_framesInFifo) {
+        // We don't have enough data in the FIFO to fulfill the
+        // request. Ask for more data.
+        fillBuffer(framesToConsume - m_framesInFifo);
     }
+
+    // We have enough data now. Copy the requested number of samples
+    // to the destination.
+
+    size_t part1Length;
+    size_t part2Length;
+    findWrapLengths(m_readIndex, framesToConsume, part1Length, part2Length);
+
+    size_t numberOfChannels = m_fifoAudioBus.numberOfChannels();
+
+    for (size_t channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
+        float* destinationData = destination->channel(channelIndex)->data();
+        float* sourceData = m_fifoAudioBus.channel(channelIndex)->data();
+
+        bool isCopyGood = ((m_readIndex < m_fifoLength)
+                           && (m_readIndex + part1Length) <= m_fifoLength
+                           && (part1Length <= destination->length())
+                           && (part1Length + part2Length) <= destination->length());
+        ASSERT(isCopyGood);
+        if (!isCopyGood)
+            return;
+
+        memcpy(destinationData, sourceData + m_readIndex, part1Length * sizeof(*sourceData));
+        // Handle wrap around of the FIFO, if needed.
+        if (part2Length > 0)
+            memcpy(destinationData + part1Length, sourceData, part2Length * sizeof(*sourceData));
+    }
+    m_readIndex = updateIndex(m_readIndex, framesToConsume);
+    m_framesInFifo -= framesToConsume;
+    ASSERT(m_framesInFifo >= 0);
 }
 
+void AudioDestinationChromium::FIFO::findWrapLengths(size_t index, size_t size, size_t& part1Length, size_t& part2Length)
+{
+    ASSERT(index < m_fifoLength && size <= m_fifoLength);
+    if (index < m_fifoLength && size <= m_fifoLength) {
+        if (index + size > m_fifoLength) {
+            // Need to wrap. Figure out the length of each piece.
+            part1Length = m_fifoLength - index;
+            part2Length = size - part1Length;
+        } else {
+            // No wrap needed.
+            part1Length = size;
+            part2Length = 0;
+        }
+    } else {
+        // Invalid values for index or size. Set the part lengths to
+        // zero so nothing is copied.
+        part1Length = 0;
+        part2Length = 0;
+    }
+}
+
+void AudioDestinationChromium::FIFO::fillBuffer(size_t numberOfFrames)
+{
+    // Keep asking the provider to give us data until we have received
+    // at least |numberOfFrames| of data. Stuff the data into the
+    // FIFO.
+    size_t framesProvided = 0;
+
+    while (framesProvided < numberOfFrames) {
+        m_provider.provideInput(&m_tempBus, m_providerSize);
+
+        size_t part1Length;
+        size_t part2Length;
+        findWrapLengths(m_writeIndex, m_providerSize, part1Length, part2Length);
+
+        size_t numberOfChannels = m_fifoAudioBus.numberOfChannels();
+        
+        for (size_t channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
+            float* destination = m_fifoAudioBus.channel(channelIndex)->data();
+            float* source = m_tempBus.channel(channelIndex)->data();
+
+            bool isCopyGood = (part1Length <= m_providerSize
+                               && (part1Length + part2Length) <= m_providerSize
+                               && (m_writeIndex < m_fifoLength)
+                               && (m_writeIndex + part1Length) <= m_fifoLength
+                               && part2Length < m_fifoLength);
+            ASSERT(isCopyGood);
+            if (!isCopyGood)
+                return;
+
+            memcpy(destination + m_writeIndex, source, part1Length * sizeof(*destination));
+            // Handle wrap around of the FIFO, if needed.
+            if (part2Length > 0)
+                memcpy(destination, source + part1Length, part2Length * sizeof(*destination));
+        }
+
+        m_framesInFifo += m_providerSize;
+        ASSERT(m_framesInFifo <= m_fifoLength);
+        m_writeIndex = updateIndex(m_writeIndex, m_providerSize);
+        framesProvided += m_providerSize;
+    }
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUDIO)

Modified: trunk/Source/WebKit/chromium/src/AudioDestinationChromium.h (100209 => 100210)


--- trunk/Source/WebKit/chromium/src/AudioDestinationChromium.h	2011-11-15 00:16:59 UTC (rev 100209)
+++ trunk/Source/WebKit/chromium/src/AudioDestinationChromium.h	2011-11-15 00:18:44 UTC (rev 100210)
@@ -31,6 +31,7 @@
 
 #include "AudioBus.h"
 #include "AudioDestination.h"
+#include "AudioSourceProvider.h"
 #include "WebAudioDevice.h"
 #include "WebVector.h"
 
@@ -55,13 +56,64 @@
     virtual void render(const WebKit::WebVector<float*>& audioData, size_t numberOfFrames);
 
 private:
-    AudioSourceProvider& m_provider;
+    // A FIFO (First In First Out) buffer to handle mismatches in the
+    // audio backend hardware buffer size and the Web Audio render size.
+    class FIFO {
+    public:
+        // Create a FIFO that gets data from |provider|. The FIFO will
+        // be large enough to hold |fifoLength| frames of data of
+        // |numberOfChannels| channels. The AudioSourceProvider will
+        // be asked to produce |providerSize| frames when the FIFO
+        // needs more data.
+        FIFO(AudioSourceProvider& provider, unsigned numberOfChannels, size_t fifoLength, size_t providerSize);
+
+        // Read |framesToConsume| frames from the FIFO into the
+        // destination. If the FIFO does not have enough data, we ask
+        // the |provider| to get more data to fulfill the request.
+        void consume(AudioBus* destination, size_t framesToConsume);
+
+    private:
+        // Update the FIFO index by the step, with appropriate
+        // wrapping around the endpoint.
+        int updateIndex(int index, int step) { return (index + step) % m_fifoLength; }
+
+        void findWrapLengths(size_t index, size_t providerSize, size_t& part1Length, size_t& part2Length);
+        
+        // Fill the FIFO buffer with at least |numberOfFrames| more data.
+        void fillBuffer(size_t numberOfFrames);
+
+        // The provider of the data in our FIFO.
+        AudioSourceProvider& m_provider;
+
+        // The FIFO itself. In reality, the FIFO is a circular buffer.
+        AudioBus m_fifoAudioBus;
+
+        // The total available space in the FIFO.
+        size_t m_fifoLength;
+
+        // The number of actual elements in the FIFO
+        size_t m_framesInFifo;
+
+        // Where to start reading from the FIFO.
+        size_t m_readIndex;
+
+        // Where to start writing to the FIFO.
+        size_t m_writeIndex;
+
+        // Number of frames of data that the provider will produce per call.
+        unsigned int m_providerSize;
+
+        // Temporary workspace to hold the data from the provider.
+        AudioBus m_tempBus;
+    };
+
+AudioSourceProvider& m_provider;
     AudioBus m_renderBus;
     float m_sampleRate;
     bool m_isPlaying;
     OwnPtr<WebKit::WebAudioDevice> m_audioDevice;
     size_t m_callbackBufferSize;
-    unsigned m_renderCountPerCallback;
+    OwnPtr<FIFO> m_fifo;
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to