| Index: third_party/WebKit/Source/platform/audio/AudioDestination.cpp
|
| diff --git a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
|
| index 72444184e89d65df7c8a29bf9fb77efc2bc4af9b..dcf0ea5841cdf4175ca5d5f325d1167cf89065de 100644
|
| --- a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
|
| +++ b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
|
| @@ -29,7 +29,6 @@
|
| #include "platform/audio/AudioDestination.h"
|
|
|
| #include "platform/Histogram.h"
|
| -#include "platform/audio/AudioFIFO.h"
|
| #include "platform/audio/AudioPullFIFO.h"
|
| #include "platform/audio/AudioUtilities.h"
|
| #include "platform/weborigin/SecurityOrigin.h"
|
| @@ -40,138 +39,71 @@
|
|
|
| namespace blink {
|
|
|
| -// Size of the FIFO
|
| -const size_t fifoSize = 8192;
|
| +// FIFO Size.
|
| +//
|
| +// TODO(hongchan): This was estimated based on the largest callback buffer size
|
| +// that we would ever need. The current UMA stats indicates that this is, in
|
| +// fact, probably too small. There are Android devices out there with a size of
|
| +// 8000 or so. We might need to make this larger. See: crbug.com/670747
|
| +const size_t kFIFOSize = 8192;
|
|
|
| -// Factory method: Chromium-implementation
|
| std::unique_ptr<AudioDestination> AudioDestination::create(
|
| AudioIOCallback& callback,
|
| - const String& inputDeviceId,
|
| - unsigned numberOfInputChannels,
|
| unsigned numberOfOutputChannels,
|
| float sampleRate,
|
| PassRefPtr<SecurityOrigin> securityOrigin) {
|
| return WTF::wrapUnique(new AudioDestination(
|
| - callback, inputDeviceId, numberOfInputChannels, numberOfOutputChannels,
|
| - sampleRate, std::move(securityOrigin)));
|
| + callback, numberOfOutputChannels, sampleRate, std::move(securityOrigin)));
|
| }
|
|
|
| AudioDestination::AudioDestination(AudioIOCallback& callback,
|
| - const String& inputDeviceId,
|
| - unsigned numberOfInputChannels,
|
| unsigned numberOfOutputChannels,
|
| float sampleRate,
|
| PassRefPtr<SecurityOrigin> securityOrigin)
|
| - : m_callback(callback),
|
| - m_numberOfOutputChannels(numberOfOutputChannels),
|
| - m_renderBus(AudioBus::create(numberOfOutputChannels,
|
| - AudioUtilities::kRenderQuantumFrames,
|
| - false)),
|
| + : m_numberOfOutputChannels(numberOfOutputChannels),
|
| m_sampleRate(sampleRate),
|
| m_isPlaying(false),
|
| - m_framesElapsed(0),
|
| - m_outputPosition() {
|
| - // Histogram for audioHardwareBufferSize
|
| - DEFINE_STATIC_LOCAL(SparseHistogram, hardwareBufferSizeHistogram,
|
| - ("WebAudio.AudioDestination.HardwareBufferSize"));
|
| - // Histogram for the actual callback size used. Typically, this is the same
|
| - // as audioHardwareBufferSize, but can be adjusted depending on some
|
| - // heuristics below.
|
| - DEFINE_STATIC_LOCAL(SparseHistogram, callbackBufferSizeHistogram,
|
| - ("WebAudio.AudioDestination.CallbackBufferSize"));
|
| -
|
| - // Use the optimal buffer size recommended by the audio backend.
|
| - size_t recommendedHardwareBufferSize =
|
| - Platform::current()->audioHardwareBufferSize();
|
| - m_callbackBufferSize = recommendedHardwareBufferSize;
|
| -
|
| -#if OS(ANDROID)
|
| - // The optimum low-latency hardware buffer size is usually too small on
|
| - // Android for WebAudio to render without glitching. So, if it is small, use
|
| - // a larger size. If it was already large, use the requested size.
|
| - //
|
| - // Since WebAudio renders in 128-frame blocks, the small buffer sizes (144
|
| - // for a Galaxy Nexus), cause significant processing jitter. Sometimes
|
| - // multiple blocks will processed, but other times will not be since the FIFO
|
| - // can satisfy the request. By using a larger callbackBufferSize, we smooth
|
| - // out the jitter.
|
| - const size_t kSmallBufferSize = 1024;
|
| - const size_t kDefaultCallbackBufferSize = 2048;
|
| -
|
| - if (m_callbackBufferSize <= kSmallBufferSize)
|
| - m_callbackBufferSize = kDefaultCallbackBufferSize;
|
| -
|
| - LOG(INFO) << "audioHardwareBufferSize = " << recommendedHardwareBufferSize;
|
| - LOG(INFO) << "callbackBufferSize = " << m_callbackBufferSize;
|
| -#endif
|
| -
|
| - // Quick exit if the requested size is too large.
|
| - DCHECK_LE(m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames,
|
| - fifoSize);
|
| - if (m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames > fifoSize)
|
| - return;
|
| -
|
| - m_audioDevice = WTF::wrapUnique(Platform::current()->createAudioDevice(
|
| - m_callbackBufferSize, numberOfInputChannels, numberOfOutputChannels,
|
| - sampleRate, this, inputDeviceId, std::move(securityOrigin)));
|
| - ASSERT(m_audioDevice);
|
| -
|
| - // Record the sizes if we successfully created an output device.
|
| - hardwareBufferSizeHistogram.sample(recommendedHardwareBufferSize);
|
| - callbackBufferSizeHistogram.sample(m_callbackBufferSize);
|
| -
|
| - // 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 =
|
| - WTF::wrapUnique(new AudioPullFIFO(*this, numberOfOutputChannels, fifoSize,
|
| - AudioUtilities::kRenderQuantumFrames));
|
| + m_callback(callback),
|
| + m_outputBus(AudioBus::create(numberOfOutputChannels,
|
| + AudioUtilities::kRenderQuantumFrames,
|
| + false)),
|
| + m_framesElapsed(0) {
|
| + // Calculate the optimum buffer size first.
|
| + if (calculateBufferSize()) {
|
| + // Create WebAudioDevice. blink::WebAudioDevice is designed to support the
|
| + // local input (e.g. loopback from OS audio system), but Chromium's media
|
| + // renderer does not support it currently. Thus, we use zero for the number
|
| + // of input channels.
|
| + m_webAudioDevice = WTF::wrapUnique(Platform::current()->createAudioDevice(
|
| + m_callbackBufferSize, 0, numberOfOutputChannels, sampleRate, this,
|
| + String(), std::move(securityOrigin)));
|
| + DCHECK(m_webAudioDevice);
|
| +
|
| + // Create a FIFO.
|
| + m_fifo = WTF::wrapUnique(
|
| + new AudioPullFIFO(*this, numberOfOutputChannels, kFIFOSize,
|
| + AudioUtilities::kRenderQuantumFrames));
|
| + } else {
|
| + NOTREACHED();
|
| + }
|
| }
|
|
|
| AudioDestination::~AudioDestination() {
|
| stop();
|
| }
|
|
|
| -void AudioDestination::start() {
|
| - if (!m_isPlaying && m_audioDevice) {
|
| - m_audioDevice->start();
|
| - m_isPlaying = true;
|
| - }
|
| -}
|
| -
|
| -void AudioDestination::stop() {
|
| - if (m_isPlaying && m_audioDevice) {
|
| - m_audioDevice->stop();
|
| - m_isPlaying = false;
|
| - }
|
| -}
|
| -
|
| -float AudioDestination::hardwareSampleRate() {
|
| - return static_cast<float>(Platform::current()->audioHardwareSampleRate());
|
| -}
|
| -
|
| -unsigned long AudioDestination::maxChannelCount() {
|
| - return static_cast<float>(Platform::current()->audioHardwareOutputChannels());
|
| -}
|
| -
|
| -void AudioDestination::render(const WebVector<float*>& audioData,
|
| +void AudioDestination::render(const WebVector<float*>& destinationData,
|
| size_t numberOfFrames,
|
| double delay,
|
| double delayTimestamp,
|
| size_t priorFramesSkipped) {
|
| - bool isNumberOfChannelsGood = audioData.size() == m_numberOfOutputChannels;
|
| - if (!isNumberOfChannelsGood) {
|
| - ASSERT_NOT_REACHED();
|
| + DCHECK_EQ(destinationData.size(), m_numberOfOutputChannels);
|
| + if (destinationData.size() != m_numberOfOutputChannels)
|
| return;
|
| - }
|
|
|
| - bool isBufferSizeGood = numberOfFrames == m_callbackBufferSize;
|
| - if (!isBufferSizeGood) {
|
| - ASSERT_NOT_REACHED();
|
| + DCHECK_EQ(numberOfFrames, m_callbackBufferSize);
|
| + if (numberOfFrames != m_callbackBufferSize)
|
| return;
|
| - }
|
|
|
| m_framesElapsed -= std::min(m_framesElapsed, priorFramesSkipped);
|
| double outputPosition =
|
| @@ -180,18 +112,20 @@ void AudioDestination::render(const WebVector<float*>& audioData,
|
| m_outputPosition.timestamp = delayTimestamp;
|
| m_outputPositionReceivedTimestamp = base::TimeTicks::Now();
|
|
|
| + // Associate the destination data array with the output bus then fill the
|
| + // FIFO.
|
| for (unsigned i = 0; i < m_numberOfOutputChannels; ++i)
|
| - m_renderBus->setChannelMemory(i, audioData[i], numberOfFrames);
|
| -
|
| - m_fifo->consume(m_renderBus.get(), numberOfFrames);
|
| + m_outputBus->setChannelMemory(i, destinationData[i], numberOfFrames);
|
| + m_fifo->consume(m_outputBus.get(), numberOfFrames);
|
|
|
| m_framesElapsed += numberOfFrames;
|
| }
|
|
|
| -void AudioDestination::provideInput(AudioBus* bus, size_t framesToProcess) {
|
| +void AudioDestination::provideInput(AudioBus* outputBus,
|
| + size_t framesToProcess) {
|
| AudioIOPosition outputPosition = m_outputPosition;
|
|
|
| - // If platfrom buffer is more than two times longer than |framesToProcess|
|
| + // If platform buffer is more than two times longer than |framesToProcess|
|
| // we do not want output position to get stuck so we promote it
|
| // using the elapsed time from the moment it was initially obtained.
|
| if (m_callbackBufferSize > framesToProcess * 2) {
|
| @@ -206,7 +140,81 @@ void AudioDestination::provideInput(AudioBus* bus, size_t framesToProcess) {
|
| if (outputPosition.position < 0.0)
|
| outputPosition.position = 0.0;
|
|
|
| - m_callback.render(nullptr, bus, framesToProcess, outputPosition);
|
| + // To fill the FIFO, start the render call chain of the destination node.
|
| + m_callback.render(nullptr, outputBus, framesToProcess, outputPosition);
|
| +}
|
| +
|
| +void AudioDestination::start() {
|
| + if (m_webAudioDevice && !m_isPlaying) {
|
| + m_webAudioDevice->start();
|
| + m_isPlaying = true;
|
| + }
|
| +}
|
| +
|
| +void AudioDestination::stop() {
|
| + if (m_webAudioDevice && m_isPlaying) {
|
| + m_webAudioDevice->stop();
|
| + m_isPlaying = false;
|
| + }
|
| +}
|
| +
|
| +size_t AudioDestination::hardwareBufferSize() {
|
| + return Platform::current()->audioHardwareBufferSize();
|
| +}
|
| +
|
| +float AudioDestination::hardwareSampleRate() {
|
| + return static_cast<float>(Platform::current()->audioHardwareSampleRate());
|
| +}
|
| +
|
| +unsigned long AudioDestination::maxChannelCount() {
|
| + return static_cast<unsigned long>(
|
| + Platform::current()->audioHardwareOutputChannels());
|
| +}
|
| +
|
| +bool AudioDestination::calculateBufferSize() {
|
| + // Use the optimal buffer size recommended by the audio backend.
|
| + size_t recommendedHardwareBufferSize = hardwareBufferSize();
|
| + m_callbackBufferSize = recommendedHardwareBufferSize;
|
| +
|
| +#if OS(ANDROID)
|
| + // The optimum low-latency hardware buffer size is usually too small on
|
| + // Android for WebAudio to render without glitching. So, if it is small, use a
|
| + // larger size. If it was already large, use the requested size.
|
| + //
|
| + // Since WebAudio renders in 128-frame blocks, the small buffer sizes (144 for
|
| + // a Galaxy Nexus), cause significant processing jitter. Sometimes multiple
|
| + // blocks will processed, but other times will not be since the FIFO can
|
| + // satisfy the request. By using a larger callbackBufferSize, we smooth out
|
| + // the jitter.
|
| + const size_t kSmallBufferSize = 1024;
|
| + const size_t kDefaultCallbackBufferSize = 2048;
|
| +
|
| + if (m_callbackBufferSize <= kSmallBufferSize)
|
| + m_callbackBufferSize = kDefaultCallbackBufferSize;
|
| +
|
| + LOG(INFO) << "audioHardwareBufferSize = " << recommendedHardwareBufferSize;
|
| + LOG(INFO) << "callbackBufferSize = " << m_callbackBufferSize;
|
| +#endif
|
| +
|
| + // Histogram for audioHardwareBufferSize
|
| + DEFINE_STATIC_LOCAL(SparseHistogram, hardwareBufferSizeHistogram,
|
| + ("WebAudio.AudioDestination.HardwareBufferSize"));
|
| +
|
| + // Histogram for the actual callback size used. Typically, this is the same
|
| + // as audioHardwareBufferSize, but can be adjusted depending on some
|
| + // heuristics below.
|
| + DEFINE_STATIC_LOCAL(SparseHistogram, callbackBufferSizeHistogram,
|
| + ("WebAudio.AudioDestination.CallbackBufferSize"));
|
| +
|
| + // Record the sizes if we successfully created an output device.
|
| + hardwareBufferSizeHistogram.sample(recommendedHardwareBufferSize);
|
| + callbackBufferSizeHistogram.sample(m_callbackBufferSize);
|
| +
|
| + // Check if the requested buffer size is too large.
|
| + bool isBufferSizeValid =
|
| + m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames <= kFIFOSize;
|
| + DCHECK(isBufferSizeValid);
|
| + return isBufferSizeValid;
|
| }
|
|
|
| } // namespace blink
|
|
|