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 "AudioNodeInput.h" 30 31#include "AudioContext.h" 32#include "AudioNode.h" 33#include "AudioNodeOutput.h" 34#include <algorithm> 35 36namespace WebCore { 37 38AudioNodeInput::AudioNodeInput(AudioNode* node) 39 : AudioSummingJunction(node->context()) 40 , m_node(node) 41{ 42 // Set to mono by default. 43 m_internalSummingBus = AudioBus::create(1, AudioNode::ProcessingSizeInFrames); 44} 45 46void AudioNodeInput::connect(AudioNodeOutput* output) 47{ 48 ASSERT(context()->isGraphOwner()); 49 50 ASSERT(output && node()); 51 if (!output || !node()) 52 return; 53 54 // Check if we're already connected to this output. 55 if (!m_outputs.add(output).isNewEntry) 56 return; 57 58 output->addInput(this); 59 changedOutputs(); 60 61 // Sombody has just connected to us, so count it as a reference. 62 node()->ref(AudioNode::RefTypeConnection); 63} 64 65void AudioNodeInput::disconnect(AudioNodeOutput* output) 66{ 67 ASSERT(context()->isGraphOwner()); 68 69 ASSERT(output && node()); 70 if (!output || !node()) 71 return; 72 73 // First try to disconnect from "active" connections. 74 if (m_outputs.remove(output)) { 75 changedOutputs(); 76 output->removeInput(this); 77 node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 78 return; 79 } 80 81 // Otherwise, try to disconnect from disabled connections. 82 if (m_disabledOutputs.remove(output)) { 83 output->removeInput(this); 84 node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 85 return; 86 } 87 88 ASSERT_NOT_REACHED(); 89} 90 91void AudioNodeInput::disable(AudioNodeOutput* output) 92{ 93 ASSERT(context()->isGraphOwner()); 94 95 ASSERT(output && node()); 96 if (!output || !node()) 97 return; 98 99 ASSERT(m_outputs.contains(output)); 100 101 m_disabledOutputs.add(output); 102 m_outputs.remove(output); 103 changedOutputs(); 104 105 // Propagate disabled state to outputs. 106 node()->disableOutputsIfNecessary(); 107} 108 109void AudioNodeInput::enable(AudioNodeOutput* output) 110{ 111 ASSERT(context()->isGraphOwner()); 112 113 ASSERT(output && node()); 114 if (!output || !node()) 115 return; 116 117 ASSERT(m_disabledOutputs.contains(output)); 118 119 // Move output from disabled list to active list. 120 m_outputs.add(output); 121 m_disabledOutputs.remove(output); 122 changedOutputs(); 123 124 // Propagate enabled state to outputs. 125 node()->enableOutputsIfNecessary(); 126} 127 128void AudioNodeInput::didUpdate() 129{ 130 node()->checkNumberOfChannelsForInput(this); 131} 132 133void AudioNodeInput::updateInternalBus() 134{ 135 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 136 137 unsigned numberOfInputChannels = numberOfChannels(); 138 139 if (numberOfInputChannels == m_internalSummingBus->numberOfChannels()) 140 return; 141 142 m_internalSummingBus = AudioBus::create(numberOfInputChannels, AudioNode::ProcessingSizeInFrames); 143} 144 145unsigned AudioNodeInput::numberOfChannels() const 146{ 147 AudioNode::ChannelCountMode mode = node()->internalChannelCountMode(); 148 if (mode == AudioNode::Explicit) 149 return node()->channelCount(); 150 151 // Find the number of channels of the connection with the largest number of channels. 152 unsigned maxChannels = 1; // one channel is the minimum allowed 153 154 for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) { 155 AudioNodeOutput* output = *i; 156 // Use output()->numberOfChannels() instead of output->bus()->numberOfChannels(), 157 // because the calling of AudioNodeOutput::bus() is not safe here. 158 maxChannels = std::max(maxChannels, output->numberOfChannels()); 159 } 160 161 if (mode == AudioNode::ClampedMax) 162 maxChannels = std::min(maxChannels, static_cast<unsigned>(node()->channelCount())); 163 164 return maxChannels; 165} 166 167AudioBus* AudioNodeInput::bus() 168{ 169 ASSERT(context()->isAudioThread()); 170 171 // Handle single connection specially to allow for in-place processing. 172 if (numberOfRenderingConnections() == 1 && node()->internalChannelCountMode() == AudioNode::Max) 173 return renderingOutput(0)->bus(); 174 175 // Multiple connections case or complex ChannelCountMode (or no connections). 176 return internalSummingBus(); 177} 178 179AudioBus* AudioNodeInput::internalSummingBus() 180{ 181 ASSERT(context()->isAudioThread()); 182 183 return m_internalSummingBus.get(); 184} 185 186void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess) 187{ 188 ASSERT(context()->isAudioThread()); 189 190 // We shouldn't be calling this method if there's only one connection, since it's less efficient. 191 ASSERT(numberOfRenderingConnections() > 1 || node()->internalChannelCountMode() != AudioNode::Max); 192 193 ASSERT(summingBus); 194 if (!summingBus) 195 return; 196 197 summingBus->zero(); 198 199 AudioBus::ChannelInterpretation interpretation = node()->internalChannelInterpretation(); 200 201 for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) { 202 AudioNodeOutput* output = renderingOutput(i); 203 ASSERT(output); 204 205 // Render audio from this output. 206 AudioBus* connectionBus = output->pull(0, framesToProcess); 207 208 // Sum, with unity-gain. 209 summingBus->sumFrom(*connectionBus, interpretation); 210 } 211} 212 213AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess) 214{ 215 ASSERT(context()->isAudioThread()); 216 217 // Handle single connection case. 218 if (numberOfRenderingConnections() == 1 && node()->internalChannelCountMode() == AudioNode::Max) { 219 // The output will optimize processing using inPlaceBus if it's able. 220 AudioNodeOutput* output = this->renderingOutput(0); 221 return output->pull(inPlaceBus, framesToProcess); 222 } 223 224 AudioBus* internalSummingBus = this->internalSummingBus(); 225 226 if (!numberOfRenderingConnections()) { 227 // At least, generate silence if we're not connected to anything. 228 // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing. 229 internalSummingBus->zero(); 230 return internalSummingBus; 231 } 232 233 // Handle multiple connections case. 234 sumAllConnections(internalSummingBus, framesToProcess); 235 236 return internalSummingBus; 237} 238 239} // namespace WebCore 240 241#endif // ENABLE(WEB_AUDIO) 242