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 "core/platform/audio/AudioBus.h" 32 #include "modules/webaudio/AudioContext.h" 33 #include "modules/webaudio/AudioNodeInput.h" 34 #include "modules/webaudio/AudioParam.h" 35 #include "wtf/Threading.h" 36 37 namespace WebCore { 38 39 AudioNodeOutput::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 53 void 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 69 void AudioNodeOutput::updateInternalBus() 70 { 71 if (numberOfChannels() == m_internalBus->numberOfChannels()) 72 return; 73 74 m_internalBus = AudioBus::create(numberOfChannels(), AudioNode::ProcessingSizeInFrames); 75 } 76 77 void AudioNodeOutput::updateRenderingState() 78 { 79 updateNumberOfChannels(); 80 m_renderingFanOutCount = fanOutCount(); 81 m_renderingParamFanOutCount = paramFanOutCount(); 82 } 83 84 void 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 95 void 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 109 AudioBus* 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 128 AudioBus* AudioNodeOutput::bus() const 129 { 130 ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread()); 131 return m_isInPlace ? m_inPlaceBus.get() : m_internalBus.get(); 132 } 133 134 unsigned AudioNodeOutput::fanOutCount() 135 { 136 ASSERT(context()->isGraphOwner()); 137 return m_inputs.size(); 138 } 139 140 unsigned AudioNodeOutput::paramFanOutCount() 141 { 142 ASSERT(context()->isGraphOwner()); 143 return m_params.size(); 144 } 145 146 unsigned AudioNodeOutput::renderingFanOutCount() const 147 { 148 return m_renderingFanOutCount; 149 } 150 151 unsigned AudioNodeOutput::renderingParamFanOutCount() const 152 { 153 return m_renderingParamFanOutCount; 154 } 155 156 void 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 167 void 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 178 void 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 189 void 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 200 void 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 211 void 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 222 void AudioNodeOutput::disconnectAll() 223 { 224 disconnectAllInputs(); 225 disconnectAllParams(); 226 } 227 228 void 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 241 void 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