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 36 using namespace std; 37 38 namespace WebCore { 39 40 AudioNodeInput::AudioNodeInput(AudioNode* node) 41 : m_node(node) 42 , m_renderingStateNeedUpdating(false) 43 { 44 m_monoSummingBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames)); 45 m_stereoSummingBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames)); 46 } 47 48 void AudioNodeInput::connect(AudioNodeOutput* output) 49 { 50 ASSERT(context()->isGraphOwner()); 51 52 ASSERT(output && node()); 53 if (!output || !node()) 54 return; 55 56 // Check if we're already connected to this output. 57 if (m_outputs.contains(output)) 58 return; 59 60 output->addInput(this); 61 m_outputs.add(output); 62 changedOutputs(); 63 64 // Sombody has just connected to us, so count it as a reference. 65 node()->ref(AudioNode::RefTypeConnection); 66 } 67 68 void AudioNodeInput::disconnect(AudioNodeOutput* output) 69 { 70 ASSERT(context()->isGraphOwner()); 71 72 ASSERT(output && node()); 73 if (!output || !node()) 74 return; 75 76 // First try to disconnect from "active" connections. 77 if (m_outputs.contains(output)) { 78 m_outputs.remove(output); 79 changedOutputs(); 80 output->removeInput(this); 81 node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 82 return; 83 } 84 85 // Otherwise, try to disconnect from disabled connections. 86 if (m_disabledOutputs.contains(output)) { 87 m_disabledOutputs.remove(output); 88 output->removeInput(this); 89 node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 90 return; 91 } 92 93 ASSERT_NOT_REACHED(); 94 } 95 96 void AudioNodeInput::disable(AudioNodeOutput* output) 97 { 98 ASSERT(context()->isGraphOwner()); 99 100 ASSERT(output && node()); 101 if (!output || !node()) 102 return; 103 104 ASSERT(m_outputs.contains(output)); 105 106 m_disabledOutputs.add(output); 107 m_outputs.remove(output); 108 changedOutputs(); 109 110 node()->ref(AudioNode::RefTypeDisabled); 111 node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 112 } 113 114 void AudioNodeInput::enable(AudioNodeOutput* output) 115 { 116 ASSERT(context()->isGraphOwner()); 117 118 ASSERT(output && node()); 119 if (!output || !node()) 120 return; 121 122 ASSERT(m_disabledOutputs.contains(output)); 123 124 // Move output from disabled list to active list. 125 m_outputs.add(output); 126 m_disabledOutputs.remove(output); 127 changedOutputs(); 128 129 node()->ref(AudioNode::RefTypeConnection); 130 node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted. 131 } 132 133 void AudioNodeInput::changedOutputs() 134 { 135 ASSERT(context()->isGraphOwner()); 136 if (!m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) { 137 context()->markAudioNodeInputDirty(this); 138 m_renderingStateNeedUpdating = true; 139 } 140 } 141 142 void AudioNodeInput::updateRenderingState() 143 { 144 ASSERT(context()->isAudioThread() && context()->isGraphOwner()); 145 146 if (m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) { 147 // Copy from m_outputs to m_renderingOutputs. 148 m_renderingOutputs.resize(m_outputs.size()); 149 unsigned j = 0; 150 for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) { 151 AudioNodeOutput* output = *i; 152 m_renderingOutputs[j] = output; 153 output->updateRenderingState(); 154 } 155 156 node()->checkNumberOfChannelsForInput(this); 157 158 m_renderingStateNeedUpdating = false; 159 } 160 } 161 162 unsigned AudioNodeInput::numberOfChannels() const 163 { 164 // Find the number of channels of the connection with the largest number of channels. 165 unsigned maxChannels = 1; // one channel is the minimum allowed 166 167 for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) { 168 AudioNodeOutput* output = *i; 169 maxChannels = max(maxChannels, output->bus()->numberOfChannels()); 170 } 171 172 return maxChannels; 173 } 174 175 unsigned AudioNodeInput::numberOfRenderingChannels() 176 { 177 ASSERT(context()->isAudioThread()); 178 179 // Find the number of channels of the rendering connection with the largest number of channels. 180 unsigned maxChannels = 1; // one channel is the minimum allowed 181 182 for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) 183 maxChannels = max(maxChannels, renderingOutput(i)->bus()->numberOfChannels()); 184 185 return maxChannels; 186 } 187 188 AudioBus* AudioNodeInput::bus() 189 { 190 ASSERT(context()->isAudioThread()); 191 192 // Handle single connection specially to allow for in-place processing. 193 if (numberOfRenderingConnections() == 1) 194 return renderingOutput(0)->bus(); 195 196 // Multiple connections case (or no connections). 197 return internalSummingBus(); 198 } 199 200 AudioBus* AudioNodeInput::internalSummingBus() 201 { 202 ASSERT(context()->isAudioThread()); 203 204 // We must pick a summing bus which is the right size to handle the largest connection. 205 switch (numberOfRenderingChannels()) { 206 case 1: 207 return m_monoSummingBus.get(); 208 case 2: 209 return m_stereoSummingBus.get(); 210 // FIXME: could implement more than just mono and stereo mixing in the future 211 } 212 213 ASSERT_NOT_REACHED(); 214 return 0; 215 } 216 217 void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess) 218 { 219 ASSERT(context()->isAudioThread()); 220 221 // We shouldn't be calling this method if there's only one connection, since it's less efficient. 222 ASSERT(numberOfRenderingConnections() > 1); 223 224 ASSERT(summingBus); 225 if (!summingBus) 226 return; 227 228 summingBus->zero(); 229 230 for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) { 231 AudioNodeOutput* output = renderingOutput(i); 232 ASSERT(output); 233 234 // Render audio from this output. 235 AudioBus* connectionBus = output->pull(0, framesToProcess); 236 237 // Sum, with unity-gain. 238 summingBus->sumFrom(*connectionBus); 239 } 240 } 241 242 AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess) 243 { 244 ASSERT(context()->isAudioThread()); 245 246 // Handle single connection case. 247 if (numberOfRenderingConnections() == 1) { 248 // The output will optimize processing using inPlaceBus if it's able. 249 AudioNodeOutput* output = this->renderingOutput(0); 250 return output->pull(inPlaceBus, framesToProcess); 251 } 252 253 AudioBus* internalSummingBus = this->internalSummingBus(); 254 255 if (!numberOfRenderingConnections()) { 256 // At least, generate silence if we're not connected to anything. 257 // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing. 258 internalSummingBus->zero(); 259 return internalSummingBus; 260 } 261 262 // Handle multiple connections case. 263 sumAllConnections(internalSummingBus, framesToProcess); 264 265 return internalSummingBus; 266 } 267 268 } // namespace WebCore 269 270 #endif // ENABLE(WEB_AUDIO) 271