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 "modules/webaudio/AudioNodeOutput.h" 30 31#include "modules/webaudio/AudioContext.h" 32#include "modules/webaudio/AudioNodeInput.h" 33#include "wtf/Threading.h" 34 35namespace blink { 36 37inline AudioNodeOutput::AudioNodeOutput(AudioNode* node, unsigned numberOfChannels) 38 : m_node(node) 39 , m_numberOfChannels(numberOfChannels) 40 , m_desiredNumberOfChannels(numberOfChannels) 41 , m_isInPlace(false) 42 , m_isEnabled(true) 43#if ENABLE_ASSERT 44 , m_didCallDispose(false) 45#endif 46 , m_renderingFanOutCount(0) 47 , m_renderingParamFanOutCount(0) 48{ 49 ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels()); 50 51 m_internalBus = AudioBus::create(numberOfChannels, AudioNode::ProcessingSizeInFrames); 52} 53 54AudioNodeOutput* AudioNodeOutput::create(AudioNode* node, unsigned numberOfChannels) 55{ 56 return new AudioNodeOutput(node, numberOfChannels); 57} 58 59void AudioNodeOutput::trace(Visitor* visitor) 60{ 61 visitor->trace(m_node); 62 visitor->trace(m_inputs); 63 visitor->trace(m_params); 64} 65 66void AudioNodeOutput::dispose() 67{ 68#if ENABLE_ASSERT 69 m_didCallDispose = true; 70#endif 71 context()->removeMarkedAudioNodeOutput(this); 72} 73 74void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels) 75{ 76 ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels()); 77 ASSERT(context()->isGraphOwner()); 78 79 m_desiredNumberOfChannels = numberOfChannels; 80 81 if (context()->isAudioThread()) { 82 // 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). 83 updateNumberOfChannels(); 84 } else { 85 ASSERT(!m_didCallDispose); 86 // Let the context take care of it in the audio thread in the pre and post render tasks. 87 context()->markAudioNodeOutputDirty(this); 88 } 89} 90 91void AudioNodeOutput::updateInternalBus() 92{ 93 if (numberOfChannels() == m_internalBus->numberOfChannels()) 94 return; 95 96 m_internalBus = AudioBus::create(numberOfChannels(), AudioNode::ProcessingSizeInFrames); 97} 98 99void AudioNodeOutput::updateRenderingState() 100{ 101 updateNumberOfChannels(); 102 m_renderingFanOutCount = fanOutCount(); 103 m_renderingParamFanOutCount = paramFanOutCount(); 104} 105 106void AudioNodeOutput::updateNumberOfChannels() 107{ 108 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 109 110 if (m_numberOfChannels != m_desiredNumberOfChannels) { 111 m_numberOfChannels = m_desiredNumberOfChannels; 112 updateInternalBus(); 113 propagateChannelCount(); 114 } 115} 116 117void AudioNodeOutput::propagateChannelCount() 118{ 119 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 120 121 if (isChannelCountKnown()) { 122 // Announce to any nodes we're connected to that we changed our channel count for its input. 123 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) 124 i->value->checkNumberOfChannelsForInput(i->key); 125 } 126} 127 128AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess) 129{ 130 ASSERT(context()->isAudioThread()); 131 ASSERT(m_renderingFanOutCount > 0 || m_renderingParamFanOutCount > 0); 132 133 // Causes our AudioNode to process if it hasn't already for this render quantum. 134 // We try to do in-place processing (using inPlaceBus) if at all possible, 135 // but we can't process in-place if we're connected to more than one input (fan-out > 1). 136 // In this case pull() is called multiple times per rendering quantum, and the processIfNecessary() call below will 137 // cause our node to process() only the first time, caching the output in m_internalOutputBus for subsequent calls. 138 139 m_isInPlace = inPlaceBus && inPlaceBus->numberOfChannels() == numberOfChannels() && (m_renderingFanOutCount + m_renderingParamFanOutCount) == 1; 140 141 m_inPlaceBus = m_isInPlace ? inPlaceBus : 0; 142 143 node()->processIfNecessary(framesToProcess); 144 return bus(); 145} 146 147AudioBus* AudioNodeOutput::bus() const 148{ 149 ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread()); 150 return m_isInPlace ? m_inPlaceBus.get() : m_internalBus.get(); 151} 152 153unsigned AudioNodeOutput::fanOutCount() 154{ 155 ASSERT(context()->isGraphOwner()); 156 return m_inputs.size(); 157} 158 159unsigned AudioNodeOutput::paramFanOutCount() 160{ 161 ASSERT(context()->isGraphOwner()); 162 return m_params.size(); 163} 164 165unsigned AudioNodeOutput::renderingFanOutCount() const 166{ 167 return m_renderingFanOutCount; 168} 169 170void AudioNodeOutput::addInput(AudioNodeInput& input) 171{ 172 ASSERT(context()->isGraphOwner()); 173 m_inputs.add(&input, &input.node()); 174 input.node().makeConnection(); 175} 176 177void AudioNodeOutput::removeInput(AudioNodeInput& input) 178{ 179 ASSERT(context()->isGraphOwner()); 180 input.node().breakConnection(); 181 m_inputs.remove(&input); 182} 183 184void AudioNodeOutput::disconnectAllInputs() 185{ 186 ASSERT(context()->isGraphOwner()); 187 188 // AudioNodeInput::disconnect() changes m_inputs by calling removeInput(). 189 while (!m_inputs.isEmpty()) 190 m_inputs.begin()->key->disconnect(*this); 191} 192 193void AudioNodeOutput::addParam(AudioParam& param) 194{ 195 ASSERT(context()->isGraphOwner()); 196 m_params.add(¶m); 197} 198 199void AudioNodeOutput::removeParam(AudioParam& param) 200{ 201 ASSERT(context()->isGraphOwner()); 202 m_params.remove(¶m); 203} 204 205void AudioNodeOutput::disconnectAllParams() 206{ 207 ASSERT(context()->isGraphOwner()); 208 209 // AudioParam::disconnect() changes m_params by calling removeParam(). 210 while (!m_params.isEmpty()) 211 (*m_params.begin())->disconnect(*this); 212} 213 214void AudioNodeOutput::disconnectAll() 215{ 216 disconnectAllInputs(); 217 disconnectAllParams(); 218} 219 220void AudioNodeOutput::disable() 221{ 222 ASSERT(context()->isGraphOwner()); 223 224 if (m_isEnabled) { 225 m_isEnabled = false; 226 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) 227 i->key->disable(*this); 228 } 229} 230 231void AudioNodeOutput::enable() 232{ 233 ASSERT(context()->isGraphOwner()); 234 235 if (!m_isEnabled) { 236 m_isEnabled = true; 237 for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) 238 i->key->enable(*this); 239 } 240} 241 242} // namespace blink 243 244#endif // ENABLE(WEB_AUDIO) 245