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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include "config.h" 26 27#if ENABLE(WEB_AUDIO) 28 29#include "AudioNodeOutput.h" 30 31#include "AudioBus.h" 32#include "AudioContext.h" 33#include "AudioNodeInput.h" 34#include "AudioParam.h" 35#include <wtf/Threading.h> 36 37namespace WebCore { 38 39AudioNodeOutput::AudioNodeOutput(AudioNode* node, unsigned numberOfChannels) 40 : m_node(node) 41 , m_numberOfChannels(numberOfChannels) 42 , m_desiredNumberOfChannels(numberOfChannels) 43 , m_isInPlace(false) 44 , m_isEnabled(true) 45 , m_renderingFanOutCount(0) 46 , m_renderingParamFanOutCount(0) 47{ 48 ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels()); 49 50 m_internalBus = AudioBus::create(numberOfChannels, AudioNode::ProcessingSizeInFrames); 51} 52 53void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels) 54{ 55 ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels()); 56 ASSERT(context()->isGraphOwner()); 57 58 m_desiredNumberOfChannels = numberOfChannels; 59 60 if (context()->isAudioThread()) { 61 // If we're in the audio thread then we can take care of it right away (we should be at the very start or end of a rendering quantum). 62 updateNumberOfChannels(); 63 } else { 64 // Let the context take care of it in the audio thread in the pre and post render tasks. 65 context()->markAudioNodeOutputDirty(this); 66 } 67} 68 69void AudioNodeOutput::updateInternalBus() 70{ 71 if (numberOfChannels() == m_internalBus->numberOfChannels()) 72 return; 73 74 m_internalBus = AudioBus::create(numberOfChannels(), AudioNode::ProcessingSizeInFrames); 75} 76 77void AudioNodeOutput::updateRenderingState() 78{ 79 updateNumberOfChannels(); 80 m_renderingFanOutCount = fanOutCount(); 81 m_renderingParamFanOutCount = paramFanOutCount(); 82} 83 84void AudioNodeOutput::updateNumberOfChannels() 85{ 86 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 87 88 if (m_numberOfChannels != m_desiredNumberOfChannels) { 89 m_numberOfChannels = m_desiredNumberOfChannels; 90 updateInternalBus(); 91 propagateChannelCount(); 92 } 93} 94 95void AudioNodeOutput::propagateChannelCount() 96{ 97 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 98 99 if (isChannelCountKnown()) { 100 // Announce to any nodes we're connected to that we changed our channel count for its input. 101 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) { 102 AudioNodeInput* input = *i; 103 AudioNode* connectionNode = input->node(); 104 connectionNode->checkNumberOfChannelsForInput(input); 105 } 106 } 107} 108 109AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess) 110{ 111 ASSERT(context()->isAudioThread()); 112 ASSERT(m_renderingFanOutCount > 0 || m_renderingParamFanOutCount > 0); 113 114 // Causes our AudioNode to process if it hasn't already for this render quantum. 115 // We try to do in-place processing (using inPlaceBus) if at all possible, 116 // but we can't process in-place if we're connected to more than one input (fan-out > 1). 117 // In this case pull() is called multiple times per rendering quantum, and the processIfNecessary() call below will 118 // cause our node to process() only the first time, caching the output in m_internalOutputBus for subsequent calls. 119 120 m_isInPlace = inPlaceBus && inPlaceBus->numberOfChannels() == numberOfChannels() && (m_renderingFanOutCount + m_renderingParamFanOutCount) == 1; 121 122 m_inPlaceBus = m_isInPlace ? inPlaceBus : 0; 123 124 node()->processIfNecessary(framesToProcess); 125 return bus(); 126} 127 128AudioBus* AudioNodeOutput::bus() const 129{ 130 ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread()); 131 return m_isInPlace ? m_inPlaceBus.get() : m_internalBus.get(); 132} 133 134unsigned AudioNodeOutput::fanOutCount() 135{ 136 ASSERT(context()->isGraphOwner()); 137 return m_inputs.size(); 138} 139 140unsigned AudioNodeOutput::paramFanOutCount() 141{ 142 ASSERT(context()->isGraphOwner()); 143 return m_params.size(); 144} 145 146unsigned AudioNodeOutput::renderingFanOutCount() const 147{ 148 return m_renderingFanOutCount; 149} 150 151unsigned AudioNodeOutput::renderingParamFanOutCount() const 152{ 153 return m_renderingParamFanOutCount; 154} 155 156void AudioNodeOutput::addInput(AudioNodeInput* input) 157{ 158 ASSERT(context()->isGraphOwner()); 159 160 ASSERT(input); 161 if (!input) 162 return; 163 164 m_inputs.add(input); 165} 166 167void AudioNodeOutput::removeInput(AudioNodeInput* input) 168{ 169 ASSERT(context()->isGraphOwner()); 170 171 ASSERT(input); 172 if (!input) 173 return; 174 175 m_inputs.remove(input); 176} 177 178void AudioNodeOutput::disconnectAllInputs() 179{ 180 ASSERT(context()->isGraphOwner()); 181 182 // AudioNodeInput::disconnect() changes m_inputs by calling removeInput(). 183 while (!m_inputs.isEmpty()) { 184 AudioNodeInput* input = *m_inputs.begin(); 185 input->disconnect(this); 186 } 187} 188 189void AudioNodeOutput::addParam(AudioParam* param) 190{ 191 ASSERT(context()->isGraphOwner()); 192 193 ASSERT(param); 194 if (!param) 195 return; 196 197 m_params.add(param); 198} 199 200void AudioNodeOutput::removeParam(AudioParam* param) 201{ 202 ASSERT(context()->isGraphOwner()); 203 204 ASSERT(param); 205 if (!param) 206 return; 207 208 m_params.remove(param); 209} 210 211void AudioNodeOutput::disconnectAllParams() 212{ 213 ASSERT(context()->isGraphOwner()); 214 215 // AudioParam::disconnect() changes m_params by calling removeParam(). 216 while (!m_params.isEmpty()) { 217 AudioParam* param = m_params.begin()->get(); 218 param->disconnect(this); 219 } 220} 221 222void AudioNodeOutput::disconnectAll() 223{ 224 disconnectAllInputs(); 225 disconnectAllParams(); 226} 227 228void AudioNodeOutput::disable() 229{ 230 ASSERT(context()->isGraphOwner()); 231 232 if (m_isEnabled) { 233 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) { 234 AudioNodeInput* input = *i; 235 input->disable(this); 236 } 237 m_isEnabled = false; 238 } 239} 240 241void AudioNodeOutput::enable() 242{ 243 ASSERT(context()->isGraphOwner()); 244 245 if (!m_isEnabled) { 246 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) { 247 AudioNodeInput* input = *i; 248 input->enable(this); 249 } 250 m_isEnabled = true; 251 } 252} 253 254} // namespace WebCore 255 256#endif // ENABLE(WEB_AUDIO) 257