diff options
Diffstat (limited to 'Source/WebCore/webaudio/ConvolverNode.cpp')
-rw-r--r-- | Source/WebCore/webaudio/ConvolverNode.cpp | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/Source/WebCore/webaudio/ConvolverNode.cpp b/Source/WebCore/webaudio/ConvolverNode.cpp new file mode 100644 index 0000000..c778a41 --- /dev/null +++ b/Source/WebCore/webaudio/ConvolverNode.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "ConvolverNode.h" + +#include "AudioBuffer.h" +#include "AudioNodeInput.h" +#include "AudioNodeOutput.h" +#include "Reverb.h" + +// Note about empirical tuning: +// The maximum FFT size affects reverb performance and accuracy. +// If the reverb is single-threaded and processes entirely in the real-time audio thread, +// it's important not to make this too high. In this case 8192 is a good value. +// But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy. +// Very large FFTs will have worse phase errors. Given these constraints 16384 is a good compromise. +const size_t MaxFFTSize = 16384; + +namespace WebCore { + +ConvolverNode::ConvolverNode(AudioContext* context, double sampleRate) + : AudioNode(context, sampleRate) +{ + addInput(adoptPtr(new AudioNodeInput(this))); + addOutput(adoptPtr(new AudioNodeOutput(this, 2))); + + setType(NodeTypeConvolver); + + initialize(); +} + +ConvolverNode::~ConvolverNode() +{ + uninitialize(); +} + +void ConvolverNode::process(size_t framesToProcess) +{ + AudioBus* outputBus = output(0)->bus(); + ASSERT(outputBus); + + // Synchronize with possible dynamic changes to the impulse response. + if (m_processLock.tryLock()) { + if (!isInitialized() || !m_reverb.get()) + outputBus->zero(); + else { + // Process using the convolution engine. + // 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. + // 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 + // we keep getting fed silence. + m_reverb->process(input(0)->bus(), outputBus, framesToProcess); + } + + m_processLock.unlock(); + } else { + // Too bad - the tryLock() failed. We must be in the middle of setting a new impulse response. + outputBus->zero(); + } +} + +void ConvolverNode::reset() +{ + MutexLocker locker(m_processLock); + if (m_reverb.get()) + m_reverb->reset(); +} + +void ConvolverNode::initialize() +{ + if (isInitialized()) + return; + + AudioNode::initialize(); +} + +void ConvolverNode::uninitialize() +{ + if (!isInitialized()) + return; + + m_reverb.clear(); + AudioNode::uninitialize(); +} + +void ConvolverNode::setBuffer(AudioBuffer* buffer) +{ + ASSERT(isMainThread()); + + ASSERT(buffer); + if (!buffer) + return; + + unsigned numberOfChannels = buffer->numberOfChannels(); + size_t bufferLength = buffer->length(); + + // The current implementation supports up to four channel impulse responses, which are interpreted as true-stereo (see Reverb class). + bool isBufferGood = numberOfChannels > 0 && numberOfChannels <= 4 && bufferLength; + ASSERT(isBufferGood); + if (!isBufferGood) + return; + + // Wrap the AudioBuffer by an AudioBus. It's an efficient pointer set and not a memcpy(). + // This memory is simply used in the Reverb constructor and no reference to it is kept for later use in that class. + AudioBus bufferBus(numberOfChannels, bufferLength, false); + for (unsigned i = 0; i < numberOfChannels; ++i) + bufferBus.setChannelMemory(i, buffer->getChannelData(i)->data(), bufferLength); + + // Create the reverb with the given impulse response. + OwnPtr<Reverb> reverb = adoptPtr(new Reverb(&bufferBus, AudioNode::ProcessingSizeInFrames, MaxFFTSize, 2, true)); + + { + // Synchronize with process(). + MutexLocker locker(m_processLock); + m_reverb = reverb.release(); + m_buffer = buffer; + } +} + +AudioBuffer* ConvolverNode::buffer() +{ + ASSERT(isMainThread()); + return m_buffer.get(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) |