1/* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30 31#if ENABLE(WEB_AUDIO) 32 33#include "platform/audio/AudioDestination.h" 34 35#include "platform/audio/AudioFIFO.h" 36#include "platform/audio/AudioPullFIFO.h" 37#include "public/platform/Platform.h" 38 39namespace blink { 40 41// Buffer size at which the web audio engine will render. 42const unsigned renderBufferSize = 128; 43 44// Size of the FIFO 45const size_t fifoSize = 8192; 46 47// Factory method: Chromium-implementation 48PassOwnPtr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, const String& inputDeviceId, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate) 49{ 50 return adoptPtr(new AudioDestination(callback, inputDeviceId, numberOfInputChannels, numberOfOutputChannels, sampleRate)); 51} 52 53AudioDestination::AudioDestination(AudioIOCallback& callback, const String& inputDeviceId, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate) 54 : m_callback(callback) 55 , m_numberOfOutputChannels(numberOfOutputChannels) 56 , m_inputBus(AudioBus::create(numberOfInputChannels, renderBufferSize)) 57 , m_renderBus(AudioBus::create(numberOfOutputChannels, renderBufferSize, false)) 58 , m_sampleRate(sampleRate) 59 , m_isPlaying(false) 60{ 61 // Use the optimal buffer size recommended by the audio backend. 62 m_callbackBufferSize = Platform::current()->audioHardwareBufferSize(); 63 64#if OS(ANDROID) 65 // The optimum low-latency hardware buffer size is usually too small on Android for WebAudio to 66 // render without glitching. So, if it is small, use a larger size. If it was already large, use 67 // the requested size. 68 // 69 // Since WebAudio renders in 128-frame blocks, the small buffer sizes (144 for a Galaxy Nexus), 70 // cause significant processing jitter. Sometimes multiple blocks will processed, but other 71 // times will not be since the FIFO can satisfy the request. By using a larger 72 // callbackBufferSize, we smooth out the jitter. 73 const size_t kSmallBufferSize = 1024; 74 const size_t kDefaultCallbackBufferSize = 2048; 75 76 if (m_callbackBufferSize <= kSmallBufferSize) 77 m_callbackBufferSize = kDefaultCallbackBufferSize; 78#endif 79 80 // Quick exit if the requested size is too large. 81 ASSERT(m_callbackBufferSize + renderBufferSize <= fifoSize); 82 if (m_callbackBufferSize + renderBufferSize > fifoSize) 83 return; 84 85 m_audioDevice = adoptPtr(Platform::current()->createAudioDevice(m_callbackBufferSize, numberOfInputChannels, numberOfOutputChannels, sampleRate, this, inputDeviceId)); 86 ASSERT(m_audioDevice); 87 88 // Create a FIFO to handle the possibility of the callback size 89 // not being a multiple of the render size. If the FIFO already 90 // contains enough data, the data will be provided directly. 91 // Otherwise, the FIFO will call the provider enough times to 92 // satisfy the request for data. 93 m_fifo = adoptPtr(new AudioPullFIFO(*this, numberOfOutputChannels, fifoSize, renderBufferSize)); 94 95 // Input buffering. 96 m_inputFifo = adoptPtr(new AudioFIFO(numberOfInputChannels, fifoSize)); 97 98 // If the callback size does not match the render size, then we need to buffer some 99 // extra silence for the input. Otherwise, we can over-consume the input FIFO. 100 if (m_callbackBufferSize != renderBufferSize) { 101 // FIXME: handle multi-channel input and don't hard-code to stereo. 102 RefPtr<AudioBus> silence = AudioBus::create(2, renderBufferSize); 103 m_inputFifo->push(silence.get()); 104 } 105} 106 107AudioDestination::~AudioDestination() 108{ 109 stop(); 110} 111 112void AudioDestination::start() 113{ 114 if (!m_isPlaying && m_audioDevice) { 115 m_audioDevice->start(); 116 m_isPlaying = true; 117 } 118} 119 120void AudioDestination::stop() 121{ 122 if (m_isPlaying && m_audioDevice) { 123 m_audioDevice->stop(); 124 m_isPlaying = false; 125 } 126} 127 128float AudioDestination::hardwareSampleRate() 129{ 130 return static_cast<float>(Platform::current()->audioHardwareSampleRate()); 131} 132 133unsigned long AudioDestination::maxChannelCount() 134{ 135 return static_cast<float>(Platform::current()->audioHardwareOutputChannels()); 136} 137 138void AudioDestination::render(const WebVector<float*>& sourceData, const WebVector<float*>& audioData, size_t numberOfFrames) 139{ 140 bool isNumberOfChannelsGood = audioData.size() == m_numberOfOutputChannels; 141 if (!isNumberOfChannelsGood) { 142 ASSERT_NOT_REACHED(); 143 return; 144 } 145 146 bool isBufferSizeGood = numberOfFrames == m_callbackBufferSize; 147 if (!isBufferSizeGood) { 148 ASSERT_NOT_REACHED(); 149 return; 150 } 151 152 // Buffer optional live input. 153 if (sourceData.size() >= 2) { 154 // FIXME: handle multi-channel input and don't hard-code to stereo. 155 RefPtr<AudioBus> wrapperBus = AudioBus::create(2, numberOfFrames, false); 156 wrapperBus->setChannelMemory(0, sourceData[0], numberOfFrames); 157 wrapperBus->setChannelMemory(1, sourceData[1], numberOfFrames); 158 m_inputFifo->push(wrapperBus.get()); 159 } 160 161 for (unsigned i = 0; i < m_numberOfOutputChannels; ++i) 162 m_renderBus->setChannelMemory(i, audioData[i], numberOfFrames); 163 164 m_fifo->consume(m_renderBus.get(), numberOfFrames); 165} 166 167void AudioDestination::provideInput(AudioBus* bus, size_t framesToProcess) 168{ 169 AudioBus* sourceBus = 0; 170 if (m_inputFifo->framesInFifo() >= framesToProcess) { 171 m_inputFifo->consume(m_inputBus.get(), framesToProcess); 172 sourceBus = m_inputBus.get(); 173 } 174 175 m_callback.render(sourceBus, bus, framesToProcess); 176} 177 178} // namespace blink 179 180#endif // ENABLE(WEB_AUDIO) 181