• 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 "JavaScriptAudioNode.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 "Float32Array.h"
39 #include <wtf/MainThread.h>
40 
41 namespace WebCore {
42 
43 const size_t DefaultBufferSize = 4096;
44 
create(AudioContext * context,double sampleRate,size_t bufferSize,unsigned numberOfInputs,unsigned numberOfOutputs)45 PassRefPtr<JavaScriptAudioNode> JavaScriptAudioNode::create(AudioContext* context, double sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs)
46 {
47     return adoptRef(new JavaScriptAudioNode(context, sampleRate, bufferSize, numberOfInputs, numberOfOutputs));
48 }
49 
JavaScriptAudioNode(AudioContext * context,double sampleRate,size_t bufferSize,unsigned numberOfInputs,unsigned numberOfOutputs)50 JavaScriptAudioNode::JavaScriptAudioNode(AudioContext* context, double sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs)
51     : AudioNode(context, sampleRate)
52     , m_doubleBufferIndex(0)
53     , m_doubleBufferIndexForEvent(0)
54     , m_bufferSize(bufferSize)
55     , m_bufferReadWriteIndex(0)
56     , m_isRequestOutstanding(false)
57 {
58     // Check for valid buffer size.
59     switch (bufferSize) {
60     case 256:
61     case 512:
62     case 1024:
63     case 2048:
64     case 4096:
65     case 8192:
66     case 16384:
67         m_bufferSize = bufferSize;
68         break;
69     default:
70         m_bufferSize = DefaultBufferSize;
71     }
72 
73     // Regardless of the allowed buffer sizes above, we still need to process at the granularity of the AudioNode.
74     if (m_bufferSize < AudioNode::ProcessingSizeInFrames)
75         m_bufferSize = AudioNode::ProcessingSizeInFrames;
76 
77     // FIXME: Right now we're hardcoded to single input and single output.
78     // Although the specification says this is OK for a simple implementation, multiple inputs and outputs would be good.
79     ASSERT_UNUSED(numberOfInputs, numberOfInputs == 1);
80     ASSERT_UNUSED(numberOfOutputs, numberOfOutputs == 1);
81     addInput(adoptPtr(new AudioNodeInput(this)));
82     addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
83 
84     setType(NodeTypeJavaScript);
85 
86     initialize();
87 }
88 
~JavaScriptAudioNode()89 JavaScriptAudioNode::~JavaScriptAudioNode()
90 {
91     uninitialize();
92 }
93 
initialize()94 void JavaScriptAudioNode::initialize()
95 {
96     if (isInitialized())
97         return;
98 
99     double sampleRate = context()->sampleRate();
100 
101     // Create double buffers on both the input and output sides.
102     // These AudioBuffers will be directly accessed in the main thread by JavaScript.
103     for (unsigned i = 0; i < 2; ++i) {
104         m_inputBuffers.append(AudioBuffer::create(2, bufferSize(), sampleRate));
105         m_outputBuffers.append(AudioBuffer::create(2, bufferSize(), sampleRate));
106     }
107 
108     AudioNode::initialize();
109 }
110 
uninitialize()111 void JavaScriptAudioNode::uninitialize()
112 {
113     if (!isInitialized())
114         return;
115 
116     m_inputBuffers.clear();
117     m_outputBuffers.clear();
118 
119     AudioNode::uninitialize();
120 }
121 
toJavaScriptAudioNode()122 JavaScriptAudioNode* JavaScriptAudioNode::toJavaScriptAudioNode()
123 {
124     return this;
125 }
126 
process(size_t framesToProcess)127 void JavaScriptAudioNode::process(size_t framesToProcess)
128 {
129     // Discussion about inputs and outputs:
130     // As in other AudioNodes, JavaScriptAudioNode uses an AudioBus for its input and output (see inputBus and outputBus below).
131     // Additionally, there is a double-buffering for input and output which is exposed directly to JavaScript (see inputBuffer and outputBuffer below).
132     // This node is the producer for inputBuffer and the consumer for outputBuffer.
133     // The JavaScript code is the consumer of inputBuffer and the producer for outputBuffer.
134 
135     // Get input and output busses.
136     AudioBus* inputBus = this->input(0)->bus();
137     AudioBus* outputBus = this->output(0)->bus();
138 
139     // Get input and output buffers.  We double-buffer both the input and output sides.
140     unsigned doubleBufferIndex = this->doubleBufferIndex();
141     bool isDoubleBufferIndexGood = doubleBufferIndex < 2 && doubleBufferIndex < m_inputBuffers.size() && doubleBufferIndex < m_outputBuffers.size();
142     ASSERT(isDoubleBufferIndexGood);
143     if (!isDoubleBufferIndexGood)
144         return;
145 
146     AudioBuffer* inputBuffer = m_inputBuffers[doubleBufferIndex].get();
147     AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get();
148 
149     // Check the consistency of input and output buffers.
150     bool buffersAreGood = inputBuffer && outputBuffer && bufferSize() == inputBuffer->length() && bufferSize() == outputBuffer->length()
151         && m_bufferReadWriteIndex + framesToProcess <= bufferSize();
152     ASSERT(buffersAreGood);
153     if (!buffersAreGood)
154         return;
155 
156     // We assume that bufferSize() is evenly divisible by framesToProcess - should always be true, but we should still check.
157     bool isFramesToProcessGood = framesToProcess && bufferSize() >= framesToProcess && !(bufferSize() % framesToProcess);
158     ASSERT(isFramesToProcessGood);
159     if (!isFramesToProcessGood)
160         return;
161 
162     unsigned numberOfInputChannels = inputBus->numberOfChannels();
163 
164     bool channelsAreGood = (numberOfInputChannels == 1 || numberOfInputChannels == 2) && outputBus->numberOfChannels() == 2;
165     ASSERT(channelsAreGood);
166     if (!channelsAreGood)
167         return;
168 
169     float* sourceL = inputBus->channel(0)->data();
170     float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : 0;
171     float* destinationL = outputBus->channel(0)->data();
172     float* destinationR = outputBus->channel(1)->data();
173 
174     // Copy from the input to the input buffer.  See "buffersAreGood" check above for safety.
175     size_t bytesToCopy = sizeof(float) * framesToProcess;
176     memcpy(inputBuffer->getChannelData(0)->data() + m_bufferReadWriteIndex, sourceL, bytesToCopy);
177 
178     if (numberOfInputChannels == 2)
179         memcpy(inputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, sourceR, bytesToCopy);
180     else if (numberOfInputChannels == 1) {
181         // If the input is mono, then also copy the mono input to the right channel of the AudioBuffer which the AudioProcessingEvent uses.
182         // FIXME: it is likely the audio API will evolve to present an AudioBuffer with the same number of channels as our input.
183         memcpy(inputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, sourceL, bytesToCopy);
184     }
185 
186     // Copy from the output buffer to the output.  See "buffersAreGood" check above for safety.
187     memcpy(destinationL, outputBuffer->getChannelData(0)->data() + m_bufferReadWriteIndex, bytesToCopy);
188     memcpy(destinationR, outputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, bytesToCopy);
189 
190     // Update the buffering index.
191     m_bufferReadWriteIndex = (m_bufferReadWriteIndex + framesToProcess) % bufferSize();
192 
193     // m_bufferReadWriteIndex will wrap back around to 0 when the current input and output buffers are full.
194     // When this happens, fire an event and swap buffers.
195     if (!m_bufferReadWriteIndex) {
196         // Avoid building up requests on the main thread to fire process events when they're not being handled.
197         // This could be a problem if the main thread is very busy doing other things and is being held up handling previous requests.
198         if (m_isRequestOutstanding) {
199             // We're late in handling the previous request.  The main thread must be very busy.
200             // The best we can do is clear out the buffer ourself here.
201             outputBuffer->zero();
202         } else {
203             // Reference ourself so we don't accidentally get deleted before fireProcessEvent() gets called.
204             ref();
205 
206             // Fire the event on the main thread, not this one (which is the realtime audio thread).
207             m_doubleBufferIndexForEvent = m_doubleBufferIndex;
208             m_isRequestOutstanding = true;
209             callOnMainThread(fireProcessEventDispatch, this);
210         }
211 
212         swapBuffers();
213     }
214 }
215 
fireProcessEventDispatch(void * userData)216 void JavaScriptAudioNode::fireProcessEventDispatch(void* userData)
217 {
218     JavaScriptAudioNode* jsAudioNode = static_cast<JavaScriptAudioNode*>(userData);
219     ASSERT(jsAudioNode);
220     if (!jsAudioNode)
221         return;
222 
223     jsAudioNode->fireProcessEvent();
224 
225     // De-reference to match the ref() call in process().
226     jsAudioNode->deref();
227 }
228 
fireProcessEvent()229 void JavaScriptAudioNode::fireProcessEvent()
230 {
231     ASSERT(isMainThread() && m_isRequestOutstanding);
232 
233     bool isIndexGood = m_doubleBufferIndexForEvent < 2;
234     ASSERT(isIndexGood);
235     if (!isIndexGood)
236         return;
237 
238     AudioBuffer* inputBuffer = m_inputBuffers[m_doubleBufferIndexForEvent].get();
239     AudioBuffer* outputBuffer = m_outputBuffers[m_doubleBufferIndexForEvent].get();
240     ASSERT(inputBuffer && outputBuffer);
241     if (!inputBuffer || !outputBuffer)
242         return;
243 
244     // Avoid firing the event if the document has already gone away.
245     if (context()->hasDocument()) {
246         // Let the audio thread know we've gotten to the point where it's OK for it to make another request.
247         m_isRequestOutstanding = false;
248 
249         // Call the JavaScript event handler which will do the audio processing.
250         dispatchEvent(AudioProcessingEvent::create(inputBuffer, outputBuffer));
251     }
252 }
253 
reset()254 void JavaScriptAudioNode::reset()
255 {
256     m_bufferReadWriteIndex = 0;
257     m_doubleBufferIndex = 0;
258 
259     for (unsigned i = 0; i < 2; ++i) {
260         m_inputBuffers[i]->zero();
261         m_outputBuffers[i]->zero();
262     }
263 }
264 
scriptExecutionContext() const265 ScriptExecutionContext* JavaScriptAudioNode::scriptExecutionContext() const
266 {
267     return const_cast<JavaScriptAudioNode*>(this)->context()->document();
268 }
269 
270 } // namespace WebCore
271 
272 #endif // ENABLE(WEB_AUDIO)
273