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