• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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