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