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.
43 const size_t MaxFFTSize = 16384;
44
45 namespace WebCore {
46
ConvolverNode(AudioContext * context,double sampleRate)47 ConvolverNode::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
~ConvolverNode()58 ConvolverNode::~ConvolverNode()
59 {
60 uninitialize();
61 }
62
process(size_t framesToProcess)63 void 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
reset()87 void ConvolverNode::reset()
88 {
89 MutexLocker locker(m_processLock);
90 if (m_reverb.get())
91 m_reverb->reset();
92 }
93
initialize()94 void ConvolverNode::initialize()
95 {
96 if (isInitialized())
97 return;
98
99 AudioNode::initialize();
100 }
101
uninitialize()102 void ConvolverNode::uninitialize()
103 {
104 if (!isInitialized())
105 return;
106
107 m_reverb.clear();
108 AudioNode::uninitialize();
109 }
110
setBuffer(AudioBuffer * buffer)111 void 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
buffer()146 AudioBuffer* ConvolverNode::buffer()
147 {
148 ASSERT(isMainThread());
149 return m_buffer.get();
150 }
151
152 } // namespace WebCore
153
154 #endif // ENABLE(WEB_AUDIO)
155