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 "AudioNodeOutput.h"
30
31#include "AudioBus.h"
32#include "AudioContext.h"
33#include "AudioNodeInput.h"
34#include "AudioParam.h"
35#include <wtf/Threading.h>
36
37namespace WebCore {
38
39AudioNodeOutput::AudioNodeOutput(AudioNode* node, unsigned numberOfChannels)
40    : m_node(node)
41    , m_numberOfChannels(numberOfChannels)
42    , m_desiredNumberOfChannels(numberOfChannels)
43    , m_isInPlace(false)
44    , m_isEnabled(true)
45    , m_renderingFanOutCount(0)
46    , m_renderingParamFanOutCount(0)
47{
48    ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels());
49
50    m_internalBus = AudioBus::create(numberOfChannels, AudioNode::ProcessingSizeInFrames);
51}
52
53void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels)
54{
55    ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels());
56    ASSERT(context()->isGraphOwner());
57
58    m_desiredNumberOfChannels = numberOfChannels;
59
60    if (context()->isAudioThread()) {
61        // If we're in the audio thread then we can take care of it right away (we should be at the very start or end of a rendering quantum).
62        updateNumberOfChannels();
63    } else {
64        // Let the context take care of it in the audio thread in the pre and post render tasks.
65        context()->markAudioNodeOutputDirty(this);
66    }
67}
68
69void AudioNodeOutput::updateInternalBus()
70{
71    if (numberOfChannels() == m_internalBus->numberOfChannels())
72        return;
73
74    m_internalBus = AudioBus::create(numberOfChannels(), AudioNode::ProcessingSizeInFrames);
75}
76
77void AudioNodeOutput::updateRenderingState()
78{
79    updateNumberOfChannels();
80    m_renderingFanOutCount = fanOutCount();
81    m_renderingParamFanOutCount = paramFanOutCount();
82}
83
84void AudioNodeOutput::updateNumberOfChannels()
85{
86    ASSERT(context()->isAudioThread() && context()->isGraphOwner());
87
88    if (m_numberOfChannels != m_desiredNumberOfChannels) {
89        m_numberOfChannels = m_desiredNumberOfChannels;
90        updateInternalBus();
91        propagateChannelCount();
92    }
93}
94
95void AudioNodeOutput::propagateChannelCount()
96{
97    ASSERT(context()->isAudioThread() && context()->isGraphOwner());
98
99    if (isChannelCountKnown()) {
100        // Announce to any nodes we're connected to that we changed our channel count for its input.
101        for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
102            AudioNodeInput* input = *i;
103            AudioNode* connectionNode = input->node();
104            connectionNode->checkNumberOfChannelsForInput(input);
105        }
106    }
107}
108
109AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
110{
111    ASSERT(context()->isAudioThread());
112    ASSERT(m_renderingFanOutCount > 0 || m_renderingParamFanOutCount > 0);
113
114    // Causes our AudioNode to process if it hasn't already for this render quantum.
115    // We try to do in-place processing (using inPlaceBus) if at all possible,
116    // but we can't process in-place if we're connected to more than one input (fan-out > 1).
117    // In this case pull() is called multiple times per rendering quantum, and the processIfNecessary() call below will
118    // cause our node to process() only the first time, caching the output in m_internalOutputBus for subsequent calls.
119
120    m_isInPlace = inPlaceBus && inPlaceBus->numberOfChannels() == numberOfChannels() && (m_renderingFanOutCount + m_renderingParamFanOutCount) == 1;
121
122    m_inPlaceBus = m_isInPlace ? inPlaceBus : 0;
123
124    node()->processIfNecessary(framesToProcess);
125    return bus();
126}
127
128AudioBus* AudioNodeOutput::bus() const
129{
130    ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread());
131    return m_isInPlace ? m_inPlaceBus.get() : m_internalBus.get();
132}
133
134unsigned AudioNodeOutput::fanOutCount()
135{
136    ASSERT(context()->isGraphOwner());
137    return m_inputs.size();
138}
139
140unsigned AudioNodeOutput::paramFanOutCount()
141{
142    ASSERT(context()->isGraphOwner());
143    return m_params.size();
144}
145
146unsigned AudioNodeOutput::renderingFanOutCount() const
147{
148    return m_renderingFanOutCount;
149}
150
151unsigned AudioNodeOutput::renderingParamFanOutCount() const
152{
153    return m_renderingParamFanOutCount;
154}
155
156void AudioNodeOutput::addInput(AudioNodeInput* input)
157{
158    ASSERT(context()->isGraphOwner());
159
160    ASSERT(input);
161    if (!input)
162        return;
163
164    m_inputs.add(input);
165}
166
167void AudioNodeOutput::removeInput(AudioNodeInput* input)
168{
169    ASSERT(context()->isGraphOwner());
170
171    ASSERT(input);
172    if (!input)
173        return;
174
175    m_inputs.remove(input);
176}
177
178void AudioNodeOutput::disconnectAllInputs()
179{
180    ASSERT(context()->isGraphOwner());
181
182    // AudioNodeInput::disconnect() changes m_inputs by calling removeInput().
183    while (!m_inputs.isEmpty()) {
184        AudioNodeInput* input = *m_inputs.begin();
185        input->disconnect(this);
186    }
187}
188
189void AudioNodeOutput::addParam(AudioParam* param)
190{
191    ASSERT(context()->isGraphOwner());
192
193    ASSERT(param);
194    if (!param)
195        return;
196
197    m_params.add(param);
198}
199
200void AudioNodeOutput::removeParam(AudioParam* param)
201{
202    ASSERT(context()->isGraphOwner());
203
204    ASSERT(param);
205    if (!param)
206        return;
207
208    m_params.remove(param);
209}
210
211void AudioNodeOutput::disconnectAllParams()
212{
213    ASSERT(context()->isGraphOwner());
214
215    // AudioParam::disconnect() changes m_params by calling removeParam().
216    while (!m_params.isEmpty()) {
217        AudioParam* param = m_params.begin()->get();
218        param->disconnect(this);
219    }
220}
221
222void AudioNodeOutput::disconnectAll()
223{
224    disconnectAllInputs();
225    disconnectAllParams();
226}
227
228void AudioNodeOutput::disable()
229{
230    ASSERT(context()->isGraphOwner());
231
232    if (m_isEnabled) {
233        for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
234            AudioNodeInput* input = *i;
235            input->disable(this);
236        }
237        m_isEnabled = false;
238    }
239}
240
241void AudioNodeOutput::enable()
242{
243    ASSERT(context()->isGraphOwner());
244
245    if (!m_isEnabled) {
246        for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
247            AudioNodeInput* input = *i;
248            input->enable(this);
249        }
250        m_isEnabled = true;
251    }
252}
253
254} // namespace WebCore
255
256#endif // ENABLE(WEB_AUDIO)
257