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