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/ConvolverNode.h"
     30 
     31 #include "bindings/v8/ExceptionState.h"
     32 #include "core/dom/ExceptionCode.h"
     33 #include "platform/audio/Reverb.h"
     34 #include "modules/webaudio/AudioBuffer.h"
     35 #include "modules/webaudio/AudioContext.h"
     36 #include "modules/webaudio/AudioNodeInput.h"
     37 #include "modules/webaudio/AudioNodeOutput.h"
     38 #include "wtf/MainThread.h"
     39 
     40 // Note about empirical tuning:
     41 // The maximum FFT size affects reverb performance and accuracy.
     42 // If the reverb is single-threaded and processes entirely in the real-time audio thread,
     43 // it's important not to make this too high.  In this case 8192 is a good value.
     44 // But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy.
     45 // Very large FFTs will have worse phase errors. Given these constraints 32768 is a good compromise.
     46 const size_t MaxFFTSize = 32768;
     47 
     48 namespace WebCore {
     49 
     50 ConvolverNode::ConvolverNode(AudioContext* context, float sampleRate)
     51     : AudioNode(context, sampleRate)
     52     , m_normalize(true)
     53 {
     54     ScriptWrappable::init(this);
     55     addInput(adoptPtr(new AudioNodeInput(this)));
     56     addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
     57 
     58     // Node-specific default mixing rules.
     59     m_channelCount = 2;
     60     m_channelCountMode = ClampedMax;
     61     m_channelInterpretation = AudioBus::Speakers;
     62 
     63     setNodeType(NodeTypeConvolver);
     64     initialize();
     65 }
     66 
     67 ConvolverNode::~ConvolverNode()
     68 {
     69     uninitialize();
     70 }
     71 
     72 void ConvolverNode::process(size_t framesToProcess)
     73 {
     74     AudioBus* outputBus = output(0)->bus();
     75     ASSERT(outputBus);
     76 
     77     // Synchronize with possible dynamic changes to the impulse response.
     78     MutexTryLocker tryLocker(m_processLock);
     79     if (tryLocker.locked()) {
     80         if (!isInitialized() || !m_reverb.get())
     81             outputBus->zero();
     82         else {
     83             // Process using the convolution engine.
     84             // Note that we can handle the case where nothing is connected to the input, in which case we'll just feed silence into the convolver.
     85             // FIXME:  If we wanted to get fancy we could try to factor in the 'tail time' and stop processing once the tail dies down if
     86             // we keep getting fed silence.
     87             m_reverb->process(input(0)->bus(), outputBus, framesToProcess);
     88         }
     89     } else {
     90         // Too bad - the tryLock() failed.  We must be in the middle of setting a new impulse response.
     91         outputBus->zero();
     92     }
     93 }
     94 
     95 void ConvolverNode::initialize()
     96 {
     97     if (isInitialized())
     98         return;
     99 
    100     AudioNode::initialize();
    101 }
    102 
    103 void ConvolverNode::uninitialize()
    104 {
    105     if (!isInitialized())
    106         return;
    107 
    108     m_reverb.clear();
    109     AudioNode::uninitialize();
    110 }
    111 
    112 void ConvolverNode::setBuffer(AudioBuffer* buffer, ExceptionState& exceptionState)
    113 {
    114     ASSERT(isMainThread());
    115 
    116     if (!buffer)
    117         return;
    118 
    119     if (buffer->sampleRate() != context()->sampleRate()) {
    120         exceptionState.throwDOMException(
    121             NotSupportedError,
    122             "The buffer sample rate of " + String::number(buffer->sampleRate())
    123             + " does not match the context rate of " + String::number(context()->sampleRate())
    124             + " Hz.");
    125     }
    126 
    127     unsigned numberOfChannels = buffer->numberOfChannels();
    128     size_t bufferLength = buffer->length();
    129 
    130     // The current implementation supports up to four channel impulse responses, which are interpreted as true-stereo (see Reverb class).
    131     bool isBufferGood = numberOfChannels > 0 && numberOfChannels <= 4 && bufferLength;
    132     ASSERT(isBufferGood);
    133     if (!isBufferGood)
    134         return;
    135 
    136     // Wrap the AudioBuffer by an AudioBus. It's an efficient pointer set and not a memcpy().
    137     // This memory is simply used in the Reverb constructor and no reference to it is kept for later use in that class.
    138     RefPtr<AudioBus> bufferBus = AudioBus::create(numberOfChannels, bufferLength, false);
    139     for (unsigned i = 0; i < numberOfChannels; ++i)
    140         bufferBus->setChannelMemory(i, buffer->getChannelData(i)->data(), bufferLength);
    141 
    142     bufferBus->setSampleRate(buffer->sampleRate());
    143 
    144     // Create the reverb with the given impulse response.
    145     bool useBackgroundThreads = !context()->isOfflineContext();
    146     OwnPtr<Reverb> reverb = adoptPtr(new Reverb(bufferBus.get(), AudioNode::ProcessingSizeInFrames, MaxFFTSize, 2, useBackgroundThreads, m_normalize));
    147 
    148     {
    149         // Synchronize with process().
    150         MutexLocker locker(m_processLock);
    151         m_reverb = reverb.release();
    152         m_buffer = buffer;
    153     }
    154 }
    155 
    156 AudioBuffer* ConvolverNode::buffer()
    157 {
    158     ASSERT(isMainThread());
    159     return m_buffer.get();
    160 }
    161 
    162 double ConvolverNode::tailTime() const
    163 {
    164     MutexTryLocker tryLocker(m_processLock);
    165     if (tryLocker.locked())
    166         return m_reverb ? m_reverb->impulseResponseLength() / static_cast<double>(sampleRate()) : 0;
    167     // Since we don't want to block the Audio Device thread, we return a large value
    168     // instead of trying to acquire the lock.
    169     return std::numeric_limits<double>::infinity();
    170 }
    171 
    172 double ConvolverNode::latencyTime() const
    173 {
    174     MutexTryLocker tryLocker(m_processLock);
    175     if (tryLocker.locked())
    176         return m_reverb ? m_reverb->latencyFrames() / static_cast<double>(sampleRate()) : 0;
    177     // Since we don't want to block the Audio Device thread, we return a large value
    178     // instead of trying to acquire the lock.
    179     return std::numeric_limits<double>::infinity();
    180 }
    181 
    182 void ConvolverNode::trace(Visitor* visitor)
    183 {
    184     visitor->trace(m_buffer);
    185     AudioNode::trace(visitor);
    186 }
    187 
    188 } // namespace WebCore
    189 
    190 #endif // ENABLE(WEB_AUDIO)
    191