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 "ConvolverNode.h" 30 31#include "AudioBuffer.h" 32#include "AudioContext.h" 33#include "AudioNodeInput.h" 34#include "AudioNodeOutput.h" 35#include "Reverb.h" 36 37// Note about empirical tuning: 38// The maximum FFT size affects reverb performance and accuracy. 39// If the reverb is single-threaded and processes entirely in the real-time audio thread, 40// it's important not to make this too high. In this case 8192 is a good value. 41// But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy. 42// Very large FFTs will have worse phase errors. Given these constraints 16384 is a good compromise. 43const size_t MaxFFTSize = 16384; 44 45namespace WebCore { 46 47ConvolverNode::ConvolverNode(AudioContext* context, double sampleRate) 48 : AudioNode(context, sampleRate) 49{ 50 addInput(adoptPtr(new AudioNodeInput(this))); 51 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); 52 53 setType(NodeTypeConvolver); 54 55 initialize(); 56} 57 58ConvolverNode::~ConvolverNode() 59{ 60 uninitialize(); 61} 62 63void ConvolverNode::process(size_t framesToProcess) 64{ 65 AudioBus* outputBus = output(0)->bus(); 66 ASSERT(outputBus); 67 68 // Synchronize with possible dynamic changes to the impulse response. 69 if (m_processLock.tryLock()) { 70 if (!isInitialized() || !m_reverb.get()) 71 outputBus->zero(); 72 else { 73 // Process using the convolution engine. 74 // 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. 75 // 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 76 // we keep getting fed silence. 77 m_reverb->process(input(0)->bus(), outputBus, framesToProcess); 78 } 79 80 m_processLock.unlock(); 81 } else { 82 // Too bad - the tryLock() failed. We must be in the middle of setting a new impulse response. 83 outputBus->zero(); 84 } 85} 86 87void ConvolverNode::reset() 88{ 89 MutexLocker locker(m_processLock); 90 if (m_reverb.get()) 91 m_reverb->reset(); 92} 93 94void ConvolverNode::initialize() 95{ 96 if (isInitialized()) 97 return; 98 99 AudioNode::initialize(); 100} 101 102void ConvolverNode::uninitialize() 103{ 104 if (!isInitialized()) 105 return; 106 107 m_reverb.clear(); 108 AudioNode::uninitialize(); 109} 110 111void ConvolverNode::setBuffer(AudioBuffer* buffer) 112{ 113 ASSERT(isMainThread()); 114 115 ASSERT(buffer); 116 if (!buffer) 117 return; 118 119 unsigned numberOfChannels = buffer->numberOfChannels(); 120 size_t bufferLength = buffer->length(); 121 122 // The current implementation supports up to four channel impulse responses, which are interpreted as true-stereo (see Reverb class). 123 bool isBufferGood = numberOfChannels > 0 && numberOfChannels <= 4 && bufferLength; 124 ASSERT(isBufferGood); 125 if (!isBufferGood) 126 return; 127 128 // Wrap the AudioBuffer by an AudioBus. It's an efficient pointer set and not a memcpy(). 129 // This memory is simply used in the Reverb constructor and no reference to it is kept for later use in that class. 130 AudioBus bufferBus(numberOfChannels, bufferLength, false); 131 for (unsigned i = 0; i < numberOfChannels; ++i) 132 bufferBus.setChannelMemory(i, buffer->getChannelData(i)->data(), bufferLength); 133 134 // Create the reverb with the given impulse response. 135 bool useBackgroundThreads = !context()->isOfflineContext(); 136 OwnPtr<Reverb> reverb = adoptPtr(new Reverb(&bufferBus, AudioNode::ProcessingSizeInFrames, MaxFFTSize, 2, useBackgroundThreads)); 137 138 { 139 // Synchronize with process(). 140 MutexLocker locker(m_processLock); 141 m_reverb = reverb.release(); 142 m_buffer = buffer; 143 } 144} 145 146AudioBuffer* ConvolverNode::buffer() 147{ 148 ASSERT(isMainThread()); 149 return m_buffer.get(); 150} 151 152} // namespace WebCore 153 154#endif // ENABLE(WEB_AUDIO) 155