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 "ScriptProcessorNode.h"
30
31#include "AudioBuffer.h"
32#include "AudioBus.h"
33#include "AudioContext.h"
34#include "AudioNodeInput.h"
35#include "AudioNodeOutput.h"
36#include "AudioProcessingEvent.h"
37#include "Document.h"
38#include "EventNames.h"
39#include <JavaScriptCore/Float32Array.h>
40#include <wtf/IsoMallocInlines.h>
41#include <wtf/MainThread.h>
42
43namespace WebCore {
44
45WTF_MAKE_ISO_ALLOCATED_IMPL(ScriptProcessorNode);
46
47Ref<ScriptProcessorNode> ScriptProcessorNode::create(AudioContext& context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
48{
49 return adoptRef(*new ScriptProcessorNode(context, sampleRate, bufferSize, numberOfInputChannels, numberOfOutputChannels));
50}
51
52ScriptProcessorNode::ScriptProcessorNode(AudioContext& context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
53 : AudioNode(context, sampleRate)
54 , m_doubleBufferIndex(0)
55 , m_doubleBufferIndexForEvent(0)
56 , m_bufferSize(bufferSize)
57 , m_bufferReadWriteIndex(0)
58 , m_isRequestOutstanding(false)
59 , m_numberOfInputChannels(numberOfInputChannels)
60 , m_numberOfOutputChannels(numberOfOutputChannels)
61 , m_internalInputBus(AudioBus::create(numberOfInputChannels, AudioNode::ProcessingSizeInFrames, false))
62 , m_hasAudioProcessListener(false)
63{
64 // Regardless of the allowed buffer sizes, we still need to process at the granularity of the AudioNode.
65 if (m_bufferSize < AudioNode::ProcessingSizeInFrames)
66 m_bufferSize = AudioNode::ProcessingSizeInFrames;
67
68 ASSERT(numberOfInputChannels <= AudioContext::maxNumberOfChannels());
69
70 setNodeType(NodeTypeJavaScript);
71 addInput(std::make_unique<AudioNodeInput>(this));
72 addOutput(std::make_unique<AudioNodeOutput>(this, numberOfOutputChannels));
73
74 initialize();
75}
76
77ScriptProcessorNode::~ScriptProcessorNode()
78{
79 uninitialize();
80}
81
82void ScriptProcessorNode::initialize()
83{
84 if (isInitialized())
85 return;
86
87 float sampleRate = context().sampleRate();
88
89 // Create double buffers on both the input and output sides.
90 // These AudioBuffers will be directly accessed in the main thread by JavaScript.
91 for (unsigned i = 0; i < 2; ++i) {
92 auto inputBuffer = m_numberOfInputChannels ? AudioBuffer::create(m_numberOfInputChannels, bufferSize(), sampleRate) : 0;
93 auto outputBuffer = m_numberOfOutputChannels ? AudioBuffer::create(m_numberOfOutputChannels, bufferSize(), sampleRate) : 0;
94
95 m_inputBuffers.append(inputBuffer);
96 m_outputBuffers.append(outputBuffer);
97 }
98
99 AudioNode::initialize();
100}
101
102void ScriptProcessorNode::uninitialize()
103{
104 if (!isInitialized())
105 return;
106
107 m_inputBuffers.clear();
108 m_outputBuffers.clear();
109
110 AudioNode::uninitialize();
111}
112
113void ScriptProcessorNode::process(size_t framesToProcess)
114{
115 // Discussion about inputs and outputs:
116 // As in other AudioNodes, ScriptProcessorNode uses an AudioBus for its input and output (see inputBus and outputBus below).
117 // Additionally, there is a double-buffering for input and output which is exposed directly to JavaScript (see inputBuffer and outputBuffer below).
118 // This node is the producer for inputBuffer and the consumer for outputBuffer.
119 // The JavaScript code is the consumer of inputBuffer and the producer for outputBuffer.
120
121 // Check if audioprocess listener is set.
122 if (!m_hasAudioProcessListener)
123 return;
124
125 // Get input and output busses.
126 AudioBus* inputBus = this->input(0)->bus();
127 AudioBus* outputBus = this->output(0)->bus();
128
129 // Get input and output buffers. We double-buffer both the input and output sides.
130 unsigned doubleBufferIndex = this->doubleBufferIndex();
131 bool isDoubleBufferIndexGood = doubleBufferIndex < 2 && doubleBufferIndex < m_inputBuffers.size() && doubleBufferIndex < m_outputBuffers.size();
132 ASSERT(isDoubleBufferIndexGood);
133 if (!isDoubleBufferIndexGood)
134 return;
135
136 AudioBuffer* inputBuffer = m_inputBuffers[doubleBufferIndex].get();
137 AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get();
138
139 // Check the consistency of input and output buffers.
140 unsigned numberOfInputChannels = m_internalInputBus->numberOfChannels();
141 bool buffersAreGood = outputBuffer && bufferSize() == outputBuffer->length() && m_bufferReadWriteIndex + framesToProcess <= bufferSize();
142
143 // If the number of input channels is zero, it's ok to have inputBuffer = 0.
144 if (m_internalInputBus->numberOfChannels())
145 buffersAreGood = buffersAreGood && inputBuffer && bufferSize() == inputBuffer->length();
146
147 ASSERT(buffersAreGood);
148 if (!buffersAreGood)
149 return;
150
151 // We assume that bufferSize() is evenly divisible by framesToProcess - should always be true, but we should still check.
152 bool isFramesToProcessGood = framesToProcess && bufferSize() >= framesToProcess && !(bufferSize() % framesToProcess);
153 ASSERT(isFramesToProcessGood);
154 if (!isFramesToProcessGood)
155 return;
156
157 unsigned numberOfOutputChannels = outputBus->numberOfChannels();
158
159 bool channelsAreGood = (numberOfInputChannels == m_numberOfInputChannels) && (numberOfOutputChannels == m_numberOfOutputChannels);
160 ASSERT(channelsAreGood);
161 if (!channelsAreGood)
162 return;
163
164 for (unsigned i = 0; i < numberOfInputChannels; i++)
165 m_internalInputBus->setChannelMemory(i, inputBuffer->channelData(i)->data() + m_bufferReadWriteIndex, framesToProcess);
166
167 if (numberOfInputChannels)
168 m_internalInputBus->copyFrom(*inputBus);
169
170 // Copy from the output buffer to the output.
171 for (unsigned i = 0; i < numberOfOutputChannels; ++i)
172 memcpy(outputBus->channel(i)->mutableData(), outputBuffer->channelData(i)->data() + m_bufferReadWriteIndex, sizeof(float) * framesToProcess);
173
174 // Update the buffering index.
175 m_bufferReadWriteIndex = (m_bufferReadWriteIndex + framesToProcess) % bufferSize();
176
177 // m_bufferReadWriteIndex will wrap back around to 0 when the current input and output buffers are full.
178 // When this happens, fire an event and swap buffers.
179 if (!m_bufferReadWriteIndex) {
180 // Avoid building up requests on the main thread to fire process events when they're not being handled.
181 // This could be a problem if the main thread is very busy doing other things and is being held up handling previous requests.
182 if (m_isRequestOutstanding) {
183 // We're late in handling the previous request. The main thread must be very busy.
184 // The best we can do is clear out the buffer ourself here.
185 outputBuffer->zero();
186 } else {
187 // Reference ourself so we don't accidentally get deleted before fireProcessEvent() gets called.
188 ref();
189
190 // Fire the event on the main thread, not this one (which is the realtime audio thread).
191 m_doubleBufferIndexForEvent = m_doubleBufferIndex;
192 m_isRequestOutstanding = true;
193
194 callOnMainThread([this] {
195 if (!m_hasAudioProcessListener)
196 return;
197
198 fireProcessEvent();
199
200 // De-reference to match the ref() call in process().
201 deref();
202 });
203 }
204
205 swapBuffers();
206 }
207}
208
209void ScriptProcessorNode::fireProcessEvent()
210{
211 ASSERT(isMainThread() && m_isRequestOutstanding);
212
213 bool isIndexGood = m_doubleBufferIndexForEvent < 2;
214 ASSERT(isIndexGood);
215 if (!isIndexGood)
216 return;
217
218 AudioBuffer* inputBuffer = m_inputBuffers[m_doubleBufferIndexForEvent].get();
219 AudioBuffer* outputBuffer = m_outputBuffers[m_doubleBufferIndexForEvent].get();
220 ASSERT(outputBuffer);
221 if (!outputBuffer)
222 return;
223
224 // Avoid firing the event if the document has already gone away.
225 if (!context().isStopped()) {
226 // Let the audio thread know we've gotten to the point where it's OK for it to make another request.
227 m_isRequestOutstanding = false;
228
229 // Calculate playbackTime with the buffersize which needs to be processed each time when onaudioprocess is called.
230 // The outputBuffer being passed to JS will be played after exhausting previous outputBuffer by double-buffering.
231 double playbackTime = (context().currentSampleFrame() + m_bufferSize) / static_cast<double>(context().sampleRate());
232
233 // Call the JavaScript event handler which will do the audio processing.
234 dispatchEvent(AudioProcessingEvent::create(inputBuffer, outputBuffer, playbackTime));
235 }
236}
237
238void ScriptProcessorNode::reset()
239{
240 m_bufferReadWriteIndex = 0;
241 m_doubleBufferIndex = 0;
242
243 for (unsigned i = 0; i < 2; ++i) {
244 m_inputBuffers[i]->zero();
245 m_outputBuffers[i]->zero();
246 }
247}
248
249double ScriptProcessorNode::tailTime() const
250{
251 return std::numeric_limits<double>::infinity();
252}
253
254double ScriptProcessorNode::latencyTime() const
255{
256 return std::numeric_limits<double>::infinity();
257}
258
259bool ScriptProcessorNode::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
260{
261 bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
262 if (success && eventType == eventNames().audioprocessEvent)
263 m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
264 return success;
265}
266
267bool ScriptProcessorNode::removeEventListener(const AtomString& eventType, EventListener& listener, const ListenerOptions& options)
268{
269 bool success = AudioNode::removeEventListener(eventType, listener, options);
270 if (success && eventType == eventNames().audioprocessEvent)
271 m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
272 return success;
273}
274
275void ScriptProcessorNode::removeAllEventListeners()
276{
277 m_hasAudioProcessListener = false;
278 AudioNode::removeAllEventListeners();
279}
280
281} // namespace WebCore
282
283#endif // ENABLE(WEB_AUDIO)
284