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
36using namespace std;
37
38namespace WebCore {
39
40AudioNodeInput::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
48void 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
68void 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
96void 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
114void 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
133void AudioNodeInput::changedOutputs()
134{
135    ASSERT(context()->isGraphOwner());
136    if (!m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) {
137        context()->markAudioNodeInputDirty(this);
138        m_renderingStateNeedUpdating = true;
139    }
140}
141
142void 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
162unsigned 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
175unsigned 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
188AudioBus* 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
200AudioBus* 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
217void 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
242AudioBus* 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