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