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
AudioNodeInput(AudioNode * node)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
connect(AudioNodeOutput * output)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
disconnect(AudioNodeOutput * output)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
disable(AudioNodeOutput * output)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
enable(AudioNodeOutput * output)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
changedOutputs()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
updateRenderingState()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
numberOfChannels() const162 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
numberOfRenderingChannels()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
bus()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
internalSummingBus()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
sumAllConnections(AudioBus * summingBus,size_t framesToProcess)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
pull(AudioBus * inPlaceBus,size_t framesToProcess)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