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