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