summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/webaudio
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/webaudio
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/webaudio')
-rw-r--r--Source/WebCore/webaudio/AudioBasicProcessorNode.cpp149
-rw-r--r--Source/WebCore/webaudio/AudioBasicProcessorNode.h68
-rw-r--r--Source/WebCore/webaudio/AudioBuffer.cpp110
-rw-r--r--Source/WebCore/webaudio/AudioBuffer.h81
-rw-r--r--Source/WebCore/webaudio/AudioBuffer.idl43
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.cpp455
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.h147
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.idl41
-rw-r--r--Source/WebCore/webaudio/AudioChannelMerger.cpp102
-rw-r--r--Source/WebCore/webaudio/AudioChannelMerger.h56
-rw-r--r--Source/WebCore/webaudio/AudioChannelMerger.idl34
-rw-r--r--Source/WebCore/webaudio/AudioChannelSplitter.cpp84
-rw-r--r--Source/WebCore/webaudio/AudioChannelSplitter.h52
-rw-r--r--Source/WebCore/webaudio/AudioChannelSplitter.idl30
-rw-r--r--Source/WebCore/webaudio/AudioContext.cpp529
-rw-r--r--Source/WebCore/webaudio/AudioContext.h259
-rw-r--r--Source/WebCore/webaudio/AudioContext.idl64
-rw-r--r--Source/WebCore/webaudio/AudioDestinationNode.cpp114
-rw-r--r--Source/WebCore/webaudio/AudioDestinationNode.h72
-rw-r--r--Source/WebCore/webaudio/AudioDestinationNode.idl32
-rw-r--r--Source/WebCore/webaudio/AudioGain.h53
-rw-r--r--Source/WebCore/webaudio/AudioGain.idl35
-rw-r--r--Source/WebCore/webaudio/AudioGainNode.cpp113
-rw-r--r--Source/WebCore/webaudio/AudioGainNode.h70
-rw-r--r--Source/WebCore/webaudio/AudioGainNode.idl33
-rw-r--r--Source/WebCore/webaudio/AudioListener.cpp51
-rw-r--r--Source/WebCore/webaudio/AudioListener.h94
-rw-r--r--Source/WebCore/webaudio/AudioListener.idl40
-rw-r--r--Source/WebCore/webaudio/AudioNode.cpp317
-rw-r--r--Source/WebCore/webaudio/AudioNode.h171
-rw-r--r--Source/WebCore/webaudio/AudioNode.idl39
-rw-r--r--Source/WebCore/webaudio/AudioNodeInput.cpp270
-rw-r--r--Source/WebCore/webaudio/AudioNodeInput.h125
-rw-r--r--Source/WebCore/webaudio/AudioNodeOutput.cpp216
-rw-r--r--Source/WebCore/webaudio/AudioNodeOutput.h134
-rw-r--r--Source/WebCore/webaudio/AudioPannerNode.cpp317
-rw-r--r--Source/WebCore/webaudio/AudioPannerNode.h148
-rw-r--r--Source/WebCore/webaudio/AudioPannerNode.idl59
-rw-r--r--Source/WebCore/webaudio/AudioParam.cpp66
-rw-r--r--Source/WebCore/webaudio/AudioParam.h100
-rw-r--r--Source/WebCore/webaudio/AudioParam.idl43
-rw-r--r--Source/WebCore/webaudio/AudioProcessingEvent.cpp59
-rw-r--r--Source/WebCore/webaudio/AudioProcessingEvent.h57
-rw-r--r--Source/WebCore/webaudio/AudioProcessingEvent.idl33
-rw-r--r--Source/WebCore/webaudio/AudioSourceNode.h46
-rw-r--r--Source/WebCore/webaudio/AudioSourceNode.idl34
-rw-r--r--Source/WebCore/webaudio/BiquadDSPKernel.cpp77
-rw-r--r--Source/WebCore/webaudio/BiquadDSPKernel.h56
-rw-r--r--Source/WebCore/webaudio/BiquadProcessor.cpp125
-rw-r--r--Source/WebCore/webaudio/BiquadProcessor.h78
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.cpp152
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.h69
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.idl33
-rw-r--r--Source/WebCore/webaudio/DelayDSPKernel.cpp140
-rw-r--r--Source/WebCore/webaudio/DelayDSPKernel.h62
-rw-r--r--Source/WebCore/webaudio/DelayNode.cpp47
-rw-r--r--Source/WebCore/webaudio/DelayNode.h53
-rw-r--r--Source/WebCore/webaudio/DelayNode.idl32
-rw-r--r--Source/WebCore/webaudio/DelayProcessor.cpp54
-rw-r--r--Source/WebCore/webaudio/DelayProcessor.h53
-rw-r--r--Source/WebCore/webaudio/HighPass2FilterNode.cpp42
-rw-r--r--Source/WebCore/webaudio/HighPass2FilterNode.h53
-rw-r--r--Source/WebCore/webaudio/HighPass2FilterNode.idl35
-rw-r--r--Source/WebCore/webaudio/JavaScriptAudioNode.cpp272
-rw-r--r--Source/WebCore/webaudio/JavaScriptAudioNode.h104
-rw-r--r--Source/WebCore/webaudio/JavaScriptAudioNode.idl40
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.cpp42
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.h53
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.idl35
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyser.cpp301
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyser.h103
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.cpp88
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.h76
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.idl48
74 files changed, 7568 insertions, 0 deletions
diff --git a/Source/WebCore/webaudio/AudioBasicProcessorNode.cpp b/Source/WebCore/webaudio/AudioBasicProcessorNode.cpp
new file mode 100644
index 0000000..828062e
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBasicProcessorNode.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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 "AudioBasicProcessorNode.h"
+
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "AudioProcessor.h"
+
+namespace WebCore {
+
+AudioBasicProcessorNode::AudioBasicProcessorNode(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 0)));
+
+ // The subclass must create m_processor.
+}
+
+void AudioBasicProcessorNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ ASSERT(processor());
+ processor()->initialize();
+
+ AudioNode::initialize();
+}
+
+void AudioBasicProcessorNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ ASSERT(processor());
+ processor()->uninitialize();
+
+ AudioNode::uninitialize();
+}
+
+void AudioBasicProcessorNode::process(size_t framesToProcess)
+{
+ AudioBus* destinationBus = output(0)->bus();
+
+ // The realtime thread can't block on this lock, so we call tryLock() instead.
+ if (m_processLock.tryLock()) {
+ if (!isInitialized() || !processor())
+ destinationBus->zero();
+ else {
+ AudioBus* sourceBus = input(0)->bus();
+
+ // FIXME: if we take "tail time" into account, then we can avoid calling processor()->process() once the tail dies down.
+ if (!input(0)->isConnected())
+ sourceBus->zero();
+
+ processor()->process(sourceBus, destinationBus, framesToProcess);
+ }
+
+ m_processLock.unlock();
+ } else {
+ // Too bad - the tryLock() failed. We must be in the middle of re-connecting and were already outputting silence anyway...
+ destinationBus->zero();
+ }
+}
+
+// Nice optimization in the very common case allowing for "in-place" processing
+void AudioBasicProcessorNode::pullInputs(size_t framesToProcess)
+{
+ // Render input stream - suggest to the input to render directly into output bus for in-place processing in process() if possible.
+ input(0)->pull(output(0)->bus(), framesToProcess);
+}
+
+void AudioBasicProcessorNode::reset()
+{
+ if (processor())
+ processor()->reset();
+}
+
+// As soon as we know the channel count of our input, we can lazily initialize.
+// Sometimes this may be called more than once with different channel counts, in which case we must safely
+// uninitialize and then re-initialize with the new channel count.
+void AudioBasicProcessorNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
+{
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+
+ ASSERT(input == this->input(0));
+ if (input != this->input(0))
+ return;
+
+ ASSERT(processor());
+ if (!processor())
+ return;
+
+ unsigned numberOfChannels = input->numberOfChannels();
+
+ if (isInitialized() && numberOfChannels != output(0)->numberOfChannels()) {
+ // We're already initialized but the channel count has changed.
+ // We need to be careful since we may be actively processing right now, so synchronize with process().
+ MutexLocker locker(m_processLock);
+ uninitialize();
+ }
+
+ if (!isInitialized()) {
+ // This will propagate the channel count to any nodes connected further down the chain...
+ output(0)->setNumberOfChannels(numberOfChannels);
+
+ // Re-initialize the processor with the new channel count.
+ processor()->setNumberOfChannels(numberOfChannels);
+ initialize();
+ }
+}
+
+unsigned AudioBasicProcessorNode::numberOfChannels()
+{
+ return output(0)->numberOfChannels();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioBasicProcessorNode.h b/Source/WebCore/webaudio/AudioBasicProcessorNode.h
new file mode 100644
index 0000000..38bfd3b
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBasicProcessorNode.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioBasicProcessorNode_h
+#define AudioBasicProcessorNode_h
+
+#include "AudioNode.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioBus;
+class AudioNodeInput;
+class AudioProcessor;
+
+// AudioBasicProcessorNode is an AudioNode with one input and one output where the input and output have the same number of channels.
+class AudioBasicProcessorNode : public AudioNode {
+public:
+ AudioBasicProcessorNode(AudioContext*, double sampleRate);
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void pullInputs(size_t framesToProcess);
+ virtual void reset();
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // Called in the main thread when the number of channels for the input may have changed.
+ virtual void checkNumberOfChannelsForInput(AudioNodeInput*);
+
+ // Returns the number of channels for both the input and the output.
+ unsigned numberOfChannels();
+
+protected:
+ AudioProcessor* processor() { return m_processor.get(); }
+ OwnPtr<AudioProcessor> m_processor;
+
+private:
+ // This synchronizes live channel count changes which require an uninitialization / re-initialization.
+ mutable Mutex m_processLock;
+};
+
+} // namespace WebCore
+
+#endif // AudioBasicProcessorNode_h
diff --git a/Source/WebCore/webaudio/AudioBuffer.cpp b/Source/WebCore/webaudio/AudioBuffer.cpp
new file mode 100644
index 0000000..f46d153
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBuffer.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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) & ENABLE(3D_CANVAS)
+
+#include "AudioBuffer.h"
+
+#include "AudioBus.h"
+#include "AudioFileReader.h"
+#include "ExceptionCode.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+PassRefPtr<AudioBuffer> AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
+{
+ return adoptRef(new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate));
+}
+
+PassRefPtr<AudioBuffer> AudioBuffer::createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, double sampleRate)
+{
+ OwnPtr<AudioBus> bus = createBusFromInMemoryAudioFile(data, dataSize, mixToMono, sampleRate);
+ if (bus.get())
+ return adoptRef(new AudioBuffer(bus.get()));
+
+ return 0;
+}
+
+AudioBuffer::AudioBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
+ : m_gain(1.0)
+ , m_sampleRate(sampleRate)
+ , m_length(numberOfFrames)
+{
+ m_channels.reserveCapacity(numberOfChannels);
+
+ for (unsigned i = 0; i < numberOfChannels; ++i) {
+ RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
+ m_channels.append(channelDataArray);
+ }
+}
+
+AudioBuffer::AudioBuffer(AudioBus* bus)
+ : m_gain(1.0)
+ , m_sampleRate(bus->sampleRate())
+ , m_length(bus->length())
+{
+ // Copy audio data from the bus to the Float32Arrays we manage.
+ unsigned numberOfChannels = bus->numberOfChannels();
+ m_channels.reserveCapacity(numberOfChannels);
+ for (unsigned i = 0; i < numberOfChannels; ++i) {
+ RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
+ ExceptionCode ec;
+ channelDataArray->setRange(bus->channel(i)->data(), m_length, 0, ec);
+ m_channels.append(channelDataArray);
+ }
+}
+
+void AudioBuffer::releaseMemory()
+{
+ m_channels.clear();
+}
+
+Float32Array* AudioBuffer::getChannelData(unsigned channelIndex)
+{
+ if (channelIndex >= m_channels.size())
+ return 0;
+
+ return m_channels[channelIndex].get();
+}
+
+void AudioBuffer::zero()
+{
+ for (unsigned i = 0; i < m_channels.size(); ++i) {
+ if (getChannelData(i)) {
+ ExceptionCode ec;
+ getChannelData(i)->zeroRange(0, length(), ec);
+ }
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO) & ENABLE(3D_CANVAS)
diff --git a/Source/WebCore/webaudio/AudioBuffer.h b/Source/WebCore/webaudio/AudioBuffer.h
new file mode 100644
index 0000000..b11a20e
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBuffer.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioBuffer_h
+#define AudioBuffer_h
+
+#include "Float32Array.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioBus;
+
+class AudioBuffer : public RefCounted<AudioBuffer> {
+public:
+ static PassRefPtr<AudioBuffer> create(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate);
+
+ // Returns 0 if data is not a valid audio file.
+ static PassRefPtr<AudioBuffer> createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, double sampleRate);
+
+ // Format
+ size_t length() const { return m_length; }
+ double duration() const { return length() / sampleRate(); }
+ double sampleRate() const { return m_sampleRate; }
+
+ // Channel data access
+ unsigned numberOfChannels() const { return m_channels.size(); }
+ Float32Array* getChannelData(unsigned channelIndex);
+ void zero();
+
+ // Scalar gain
+ double gain() const { return m_gain; }
+ void setGain(double gain) { m_gain = gain; }
+
+ // Because an AudioBuffer has a JavaScript wrapper, which will be garbage collected, it may take awhile for this object to be deleted.
+ // releaseMemory() can be called when the AudioContext goes away, so we can release the memory earlier than when the garbage collection happens.
+ // Careful! Only call this when the page unloads, after the AudioContext is no longer processing.
+ void releaseMemory();
+
+protected:
+ AudioBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate);
+ AudioBuffer(AudioBus* bus);
+
+ double m_gain; // scalar gain
+ double m_sampleRate;
+ size_t m_length;
+
+ Vector<RefPtr<Float32Array> > m_channels;
+};
+
+} // namespace WebCore
+
+#endif // AudioBuffer_h
diff --git a/Source/WebCore/webaudio/AudioBuffer.idl b/Source/WebCore/webaudio/AudioBuffer.idl
new file mode 100644
index 0000000..e7353bf
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBuffer.idl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO & 3D_CANVAS
+ ] AudioBuffer {
+ readonly attribute long length; // in sample-frames
+ readonly attribute float duration; // in seconds
+ readonly attribute float sampleRate; // in sample-frames per second
+
+ attribute float gain; // linear gain (default 1.0)
+
+ // Channel access
+ readonly attribute unsigned long numberOfChannels;
+ Float32Array getChannelData(in unsigned long channelIndex);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioBufferSourceNode.cpp b/Source/WebCore/webaudio/AudioBufferSourceNode.cpp
new file mode 100644
index 0000000..05abed8
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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 "AudioBufferSourceNode.h"
+
+#include "AudioContext.h"
+#include "AudioNodeOutput.h"
+#include <algorithm>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+const double DefaultGrainDuration = 0.020; // 20ms
+
+PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, double sampleRate)
+{
+ return adoptRef(new AudioBufferSourceNode(context, sampleRate));
+}
+
+AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, double sampleRate)
+ : AudioSourceNode(context, sampleRate)
+ , m_buffer(0)
+ , m_isPlaying(false)
+ , m_isLooping(false)
+ , m_hasFinished(false)
+ , m_startTime(0.0)
+ , m_schedulingFrameDelay(0)
+ , m_readIndex(0)
+ , m_isGrain(false)
+ , m_grainOffset(0.0)
+ , m_grainDuration(DefaultGrainDuration)
+ , m_grainFrameCount(0)
+ , m_lastGain(1.0)
+ , m_pannerNode(0)
+{
+ setType(NodeTypeAudioBufferSource);
+
+ m_gain = AudioGain::create("gain", 1.0, 0.0, 1.0);
+ m_playbackRate = AudioParam::create("playbackRate", 1.0, 0.0, AudioResampler::MaxRate);
+
+ // Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer.
+ addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+ initialize();
+}
+
+AudioBufferSourceNode::~AudioBufferSourceNode()
+{
+ uninitialize();
+}
+
+void AudioBufferSourceNode::process(size_t framesToProcess)
+{
+ AudioBus* outputBus = output(0)->bus();
+
+ if (!isInitialized()) {
+ outputBus->zero();
+ return;
+ }
+
+ // The audio thread can't block on this lock, so we call tryLock() instead.
+ // Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return.
+ if (m_processLock.tryLock()) {
+ // Check if it's time to start playing.
+ double sampleRate = this->sampleRate();
+ double pitchRate = totalPitchRate();
+ double quantumStartTime = context()->currentTime();
+ double quantumEndTime = quantumStartTime + framesToProcess / sampleRate;
+
+ if (!m_isPlaying || m_hasFinished || !buffer() || m_startTime >= quantumEndTime) {
+ // FIXME: can optimize here by propagating silent hint instead of forcing the whole chain to process silence.
+ outputBus->zero();
+ m_processLock.unlock();
+ return;
+ }
+
+ // Handle sample-accurate scheduling so that buffer playback will happen at a very precise time.
+ m_schedulingFrameDelay = 0;
+ if (m_startTime >= quantumStartTime) {
+ // m_schedulingFrameDelay is set here only the very first render quantum (because of above check: m_startTime >= quantumEndTime)
+ // So: quantumStartTime <= m_startTime < quantumEndTime
+ ASSERT(m_startTime < quantumEndTime);
+
+ double startTimeInQuantum = m_startTime - quantumStartTime;
+ double startFrameInQuantum = startTimeInQuantum * sampleRate;
+
+ // m_schedulingFrameDelay is used in provideInput(), so factor in the current playback pitch rate.
+ m_schedulingFrameDelay = static_cast<int>(pitchRate * startFrameInQuantum);
+ }
+
+ // FIXME: optimization opportunity:
+ // With a bit of work, it should be possible to avoid going through the resampler completely when the pitchRate == 1,
+ // especially if the pitchRate has never deviated from 1 in the past.
+
+ // Read the samples through the pitch resampler. Our provideInput() method will be called by the resampler.
+ m_resampler.setRate(pitchRate);
+ m_resampler.process(this, outputBus, framesToProcess);
+
+ // Apply the gain (in-place) to the output bus.
+ double totalGain = gain()->value() * m_buffer->gain();
+ outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
+
+ m_processLock.unlock();
+ } else {
+ // Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
+ outputBus->zero();
+ }
+}
+
+// The resampler calls us back here to get the input samples from our buffer.
+void AudioBufferSourceNode::provideInput(AudioBus* bus, size_t numberOfFrames)
+{
+ ASSERT(context()->isAudioThread());
+
+ // Basic sanity checking
+ ASSERT(bus);
+ ASSERT(buffer());
+ if (!bus || !buffer())
+ return;
+
+ unsigned numberOfChannels = this->numberOfChannels();
+ unsigned busNumberOfChannels = bus->numberOfChannels();
+
+ // FIXME: we can add support for sources with more than two channels, but this is not a common case.
+ bool channelCountGood = numberOfChannels == busNumberOfChannels && (numberOfChannels == 1 || numberOfChannels == 2);
+ ASSERT(channelCountGood);
+ if (!channelCountGood)
+ return;
+
+ // Get the destination pointers.
+ float* destinationL = bus->channel(0)->data();
+ ASSERT(destinationL);
+ if (!destinationL)
+ return;
+ float* destinationR = (numberOfChannels < 2) ? 0 : bus->channel(1)->data();
+
+ size_t bufferLength = buffer()->length();
+ double bufferSampleRate = buffer()->sampleRate();
+
+ // Calculate the start and end frames in our buffer that we want to play.
+ // If m_isGrain is true, then we will be playing a portion of the total buffer.
+ unsigned startFrame = m_isGrain ? static_cast<unsigned>(m_grainOffset * bufferSampleRate) : 0;
+ unsigned endFrame = m_isGrain ? static_cast<unsigned>(startFrame + m_grainDuration * bufferSampleRate) : bufferLength;
+
+ // This is a HACK to allow for HRTF tail-time - avoids glitch at end.
+ // FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
+ if (m_isGrain)
+ endFrame += 512;
+
+ // Do some sanity checking.
+ if (startFrame >= bufferLength)
+ startFrame = !bufferLength ? 0 : bufferLength - 1;
+ if (endFrame > bufferLength)
+ endFrame = bufferLength;
+ if (m_readIndex >= endFrame)
+ m_readIndex = startFrame; // reset to start
+
+ int framesToProcess = numberOfFrames;
+
+ // Handle sample-accurate scheduling so that we play the buffer at a very precise time.
+ // m_schedulingFrameDelay will only be non-zero the very first time that provideInput() is called, which corresponds
+ // with the very start of the buffer playback.
+ if (m_schedulingFrameDelay > 0) {
+ ASSERT(m_schedulingFrameDelay <= framesToProcess);
+ if (m_schedulingFrameDelay <= framesToProcess) {
+ // Generate silence for the initial portion of the destination.
+ memset(destinationL, 0, sizeof(float) * m_schedulingFrameDelay);
+ destinationL += m_schedulingFrameDelay;
+ if (destinationR) {
+ memset(destinationR, 0, sizeof(float) * m_schedulingFrameDelay);
+ destinationR += m_schedulingFrameDelay;
+ }
+
+ // Since we just generated silence for the initial portion, we have fewer frames to provide.
+ framesToProcess -= m_schedulingFrameDelay;
+ }
+ }
+
+ // We have to generate a certain number of output sample-frames, but we need to handle the case where we wrap around
+ // from the end of the buffer to the start if playing back with looping and also the case where we simply reach the
+ // end of the sample data, but haven't yet rendered numberOfFrames worth of output.
+ while (framesToProcess > 0) {
+ ASSERT(m_readIndex <= endFrame);
+ if (m_readIndex > endFrame)
+ return;
+
+ // Figure out how many frames we can process this time.
+ int framesAvailable = endFrame - m_readIndex;
+ int framesThisTime = min(framesToProcess, framesAvailable);
+
+ // Create the destination bus for the part of the destination we're processing this time.
+ AudioBus currentDestinationBus(busNumberOfChannels, framesThisTime, false);
+ currentDestinationBus.setChannelMemory(0, destinationL, framesThisTime);
+ if (busNumberOfChannels > 1)
+ currentDestinationBus.setChannelMemory(1, destinationR, framesThisTime);
+
+ // Generate output from the buffer.
+ readFromBuffer(&currentDestinationBus, framesThisTime);
+
+ // Update the destination pointers.
+ destinationL += framesThisTime;
+ if (busNumberOfChannels > 1)
+ destinationR += framesThisTime;
+
+ framesToProcess -= framesThisTime;
+
+ // Handle the case where we reach the end of the part of the sample data we're supposed to play for the buffer.
+ if (m_readIndex >= endFrame) {
+ m_readIndex = startFrame;
+ m_grainFrameCount = 0;
+
+ if (!looping()) {
+ // If we're not looping, then stop playing when we get to the end.
+ m_isPlaying = false;
+
+ if (framesToProcess > 0) {
+ // We're not looping and we've reached the end of the sample data, but we still need to provide more output,
+ // so generate silence for the remaining.
+ memset(destinationL, 0, sizeof(float) * framesToProcess);
+
+ if (destinationR)
+ memset(destinationR, 0, sizeof(float) * framesToProcess);
+ }
+
+ if (!m_hasFinished) {
+ // Let the context dereference this AudioNode.
+ context()->notifyNodeFinishedProcessing(this);
+ m_hasFinished = true;
+ }
+ return;
+ }
+ }
+ }
+}
+
+void AudioBufferSourceNode::readFromBuffer(AudioBus* destinationBus, size_t framesToProcess)
+{
+ bool isBusGood = destinationBus && destinationBus->length() == framesToProcess && destinationBus->numberOfChannels() == numberOfChannels();
+ ASSERT(isBusGood);
+ if (!isBusGood)
+ return;
+
+ unsigned numberOfChannels = this->numberOfChannels();
+ // FIXME: we can add support for sources with more than two channels, but this is not a common case.
+ bool channelCountGood = numberOfChannels == 1 || numberOfChannels == 2;
+ ASSERT(channelCountGood);
+ if (!channelCountGood)
+ return;
+
+ // Get pointers to the start of the sample buffer.
+ float* sourceL = m_buffer->getChannelData(0)->data();
+ float* sourceR = m_buffer->numberOfChannels() == 2 ? m_buffer->getChannelData(1)->data() : 0;
+
+ // Sanity check buffer access.
+ bool isSourceGood = sourceL && (numberOfChannels == 1 || sourceR) && m_readIndex + framesToProcess <= m_buffer->length();
+ ASSERT(isSourceGood);
+ if (!isSourceGood)
+ return;
+
+ // Offset the pointers to the current read position in the sample buffer.
+ sourceL += m_readIndex;
+ sourceR += m_readIndex;
+
+ // Get pointers to the destination.
+ float* destinationL = destinationBus->channel(0)->data();
+ float* destinationR = numberOfChannels == 2 ? destinationBus->channel(1)->data() : 0;
+ bool isDestinationGood = destinationL && (numberOfChannels == 1 || destinationR);
+ ASSERT(isDestinationGood);
+ if (!isDestinationGood)
+ return;
+
+ if (m_isGrain)
+ readFromBufferWithGrainEnvelope(sourceL, sourceR, destinationL, destinationR, framesToProcess);
+ else {
+ // Simply copy the data from the source buffer to the destination.
+ memcpy(destinationL, sourceL, sizeof(float) * framesToProcess);
+ if (numberOfChannels == 2)
+ memcpy(destinationR, sourceR, sizeof(float) * framesToProcess);
+ }
+
+ // Advance the buffer's read index.
+ m_readIndex += framesToProcess;
+}
+
+void AudioBufferSourceNode::readFromBufferWithGrainEnvelope(float* sourceL, float* sourceR, float* destinationL, float* destinationR, size_t framesToProcess)
+{
+ ASSERT(sourceL && destinationL);
+ if (!sourceL || !destinationL)
+ return;
+
+ int grainFrameLength = static_cast<int>(m_grainDuration * m_buffer->sampleRate());
+ bool isStereo = sourceR && destinationR;
+
+ int n = framesToProcess;
+ while (n--) {
+ // Apply the grain envelope.
+ float x = static_cast<float>(m_grainFrameCount) / static_cast<float>(grainFrameLength);
+ m_grainFrameCount++;
+
+ x = min(1.0f, x);
+ float grainEnvelope = sinf(piFloat * x);
+
+ *destinationL++ = grainEnvelope * *sourceL++;
+
+ if (isStereo)
+ *destinationR++ = grainEnvelope * *sourceR++;
+ }
+}
+
+void AudioBufferSourceNode::reset()
+{
+ m_resampler.reset();
+ m_readIndex = 0;
+ m_grainFrameCount = 0;
+ m_lastGain = gain()->value();
+}
+
+void AudioBufferSourceNode::setBuffer(AudioBuffer* buffer)
+{
+ ASSERT(isMainThread());
+
+ // The context must be locked since changing the buffer can re-configure the number of channels that are output.
+ AudioContext::AutoLocker contextLocker(context());
+
+ // This synchronizes with process().
+ MutexLocker processLocker(m_processLock);
+
+ if (buffer) {
+ // Do any necesssary re-configuration to the buffer's number of channels.
+ unsigned numberOfChannels = buffer->numberOfChannels();
+ m_resampler.configureChannels(numberOfChannels);
+ output(0)->setNumberOfChannels(numberOfChannels);
+ }
+
+ m_readIndex = 0;
+ m_buffer = buffer;
+}
+
+unsigned AudioBufferSourceNode::numberOfChannels()
+{
+ return output(0)->numberOfChannels();
+}
+
+void AudioBufferSourceNode::noteOn(double when)
+{
+ ASSERT(isMainThread());
+ if (m_isPlaying)
+ return;
+
+ m_isGrain = false;
+ m_startTime = when;
+ m_readIndex = 0;
+ m_isPlaying = true;
+}
+
+void AudioBufferSourceNode::noteGrainOn(double when, double grainOffset, double grainDuration)
+{
+ ASSERT(isMainThread());
+ if (m_isPlaying)
+ return;
+
+ if (!buffer())
+ return;
+
+ // Do sanity checking of grain parameters versus buffer size.
+ double bufferDuration = buffer()->duration();
+
+ if (grainDuration > bufferDuration)
+ return; // FIXME: maybe should throw exception - consider in specification.
+
+ double maxGrainOffset = bufferDuration - grainDuration;
+ maxGrainOffset = max(0.0, maxGrainOffset);
+
+ grainOffset = max(0.0, grainOffset);
+ grainOffset = min(maxGrainOffset, grainOffset);
+ m_grainOffset = grainOffset;
+
+ m_grainDuration = grainDuration;
+ m_grainFrameCount = 0;
+
+ m_isGrain = true;
+ m_startTime = when;
+ m_readIndex = static_cast<int>(m_grainOffset * buffer()->sampleRate());
+ m_isPlaying = true;
+}
+
+void AudioBufferSourceNode::noteOff(double)
+{
+ ASSERT(isMainThread());
+ if (!m_isPlaying)
+ return;
+
+ // FIXME: the "when" argument to this method is ignored.
+ m_isPlaying = false;
+ m_readIndex = 0;
+}
+
+double AudioBufferSourceNode::totalPitchRate()
+{
+ double dopplerRate = 1.0;
+ if (m_pannerNode.get())
+ dopplerRate = m_pannerNode->dopplerRate();
+
+ // Incorporate buffer's sample-rate versus AudioContext's sample-rate.
+ // Normally it's not an issue because buffers are loaded at the AudioContext's sample-rate, but we can handle it in any case.
+ double sampleRateFactor = 1.0;
+ if (buffer())
+ sampleRateFactor = buffer()->sampleRate() / sampleRate();
+
+ double basePitchRate = playbackRate()->value();
+
+ double totalRate = dopplerRate * sampleRateFactor * basePitchRate;
+
+ // Sanity check the total rate. It's very important that the resampler not get any bad rate values.
+ totalRate = max(0.0, totalRate);
+ totalRate = min(AudioResampler::MaxRate, totalRate);
+
+ bool isTotalRateValid = !isnan(totalRate) && !isinf(totalRate);
+ ASSERT(isTotalRateValid);
+ if (!isTotalRateValid)
+ totalRate = 1.0;
+
+ return totalRate;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioBufferSourceNode.h b/Source/WebCore/webaudio/AudioBufferSourceNode.h
new file mode 100644
index 0000000..40b8555
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioBufferSourceNode_h
+#define AudioBufferSourceNode_h
+
+#include "AudioBuffer.h"
+#include "AudioBus.h"
+#include "AudioGain.h"
+#include "AudioPannerNode.h"
+#include "AudioResampler.h"
+#include "AudioSourceNode.h"
+#include "AudioSourceProvider.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+// AudioBufferSourceNode is an AudioNode representing an audio source from an in-memory audio asset represented by an AudioBuffer.
+// It generally will be used for short sounds which require a high degree of scheduling flexibility (can playback in rhythmically perfect ways).
+
+class AudioBufferSourceNode : public AudioSourceNode, public AudioSourceProvider {
+public:
+ static PassRefPtr<AudioBufferSourceNode> create(AudioContext*, double sampleRate);
+
+ virtual ~AudioBufferSourceNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+ // AudioSourceProvider
+ // When process() is called, the resampler calls provideInput (in the audio thread) to gets its input stream.
+ virtual void provideInput(AudioBus*, size_t numberOfFrames);
+
+ // setBuffer() is called on the main thread. This is the buffer we use for playback.
+ void setBuffer(AudioBuffer*);
+ AudioBuffer* buffer() { return m_buffer.get(); }
+
+ // numberOfChannels() returns the number of output channels. This value equals the number of channels from the buffer.
+ // If a new buffer is set with a different number of channels, then this value will dynamically change.
+ unsigned numberOfChannels();
+
+ // Play-state
+ // noteOn(), noteGrainOn(), and noteOff() must all be called from the main thread.
+ void noteOn(double when);
+ void noteGrainOn(double when, double grainOffset, double grainDuration);
+ void noteOff(double when);
+
+ bool looping() const { return m_isLooping; }
+ void setLooping(bool looping) { m_isLooping = looping; }
+
+ AudioGain* gain() { return m_gain.get(); }
+ AudioParam* playbackRate() { return m_playbackRate.get(); }
+
+ // If a panner node is set, then we can incorporate doppler shift into the playback pitch rate.
+ void setPannerNode(PassRefPtr<AudioPannerNode> pannerNode) { m_pannerNode = pannerNode; }
+
+private:
+ AudioBufferSourceNode(AudioContext*, double sampleRate);
+
+ // m_buffer holds the sample data which this node outputs.
+ RefPtr<AudioBuffer> m_buffer;
+
+ // Used for the "gain" and "playbackRate" attributes.
+ RefPtr<AudioGain> m_gain;
+ RefPtr<AudioParam> m_playbackRate;
+
+ // m_isPlaying is set to true when noteOn() or noteGrainOn() is called.
+ bool m_isPlaying;
+
+ // If m_isLooping is false, then this node will be done playing and become inactive after it reaches the end of the sample data in the buffer.
+ // If true, it will wrap around to the start of the buffer each time it reaches the end.
+ bool m_isLooping;
+
+ // This node is considered finished when it reaches the end of the buffer's sample data after noteOn() has been called.
+ // This will only be set to true if m_isLooping == false.
+ bool m_hasFinished;
+
+ // m_startTime is the time to start playing based on the context's timeline (0.0 or a time less than the context's current time means "now").
+ double m_startTime; // in seconds
+
+ // m_schedulingFrameDelay is the sample-accurate scheduling offset.
+ // It's used so that we start rendering audio samples at a very precise point in time.
+ // It will only be a non-zero value the very first render quantum that we render from the buffer.
+ int m_schedulingFrameDelay;
+
+ // m_readIndex is a sample-frame index into our buffer representing the current playback position.
+ unsigned m_readIndex;
+
+ // Granular playback
+ bool m_isGrain;
+ double m_grainOffset; // in seconds
+ double m_grainDuration; // in seconds
+ int m_grainFrameCount; // keeps track of which frame in the grain we're currently rendering
+
+ // totalPitchRate() returns the instantaneous pitch rate (non-time preserving).
+ // It incorporates the base pitch rate, any sample-rate conversion factor from the buffer, and any doppler shift from an associated panner node.
+ double totalPitchRate();
+
+ // m_resampler performs the pitch rate changes to the buffer playback.
+ AudioResampler m_resampler;
+
+ // m_lastGain provides continuity when we dynamically adjust the gain.
+ double m_lastGain;
+
+ // We optionally keep track of a panner node which has a doppler shift that is incorporated into the pitch rate.
+ RefPtr<AudioPannerNode> m_pannerNode;
+
+ // This synchronizes process() with setBuffer() which can cause dynamic channel count changes.
+ mutable Mutex m_processLock;
+
+ // Reads the next framesToProcess sample-frames from the AudioBuffer into destinationBus.
+ // A grain envelope will be applied if m_isGrain is set to true.
+ void readFromBuffer(AudioBus* destinationBus, size_t framesToProcess);
+
+ // readFromBufferWithGrainEnvelope() is a low-level blitter which reads from the AudioBuffer and applies a grain envelope.
+ void readFromBufferWithGrainEnvelope(float* sourceL, float* sourceR, float* destinationL, float* destinationR, size_t framesToProcess);
+};
+
+} // namespace WebCore
+
+#endif // AudioBufferSourceNode_h
diff --git a/Source/WebCore/webaudio/AudioBufferSourceNode.idl b/Source/WebCore/webaudio/AudioBufferSourceNode.idl
new file mode 100644
index 0000000..dec7461
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.idl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+module audio {
+ // A cached (non-streamed), memory-resident audio source
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] AudioBufferSourceNode : AudioSourceNode {
+ attribute [JSCCustomSetter] AudioBuffer buffer;
+
+ readonly attribute AudioGain gain;
+ readonly attribute AudioParam playbackRate;
+ attribute boolean looping; // FIXME: change name to 'loop' once samples are updated
+
+ void noteOn(in float when);
+ void noteGrainOn(in float when, in float grainOffset, in float grainDuration);
+ void noteOff(in float when);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioChannelMerger.cpp b/Source/WebCore/webaudio/AudioChannelMerger.cpp
new file mode 100644
index 0000000..c418a61
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelMerger.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "AudioChannelMerger.h"
+
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+
+namespace WebCore {
+
+// This is considering that 5.1 (6 channels) is the largest we'll ever deal with.
+// It can easily be increased to support more if the web audio specification is updated.
+const unsigned NumberOfInputs = 6;
+
+AudioChannelMerger::AudioChannelMerger(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ // Create a fixed number of inputs (able to handle the maximum number of channels we deal with).
+ for (unsigned i = 0; i < NumberOfInputs; ++i)
+ addInput(adoptPtr(new AudioNodeInput(this)));
+
+ addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+ setType(NodeTypeChannelMerger);
+
+ initialize();
+}
+
+void AudioChannelMerger::process(size_t framesToProcess)
+{
+ AudioNodeOutput* output = this->output(0);
+ ASSERT(output);
+ ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus()->length());
+
+ // Count how many channels we have all together from all of the inputs.
+ unsigned numberOfOutputChannels = 0;
+ for (unsigned i = 0; i < numberOfInputs(); ++i) {
+ AudioNodeInput* input = this->input(i);
+ if (input->isConnected())
+ numberOfOutputChannels += input->bus()->numberOfChannels();
+ }
+
+ // Set the correct number of channels on the output
+ output->setNumberOfChannels(numberOfOutputChannels);
+
+ // Now merge the channels back into one output.
+ unsigned outputChannelIndex = 0;
+ for (unsigned i = 0; i < numberOfInputs(); ++i) {
+ AudioNodeInput* input = this->input(i);
+ if (input->isConnected()) {
+ unsigned numberOfInputChannels = input->bus()->numberOfChannels();
+
+ // Merge channels from this particular input.
+ for (unsigned j = 0; j < numberOfInputChannels; ++j) {
+ AudioChannel* inputChannel = input->bus()->channel(j);
+ AudioChannel* outputChannel = output->bus()->channel(outputChannelIndex);
+ outputChannel->copyFrom(inputChannel);
+
+ ++outputChannelIndex;
+ }
+ }
+ }
+
+ ASSERT(outputChannelIndex == numberOfOutputChannels);
+}
+
+void AudioChannelMerger::reset()
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioChannelMerger.h b/Source/WebCore/webaudio/AudioChannelMerger.h
new file mode 100644
index 0000000..20a9628
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelMerger.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioChannelMerger_h
+#define AudioChannelMerger_h
+
+#include "AudioNode.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+class AudioChannelMerger : public AudioNode {
+public:
+ static PassRefPtr<AudioChannelMerger> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new AudioChannelMerger(context, sampleRate));
+ }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+private:
+ AudioChannelMerger(AudioContext*, double sampleRate);
+};
+
+} // namespace WebCore
+
+#endif // AudioChannelMerger_h
diff --git a/Source/WebCore/webaudio/AudioChannelMerger.idl b/Source/WebCore/webaudio/AudioChannelMerger.idl
new file mode 100644
index 0000000..3862af9
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelMerger.idl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioChannelMerger : AudioNode {
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioChannelSplitter.cpp b/Source/WebCore/webaudio/AudioChannelSplitter.cpp
new file mode 100644
index 0000000..f4fa041
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelSplitter.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "AudioChannelSplitter.h"
+
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+
+namespace WebCore {
+
+// This is considering that 5.1 (6 channels) is the largest we'll ever deal with.
+// It can easily be increased to support more if the web audio specification is updated.
+const unsigned NumberOfOutputs = 6;
+
+AudioChannelSplitter::AudioChannelSplitter(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+
+ // Create a fixed number of outputs (able to handle the maximum number of channels fed to an input).
+ for (unsigned i = 0; i < NumberOfOutputs; ++i)
+ addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+ setType(NodeTypeChannelSplitter);
+
+ initialize();
+}
+
+void AudioChannelSplitter::process(size_t framesToProcess)
+{
+ AudioBus* source = input(0)->bus();
+ ASSERT(source);
+ ASSERT_UNUSED(framesToProcess, framesToProcess == source->length());
+
+ unsigned numberOfSourceChannels = source->numberOfChannels();
+
+ ASSERT(numberOfOutputs() == NumberOfOutputs);
+ for (unsigned i = 0; i < NumberOfOutputs; ++i) {
+ AudioBus* destination = output(i)->bus();
+ ASSERT(destination);
+
+ if (i < numberOfSourceChannels) {
+ // Split the channel out if it exists in the source.
+ // It would be nice to avoid the copy and simply pass along pointers, but this becomes extremely difficult with fanout and fanin.
+ destination->channel(0)->copyFrom(source->channel(i));
+ } else if (output(i)->renderingFanOutCount() > 0) {
+ // Only bother zeroing out the destination if it's connected to anything
+ destination->zero();
+ }
+ }
+}
+
+void AudioChannelSplitter::reset()
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioChannelSplitter.h b/Source/WebCore/webaudio/AudioChannelSplitter.h
new file mode 100644
index 0000000..7dadac5
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelSplitter.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioChannelSplitter_h
+#define AudioChannelSplitter_h
+
+#include "AudioNode.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+class AudioChannelSplitter : public AudioNode {
+public:
+ static PassRefPtr<AudioChannelSplitter> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new AudioChannelSplitter(context, sampleRate));
+ }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+private:
+ AudioChannelSplitter(AudioContext*, double sampleRate);
+};
+
+} // namespace WebCore
+
+#endif // AudioChannelSplitter_h
diff --git a/Source/WebCore/webaudio/AudioChannelSplitter.idl b/Source/WebCore/webaudio/AudioChannelSplitter.idl
new file mode 100644
index 0000000..076c051
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelSplitter.idl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioChannelSplitter : AudioNode {
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioContext.cpp b/Source/WebCore/webaudio/AudioContext.cpp
new file mode 100644
index 0000000..a452775
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.cpp
@@ -0,0 +1,529 @@
+/*
+ * 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 "AudioContext.h"
+
+#include "ArrayBuffer.h"
+#include "AudioBuffer.h"
+#include "AudioBufferSourceNode.h"
+#include "AudioChannelMerger.h"
+#include "AudioChannelSplitter.h"
+#include "AudioGainNode.h"
+#include "AudioListener.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "AudioPannerNode.h"
+#include "ConvolverNode.h"
+#include "DelayNode.h"
+#include "Document.h"
+#include "HRTFDatabaseLoader.h"
+#include "HRTFPanner.h"
+#include "HighPass2FilterNode.h"
+#include "JavaScriptAudioNode.h"
+#include "LowPass2FilterNode.h"
+#include "PlatformString.h"
+#include "RealtimeAnalyserNode.h"
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefCounted.h>
+
+// FIXME: check the proper way to reference an undefined thread ID
+const int UndefinedThreadIdentifier = 0xffffffff;
+
+const unsigned MaxNodesToDeletePerQuantum = 10;
+
+namespace WebCore {
+
+PassRefPtr<AudioContext> AudioContext::create(Document* document)
+{
+ return adoptRef(new AudioContext(document));
+}
+
+AudioContext::AudioContext(Document* document)
+ : ActiveDOMObject(document, this)
+ , m_isInitialized(false)
+ , m_isAudioThreadFinished(false)
+ , m_document(document)
+ , m_destinationNode(0)
+ , m_connectionCount(0)
+ , m_audioThread(0)
+ , m_graphOwnerThread(UndefinedThreadIdentifier)
+{
+ // Note: because adoptRef() won't be called until we leave this constructor, but code in this constructor needs to reference this context,
+ // relax the check.
+ relaxAdoptionRequirement();
+
+ m_destinationNode = AudioDestinationNode::create(this);
+ m_listener = AudioListener::create();
+ m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
+ m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
+
+ // This sets in motion an asynchronous loading mechanism on another thread.
+ // We can check m_hrtfDatabaseLoader->isLoaded() to find out whether or not it has been fully loaded.
+ // It's not that useful to have a callback function for this since the audio thread automatically starts rendering on the graph
+ // when this has finished (see AudioDestinationNode).
+ m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate());
+}
+
+AudioContext::~AudioContext()
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: AudioContext::~AudioContext()\n", this);
+#endif
+ // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
+ ASSERT(!m_nodesToDelete.size());
+ ASSERT(!m_referencedNodes.size());
+ ASSERT(!m_finishedNodes.size());
+}
+
+void AudioContext::lazyInitialize()
+{
+ if (!m_isInitialized) {
+ // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
+ ASSERT(!m_isAudioThreadFinished);
+ if (!m_isAudioThreadFinished) {
+ if (m_destinationNode.get()) {
+ // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
+ // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
+ m_destinationNode->initialize();
+ }
+ m_isInitialized = true;
+ }
+ }
+}
+
+void AudioContext::uninitialize()
+{
+ if (m_isInitialized) {
+ // This stops the audio thread and all audio rendering.
+ m_destinationNode->uninitialize();
+
+ // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
+ m_isAudioThreadFinished = true;
+
+ // We have to release our reference to the destination node before the context will ever be deleted since the destination node holds a reference to the context.
+ m_destinationNode.clear();
+
+ // Get rid of the sources which may still be playing.
+ derefUnfinishedSourceNodes();
+
+ // Because the AudioBuffers are garbage collected, we can't delete them here.
+ // Instead, at least release the potentially large amount of allocated memory for the audio data.
+ // Note that we do this *after* the context is uninitialized and stops processing audio.
+ for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i)
+ m_allocatedBuffers[i]->releaseMemory();
+ m_allocatedBuffers.clear();
+
+ m_isInitialized = false;
+ }
+}
+
+bool AudioContext::isInitialized() const
+{
+ return m_isInitialized;
+}
+
+bool AudioContext::isRunnable() const
+{
+ if (!isInitialized())
+ return false;
+
+ // Check with the HRTF spatialization system to see if it's finished loading.
+ return m_hrtfDatabaseLoader->isLoaded();
+}
+
+void AudioContext::stop()
+{
+ m_document = 0; // document is going away
+ uninitialize();
+}
+
+Document* AudioContext::document()
+{
+ ASSERT(m_document);
+ return m_document;
+}
+
+bool AudioContext::hasDocument()
+{
+ return m_document;
+}
+
+void AudioContext::refBuffer(PassRefPtr<AudioBuffer> buffer)
+{
+ m_allocatedBuffers.append(buffer);
+}
+
+PassRefPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
+{
+ return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
+}
+
+PassRefPtr<AudioBuffer> AudioContext::createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono)
+{
+ ASSERT(arrayBuffer);
+ if (!arrayBuffer)
+ return 0;
+
+ return AudioBuffer::createFromAudioFileData(arrayBuffer->data(), arrayBuffer->byteLength(), mixToMono, sampleRate());
+}
+
+PassRefPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
+
+ refNode(node.get()); // context keeps reference until source has finished playing
+ return node;
+}
+
+PassRefPtr<JavaScriptAudioNode> AudioContext::createJavaScriptNode(size_t bufferSize)
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ RefPtr<JavaScriptAudioNode> node = JavaScriptAudioNode::create(this, m_destinationNode->sampleRate(), bufferSize);
+
+ refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
+ return node;
+}
+
+PassRefPtr<LowPass2FilterNode> AudioContext::createLowPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return LowPass2FilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<HighPass2FilterNode> AudioContext::createHighPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return HighPass2FilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioPannerNode> AudioContext::createPanner()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioPannerNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<ConvolverNode> AudioContext::createConvolver()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return ConvolverNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<RealtimeAnalyserNode> AudioContext::createAnalyser()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return RealtimeAnalyserNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioGainNode> AudioContext::createGainNode()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioGainNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<DelayNode> AudioContext::createDelayNode()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return DelayNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioChannelSplitter> AudioContext::createChannelSplitter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioChannelSplitter::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioChannelMerger> AudioContext::createChannelMerger()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioChannelMerger::create(this, m_destinationNode->sampleRate());
+}
+
+void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
+{
+ ASSERT(isAudioThread());
+ m_finishedNodes.append(node);
+}
+
+void AudioContext::derefFinishedSourceNodes()
+{
+ ASSERT(isGraphOwner());
+ ASSERT(isAudioThread() || isAudioThreadFinished());
+ for (unsigned i = 0; i < m_finishedNodes.size(); i++)
+ derefNode(m_finishedNodes[i]);
+
+ m_finishedNodes.clear();
+}
+
+void AudioContext::refNode(AudioNode* node)
+{
+ ASSERT(isMainThread());
+ AutoLocker locker(this);
+
+ node->ref(AudioNode::RefTypeConnection);
+ m_referencedNodes.append(node);
+}
+
+void AudioContext::derefNode(AudioNode* node)
+{
+ ASSERT(isGraphOwner());
+
+ node->deref(AudioNode::RefTypeConnection);
+
+ for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
+ if (node == m_referencedNodes[i]) {
+ m_referencedNodes.remove(i);
+ break;
+ }
+ }
+}
+
+void AudioContext::derefUnfinishedSourceNodes()
+{
+ ASSERT(isMainThread() && isAudioThreadFinished());
+ for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
+ m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
+
+ m_referencedNodes.clear();
+}
+
+void AudioContext::lock(bool& mustReleaseLock)
+{
+ // Don't allow regular lock in real-time audio thread.
+ ASSERT(isMainThread());
+
+ ThreadIdentifier thisThread = currentThread();
+
+ if (thisThread == m_graphOwnerThread) {
+ // We already have the lock.
+ mustReleaseLock = false;
+ } else {
+ // Acquire the lock.
+ m_contextGraphMutex.lock();
+ m_graphOwnerThread = thisThread;
+ mustReleaseLock = true;
+ }
+}
+
+bool AudioContext::tryLock(bool& mustReleaseLock)
+{
+ ThreadIdentifier thisThread = currentThread();
+ bool isAudioThread = thisThread == audioThread();
+
+ // Try to catch cases of using try lock on main thread - it should use regular lock.
+ ASSERT(isAudioThread || isAudioThreadFinished());
+
+ if (!isAudioThread) {
+ // In release build treat tryLock() as lock() (since above ASSERT(isAudioThread) never fires) - this is the best we can do.
+ lock(mustReleaseLock);
+ return true;
+ }
+
+ bool hasLock;
+
+ if (thisThread == m_graphOwnerThread) {
+ // Thread already has the lock.
+ hasLock = true;
+ mustReleaseLock = false;
+ } else {
+ // Don't already have the lock - try to acquire it.
+ hasLock = m_contextGraphMutex.tryLock();
+
+ if (hasLock)
+ m_graphOwnerThread = thisThread;
+
+ mustReleaseLock = hasLock;
+ }
+
+ return hasLock;
+}
+
+void AudioContext::unlock()
+{
+ ASSERT(currentThread() == m_graphOwnerThread);
+
+ m_graphOwnerThread = UndefinedThreadIdentifier;
+ m_contextGraphMutex.unlock();
+}
+
+bool AudioContext::isAudioThread() const
+{
+ return currentThread() == m_audioThread;
+}
+
+bool AudioContext::isGraphOwner() const
+{
+ return currentThread() == m_graphOwnerThread;
+}
+
+void AudioContext::addDeferredFinishDeref(AudioNode* node, AudioNode::RefType refType)
+{
+ ASSERT(isAudioThread());
+ m_deferredFinishDerefList.append(AudioContext::RefInfo(node, refType));
+}
+
+void AudioContext::handlePreRenderTasks()
+{
+ ASSERT(isAudioThread());
+
+ // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes).
+ // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes.
+ bool mustReleaseLock;
+ if (tryLock(mustReleaseLock)) {
+ // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
+ handleDirtyAudioNodeInputs();
+ handleDirtyAudioNodeOutputs();
+
+ if (mustReleaseLock)
+ unlock();
+ }
+}
+
+void AudioContext::handlePostRenderTasks()
+{
+ ASSERT(isAudioThread());
+
+ // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently.
+ // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed
+ // from the render graph (in which case they'll render silence).
+ bool mustReleaseLock;
+ if (tryLock(mustReleaseLock)) {
+ // Take care of finishing any derefs where the tryLock() failed previously.
+ handleDeferredFinishDerefs();
+
+ // Dynamically clean up nodes which are no longer needed.
+ derefFinishedSourceNodes();
+
+ // Finally actually delete.
+ deleteMarkedNodes();
+
+ // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
+ handleDirtyAudioNodeInputs();
+ handleDirtyAudioNodeOutputs();
+
+ if (mustReleaseLock)
+ unlock();
+ }
+}
+
+void AudioContext::handleDeferredFinishDerefs()
+{
+ ASSERT(isAudioThread() && isGraphOwner());
+ for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
+ AudioNode* node = m_deferredFinishDerefList[i].m_node;
+ AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType;
+ node->finishDeref(refType);
+ }
+
+ m_deferredFinishDerefList.clear();
+}
+
+void AudioContext::markForDeletion(AudioNode* node)
+{
+ ASSERT(isGraphOwner());
+ m_nodesToDelete.append(node);
+}
+
+void AudioContext::deleteMarkedNodes()
+{
+ ASSERT(isGraphOwner() || isAudioThreadFinished());
+
+ // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
+ size_t nodesDeleted = 0;
+ while (size_t n = m_nodesToDelete.size()) {
+ AudioNode* node = m_nodesToDelete[n - 1];
+ m_nodesToDelete.removeLast();
+
+ // Before deleting the node, clear out any AudioNodeInputs from m_dirtyAudioNodeInputs.
+ unsigned numberOfInputs = node->numberOfInputs();
+ for (unsigned i = 0; i < numberOfInputs; ++i)
+ m_dirtyAudioNodeInputs.remove(node->input(i));
+
+ // Before deleting the node, clear out any AudioNodeOutputs from m_dirtyAudioNodeOutputs.
+ unsigned numberOfOutputs = node->numberOfOutputs();
+ for (unsigned i = 0; i < numberOfOutputs; ++i)
+ m_dirtyAudioNodeOutputs.remove(node->output(i));
+
+ // Finally, delete it.
+ delete node;
+
+ // Don't delete too many nodes per render quantum since we don't want to do too much work in the realtime audio thread.
+ if (++nodesDeleted > MaxNodesToDeletePerQuantum)
+ break;
+ }
+}
+
+void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input)
+{
+ ASSERT(isGraphOwner());
+ m_dirtyAudioNodeInputs.add(input);
+}
+
+void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
+{
+ ASSERT(isGraphOwner());
+ m_dirtyAudioNodeOutputs.add(output);
+}
+
+void AudioContext::handleDirtyAudioNodeInputs()
+{
+ ASSERT(isGraphOwner());
+
+ for (HashSet<AudioNodeInput*>::iterator i = m_dirtyAudioNodeInputs.begin(); i != m_dirtyAudioNodeInputs.end(); ++i)
+ (*i)->updateRenderingState();
+
+ m_dirtyAudioNodeInputs.clear();
+}
+
+void AudioContext::handleDirtyAudioNodeOutputs()
+{
+ ASSERT(isGraphOwner());
+
+ for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
+ (*i)->updateRenderingState();
+
+ m_dirtyAudioNodeOutputs.clear();
+}
+
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioContext.h b/Source/WebCore/webaudio/AudioContext.h
new file mode 100644
index 0000000..ddd474c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.h
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioContext_h
+#define AudioContext_h
+
+#include "ActiveDOMObject.h"
+#include "AudioBus.h"
+#include "AudioDestinationNode.h"
+#include "HRTFDatabaseLoader.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class ArrayBuffer;
+class AudioBuffer;
+class AudioBufferSourceNode;
+class AudioChannelMerger;
+class AudioChannelSplitter;
+class AudioGainNode;
+class AudioPannerNode;
+class AudioListener;
+class DelayNode;
+class Document;
+class LowPass2FilterNode;
+class HighPass2FilterNode;
+class ConvolverNode;
+class RealtimeAnalyserNode;
+class JavaScriptAudioNode;
+
+// AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
+// For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism.
+
+class AudioContext : public ActiveDOMObject, public RefCounted<AudioContext> {
+public:
+ static PassRefPtr<AudioContext> create(Document*);
+
+ virtual ~AudioContext();
+
+ bool isInitialized() const;
+
+ // Returns true when initialize() was called AND all asynchronous initialization has completed.
+ bool isRunnable() const;
+
+ // Document notification
+ virtual void stop();
+
+ Document* document(); // ASSERTs if document no longer exists.
+ bool hasDocument();
+
+ AudioDestinationNode* destination() { return m_destinationNode.get(); }
+ double currentTime() { return m_destinationNode->currentTime(); }
+ double sampleRate() { return m_destinationNode->sampleRate(); }
+
+ PassRefPtr<AudioBuffer> createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate);
+ PassRefPtr<AudioBuffer> createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono);
+
+ // Keep track of this buffer so we can release memory after the context is shut down...
+ void refBuffer(PassRefPtr<AudioBuffer> buffer);
+
+ AudioListener* listener() { return m_listener.get(); }
+
+ // The AudioNode create methods are called on the main thread (from JavaScript).
+ PassRefPtr<AudioBufferSourceNode> createBufferSource();
+ PassRefPtr<AudioGainNode> createGainNode();
+ PassRefPtr<DelayNode> createDelayNode();
+ PassRefPtr<LowPass2FilterNode> createLowPass2Filter();
+ PassRefPtr<HighPass2FilterNode> createHighPass2Filter();
+ PassRefPtr<AudioPannerNode> createPanner();
+ PassRefPtr<ConvolverNode> createConvolver();
+ PassRefPtr<RealtimeAnalyserNode> createAnalyser();
+ PassRefPtr<JavaScriptAudioNode> createJavaScriptNode(size_t bufferSize);
+ PassRefPtr<AudioChannelSplitter> createChannelSplitter();
+ PassRefPtr<AudioChannelMerger> createChannelMerger();
+
+ AudioBus* temporaryMonoBus() { return m_temporaryMonoBus.get(); }
+ AudioBus* temporaryStereoBus() { return m_temporaryStereoBus.get(); }
+
+ // When a source node has no more processing to do (has finished playing), then it tells the context to dereference it.
+ void notifyNodeFinishedProcessing(AudioNode*);
+
+ // Called at the start of each render quantum.
+ void handlePreRenderTasks();
+
+ // Called at the end of each render quantum.
+ void handlePostRenderTasks();
+
+ // Called periodically at the end of each render quantum to dereference finished source nodes.
+ void derefFinishedSourceNodes();
+
+ // We reap all marked nodes at the end of each realtime render quantum in deleteMarkedNodes().
+ void markForDeletion(AudioNode*);
+ void deleteMarkedNodes();
+
+ // Keeps track of the number of connections made.
+ void incrementConnectionCount()
+ {
+ ASSERT(isMainThread());
+ m_connectionCount++;
+ }
+
+ unsigned connectionCount() const { return m_connectionCount; }
+
+ //
+ // Thread Safety and Graph Locking:
+ //
+
+ void setAudioThread(ThreadIdentifier thread) { m_audioThread = thread; } // FIXME: check either not initialized or the same
+ ThreadIdentifier audioThread() const { return m_audioThread; }
+ bool isAudioThread() const;
+
+ // Returns true only after the audio thread has been started and then shutdown.
+ bool isAudioThreadFinished() { return m_isAudioThreadFinished; }
+
+ // mustReleaseLock is set to true if we acquired the lock in this method call and caller must unlock(), false if it was previously acquired.
+ void lock(bool& mustReleaseLock);
+
+ // Returns true if we own the lock.
+ // mustReleaseLock is set to true if we acquired the lock in this method call and caller must unlock(), false if it was previously acquired.
+ bool tryLock(bool& mustReleaseLock);
+
+ void unlock();
+
+ // Returns true if this thread owns the context's lock.
+ bool isGraphOwner() const;
+
+ class AutoLocker {
+ public:
+ AutoLocker(AudioContext* context)
+ : m_context(context)
+ {
+ ASSERT(context);
+ context->lock(m_mustReleaseLock);
+ }
+
+ ~AutoLocker()
+ {
+ if (m_mustReleaseLock)
+ m_context->unlock();
+ }
+ private:
+ AudioContext* m_context;
+ bool m_mustReleaseLock;
+ };
+
+ // In AudioNode::deref() a tryLock() is used for calling finishDeref(), but if it fails keep track here.
+ void addDeferredFinishDeref(AudioNode*, AudioNode::RefType);
+
+ // In the audio thread at the start of each render cycle, we'll call handleDeferredFinishDerefs().
+ void handleDeferredFinishDerefs();
+
+ // Only accessed when the graph lock is held.
+ void markAudioNodeInputDirty(AudioNodeInput*);
+ void markAudioNodeOutputDirty(AudioNodeOutput*);
+
+private:
+ AudioContext(Document*);
+ void lazyInitialize();
+ void uninitialize();
+
+ bool m_isInitialized;
+ bool m_isAudioThreadFinished;
+ bool m_isAudioThreadShutdown;
+
+ Document* m_document;
+
+ // The context itself keeps a reference to all source nodes. The source nodes, then reference all nodes they're connected to.
+ // In turn, these nodes reference all nodes they're connected to. All nodes are ultimately connected to the AudioDestinationNode.
+ // When the context dereferences a source node, it will be deactivated from the rendering graph along with all other nodes it is
+ // uniquely connected to. See the AudioNode::ref() and AudioNode::deref() methods for more details.
+ void refNode(AudioNode*);
+ void derefNode(AudioNode*);
+
+ // When the context goes away, there might still be some sources which haven't finished playing.
+ // Make sure to dereference them here.
+ void derefUnfinishedSourceNodes();
+
+ RefPtr<AudioDestinationNode> m_destinationNode;
+ RefPtr<AudioListener> m_listener;
+
+ // Only accessed in the main thread.
+ Vector<RefPtr<AudioBuffer> > m_allocatedBuffers;
+
+ // Only accessed in the audio thread.
+ Vector<AudioNode*> m_finishedNodes;
+
+ // We don't use RefPtr<AudioNode> here because AudioNode has a more complex ref() / deref() implementation
+ // with an optional argument for refType. We need to use the special refType: RefTypeConnection
+ // Either accessed when the graph lock is held, or on the main thread when the audio thread has finished.
+ Vector<AudioNode*> m_referencedNodes;
+
+ // Accumulate nodes which need to be deleted at the end of a render cycle (in realtime thread) here.
+ Vector<AudioNode*> m_nodesToDelete;
+
+ // Only accessed when the graph lock is held.
+ HashSet<AudioNodeInput*> m_dirtyAudioNodeInputs;
+ HashSet<AudioNodeOutput*> m_dirtyAudioNodeOutputs;
+ void handleDirtyAudioNodeInputs();
+ void handleDirtyAudioNodeOutputs();
+
+ OwnPtr<AudioBus> m_temporaryMonoBus;
+ OwnPtr<AudioBus> m_temporaryStereoBus;
+
+ unsigned m_connectionCount;
+
+ // Graph locking.
+ Mutex m_contextGraphMutex;
+ volatile ThreadIdentifier m_audioThread;
+ volatile ThreadIdentifier m_graphOwnerThread; // if the lock is held then this is the thread which owns it, otherwise == UndefinedThreadIdentifier
+
+ // Deferred de-referencing.
+ struct RefInfo {
+ RefInfo(AudioNode* node, AudioNode::RefType refType)
+ : m_node(node)
+ , m_refType(refType)
+ {
+ }
+ AudioNode* m_node;
+ AudioNode::RefType m_refType;
+ };
+
+ // Only accessed in the audio thread.
+ Vector<RefInfo> m_deferredFinishDerefList;
+
+ // HRTF Database loader
+ RefPtr<HRTFDatabaseLoader> m_hrtfDatabaseLoader;
+};
+
+} // WebCore
+
+#endif // AudioContext_h
diff --git a/Source/WebCore/webaudio/AudioContext.idl b/Source/WebCore/webaudio/AudioContext.idl
new file mode 100644
index 0000000..9f0f49c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.idl
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+module webaudio {
+ interface [
+ Conditional=WEB_AUDIO,
+ CanBeConstructed,
+ CustomConstructFunction,
+ V8CustomConstructor
+ ] AudioContext {
+ // All rendered audio ultimately connects to destination, which represents the audio hardware.
+ readonly attribute AudioDestinationNode destination;
+
+ // All scheduled times are relative to this time in seconds.
+ readonly attribute float currentTime;
+
+ // All AudioNodes in the context run at this sample-rate (in sample-frames per second).
+ readonly attribute float sampleRate;
+
+ // All panning is relative to this listener.
+ readonly attribute AudioListener listener;
+
+ AudioBuffer createBuffer(in unsigned long numberOfChannels, in unsigned long numberOfFrames, in float sampleRate);
+ AudioBuffer createBuffer(in ArrayBuffer buffer, in boolean mixToMono);
+
+ // Source
+ AudioBufferSourceNode createBufferSource();
+
+ // Processing nodes
+ AudioGainNode createGainNode();
+ DelayNode createDelayNode();
+ LowPass2FilterNode createLowPass2Filter();
+ HighPass2FilterNode createHighPass2Filter();
+ AudioPannerNode createPanner();
+ ConvolverNode createConvolver();
+ RealtimeAnalyserNode createAnalyser();
+ JavaScriptAudioNode createJavaScriptNode(in unsigned long bufferSize);
+
+ // Channel splitting and merging
+ AudioChannelSplitter createChannelSplitter();
+ AudioChannelMerger createChannelMerger();
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioDestinationNode.cpp b/Source/WebCore/webaudio/AudioDestinationNode.cpp
new file mode 100644
index 0000000..d2f4928
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioDestinationNode.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "AudioDestinationNode.h"
+
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+AudioDestinationNode::AudioDestinationNode(AudioContext* context)
+ : AudioNode(context, AudioDestination::hardwareSampleRate())
+ , m_currentTime(0.0)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+
+ setType(NodeTypeDestination);
+
+ initialize();
+}
+
+AudioDestinationNode::~AudioDestinationNode()
+{
+ uninitialize();
+}
+
+void AudioDestinationNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ double hardwareSampleRate = AudioDestination::hardwareSampleRate();
+#ifndef NDEBUG
+ fprintf(stderr, ">>>> hardwareSampleRate = %f\n", hardwareSampleRate);
+#endif
+
+ m_destination = AudioDestination::create(*this, hardwareSampleRate);
+ m_destination->start();
+
+ AudioNode::initialize();
+}
+
+void AudioDestinationNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_destination->stop();
+
+ AudioNode::uninitialize();
+}
+
+// The audio hardware calls us back here to gets its input stream.
+void AudioDestinationNode::provideInput(AudioBus* destinationBus, size_t numberOfFrames)
+{
+ context()->setAudioThread(currentThread());
+
+ if (!context()->isRunnable()) {
+ destinationBus->zero();
+ return;
+ }
+
+ // Let the context take care of any business at the start of each render quantum.
+ context()->handlePreRenderTasks();
+
+ // This will cause the node(s) connected to us to process, which in turn will pull on their input(s),
+ // all the way backwards through the rendering graph.
+ AudioBus* renderedBus = input(0)->pull(destinationBus, numberOfFrames);
+
+ if (!renderedBus)
+ destinationBus->zero();
+ else if (renderedBus != destinationBus) {
+ // in-place processing was not possible - so copy
+ destinationBus->copyFrom(*renderedBus);
+ }
+
+ // Let the context take care of any business at the end of each render quantum.
+ context()->handlePostRenderTasks();
+
+ // Advance current time.
+ m_currentTime += numberOfFrames / sampleRate();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioDestinationNode.h b/Source/WebCore/webaudio/AudioDestinationNode.h
new file mode 100644
index 0000000..4c21bb8
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioDestinationNode.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioDestinationNode_h
+#define AudioDestinationNode_h
+
+#include "AudioDestination.h"
+#include "AudioNode.h"
+#include "AudioSourceProvider.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+class AudioContext;
+
+class AudioDestinationNode : public AudioNode, public AudioSourceProvider {
+public:
+ static PassRefPtr<AudioDestinationNode> create(AudioContext* context)
+ {
+ return adoptRef(new AudioDestinationNode(context));
+ }
+
+ virtual ~AudioDestinationNode();
+
+ // AudioNode
+ virtual void process(size_t) { }; // we're pulled by hardware so this is never called
+ virtual void reset() { m_currentTime = 0.0; };
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // The audio hardware calls here periodically to gets its input stream.
+ virtual void provideInput(AudioBus*, size_t numberOfFrames);
+
+ double currentTime() { return m_currentTime; }
+
+ double sampleRate() const { return m_destination->sampleRate(); }
+
+ unsigned numberOfChannels() const { return 2; } // FIXME: update when multi-channel (more than stereo) is supported
+
+private:
+ AudioDestinationNode(AudioContext*);
+
+ OwnPtr<AudioDestination> m_destination;
+ double m_currentTime;
+};
+
+} // namespace WebCore
+
+#endif // AudioDestinationNode_h
diff --git a/Source/WebCore/webaudio/AudioDestinationNode.idl b/Source/WebCore/webaudio/AudioDestinationNode.idl
new file mode 100644
index 0000000..d7bf09f
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioDestinationNode.idl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] AudioDestinationNode : AudioNode {
+ readonly attribute long numberOfChannels;
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioGain.h b/Source/WebCore/webaudio/AudioGain.h
new file mode 100644
index 0000000..eb3c52d
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGain.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioGain_h
+#define AudioGain_h
+
+#include "AudioParam.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioGain : public AudioParam {
+public:
+ static PassRefPtr<AudioGain> create(const char* name, double defaultValue, double minValue, double maxValue)
+ {
+ return adoptRef(new AudioGain(name, defaultValue, minValue, maxValue));
+ }
+
+private:
+ AudioGain(const char* name, double defaultValue, double minValue, double maxValue)
+ : AudioParam(name, defaultValue, minValue, maxValue)
+ {
+ }
+};
+
+} // namespace WebCore
+
+#endif // AudioParam_h
diff --git a/Source/WebCore/webaudio/AudioGain.idl b/Source/WebCore/webaudio/AudioGain.idl
new file mode 100644
index 0000000..ead7c9a
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGain.idl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] AudioGain : AudioParam {
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioGainNode.cpp b/Source/WebCore/webaudio/AudioGainNode.cpp
new file mode 100644
index 0000000..5b9af07
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGainNode.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "AudioGainNode.h"
+
+#include "AudioBus.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+
+namespace WebCore {
+
+AudioGainNode::AudioGainNode(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+ , m_lastGain(1.0)
+{
+ m_gain = AudioGain::create("gain", 1.0, 0.0, 1.0);
+
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+ setType(NodeTypeGain);
+
+ initialize();
+}
+
+void AudioGainNode::process(size_t /*framesToProcess*/)
+{
+ // FIXME: there is a nice optimization to avoid processing here, and let the gain change
+ // happen in the summing junction input of the AudioNode we're connected to.
+ // Then we can avoid all of the following:
+
+ AudioBus* outputBus = output(0)->bus();
+ ASSERT(outputBus);
+
+ // The realtime thread can't block on this lock, so we call tryLock() instead.
+ if (m_processLock.tryLock()) {
+ if (!isInitialized() || !input(0)->isConnected())
+ outputBus->zero();
+ else {
+ AudioBus* inputBus = input(0)->bus();
+
+ // Apply the gain with de-zippering into the output bus.
+ outputBus->copyWithGainFrom(*inputBus, &m_lastGain, gain()->value());
+ }
+
+ m_processLock.unlock();
+ } else {
+ // Too bad - the tryLock() failed. We must be in the middle of re-connecting and were already outputting silence anyway...
+ outputBus->zero();
+ }
+}
+
+void AudioGainNode::reset()
+{
+ // Snap directly to desired gain.
+ m_lastGain = gain()->value();
+}
+
+// FIXME: this can go away when we do mixing with gain directly in summing junction of AudioNodeInput
+//
+// As soon as we know the channel count of our input, we can lazily initialize.
+// Sometimes this may be called more than once with different channel counts, in which case we must safely
+// uninitialize and then re-initialize with the new channel count.
+void AudioGainNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
+{
+ ASSERT(input && input == this->input(0));
+ if (input != this->input(0))
+ return;
+
+ unsigned numberOfChannels = input->numberOfChannels();
+
+ if (isInitialized() && numberOfChannels != output(0)->numberOfChannels()) {
+ // We're already initialized but the channel count has changed.
+ // We need to be careful since we may be actively processing right now, so synchronize with process().
+ MutexLocker locker(m_processLock);
+ uninitialize();
+ }
+
+ if (!isInitialized()) {
+ // This will propagate the channel count to any nodes connected further downstream in the graph.
+ output(0)->setNumberOfChannels(numberOfChannels);
+ initialize();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioGainNode.h b/Source/WebCore/webaudio/AudioGainNode.h
new file mode 100644
index 0000000..3710472
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGainNode.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioGainNode_h
+#define AudioGainNode_h
+
+#include "AudioGain.h"
+#include "AudioNode.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+// AudioGainNode is an AudioNode with one input and one output which applies a gain (volume) change to the audio signal.
+// De-zippering (smoothing) is applied when the gain value is changed dynamically.
+
+class AudioGainNode : public AudioNode {
+public:
+ static PassRefPtr<AudioGainNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new AudioGainNode(context, sampleRate));
+ }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+ // Called in the main thread when the number of channels for the input may have changed.
+ virtual void checkNumberOfChannelsForInput(AudioNodeInput*);
+
+ // JavaScript interface
+ AudioGain* gain() { return m_gain.get(); }
+
+private:
+ AudioGainNode(AudioContext*, double sampleRate);
+
+ double m_lastGain; // for de-zippering
+ RefPtr<AudioGain> m_gain;
+
+ // This synchronizes live channel count changes which require an uninitialization / re-initialization.
+ // FIXME: this can go away when we implement optimization for mixing with gain directly in summing junction of AudioNodeInput.
+ mutable Mutex m_processLock;
+};
+
+} // namespace WebCore
+
+#endif // AudioGainNode_h
diff --git a/Source/WebCore/webaudio/AudioGainNode.idl b/Source/WebCore/webaudio/AudioGainNode.idl
new file mode 100644
index 0000000..3d4f40f
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGainNode.idl
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] AudioGainNode : AudioNode {
+ // FIXME: eventually it will be interesting to remove the readonly restriction, but need to properly deal with thread safety here.
+ readonly attribute AudioGain gain;
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioListener.cpp b/Source/WebCore/webaudio/AudioListener.cpp
new file mode 100644
index 0000000..44fb02c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioListener.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "AudioListener.h"
+
+#include "AudioBus.h"
+
+namespace WebCore {
+
+AudioListener::AudioListener()
+ : m_position(0, 0, 0)
+ , m_orientation(0.0, 0.0, -1.0)
+ , m_upVector(0.0, 1.0, 0.0)
+ , m_velocity(0, 0, 0)
+ , m_dopplerFactor(1.0)
+ , m_speedOfSound(343.3)
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioListener.h b/Source/WebCore/webaudio/AudioListener.h
new file mode 100644
index 0000000..5281a89
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioListener.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioListener_h
+#define AudioListener_h
+
+#include "FloatPoint3D.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+// AudioListener maintains the state of the listener in the audio scene as defined in the OpenAL specification.
+
+class AudioListener : public RefCounted<AudioListener> {
+public:
+ static PassRefPtr<AudioListener> create()
+ {
+ return adoptRef(new AudioListener());
+ }
+
+ // Position
+ void setPosition(double x, double y, double z) { setPosition(FloatPoint3D(x, y, z)); }
+ void setPosition(const FloatPoint3D &position) { m_position = position; }
+ const FloatPoint3D& position() const { return m_position; }
+
+ // Orientation
+ void setOrientation(double x, double y, double z, double upX, double upY, double upZ)
+ {
+ setOrientation(FloatPoint3D(x, y, z));
+ setUpVector(FloatPoint3D(upX, upY, upZ));
+ }
+ void setOrientation(const FloatPoint3D &orientation) { m_orientation = orientation; }
+ const FloatPoint3D& orientation() const { return m_orientation; }
+
+ // Up-vector
+ void setUpVector(const FloatPoint3D &upVector) { m_upVector = upVector; }
+ const FloatPoint3D& upVector() const { return m_upVector; }
+
+ // Velocity
+ void setVelocity(double x, double y, double z) { setVelocity(FloatPoint3D(x, y, z)); }
+ void setVelocity(const FloatPoint3D &velocity) { m_velocity = velocity; }
+ const FloatPoint3D& velocity() const { return m_velocity; }
+
+ // Doppler factor
+ void setDopplerFactor(double dopplerFactor) { m_dopplerFactor = dopplerFactor; }
+ double dopplerFactor() const { return m_dopplerFactor; }
+
+ // Speed of sound
+ void setSpeedOfSound(double speedOfSound) { m_speedOfSound = speedOfSound; }
+ double speedOfSound() const { return m_speedOfSound; }
+
+private:
+ AudioListener();
+
+ // Position / Orientation
+ FloatPoint3D m_position;
+ FloatPoint3D m_orientation;
+ FloatPoint3D m_upVector;
+
+ FloatPoint3D m_velocity;
+
+ double m_dopplerFactor;
+ double m_speedOfSound;
+};
+
+} // WebCore
+
+#endif // AudioListener_h
diff --git a/Source/WebCore/webaudio/AudioListener.idl b/Source/WebCore/webaudio/AudioListener.idl
new file mode 100644
index 0000000..cf6d8cf
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioListener.idl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioListener {
+ attribute float dopplerFactor; // same as OpenAL (default 1.0)
+ attribute float speedOfSound; // in meters / second (default 343.3)
+
+ void setPosition(in float x, in float y, in float z);
+ void setOrientation(in float x, in float y, in float z, in float xUp, in float yUp, in float zUp);
+ void setVelocity(in float x, in float y, in float z);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioNode.cpp b/Source/WebCore/webaudio/AudioNode.cpp
new file mode 100644
index 0000000..18ddd3b
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNode.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 "AudioNode.h"
+
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include <wtf/Atomics.h>
+
+namespace WebCore {
+
+AudioNode::AudioNode(AudioContext* context, double sampleRate)
+ : m_isInitialized(false)
+ , m_type(NodeTypeUnknown)
+ , m_context(context)
+ , m_sampleRate(sampleRate)
+ , m_lastProcessingTime(-1.0)
+ , m_normalRefCount(1) // start out with normal refCount == 1 (like WTF::RefCounted class)
+ , m_connectionRefCount(0)
+ , m_disabledRefCount(0)
+ , m_isMarkedForDeletion(false)
+ , m_isDisabled(false)
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ if (!s_isNodeCountInitialized) {
+ s_isNodeCountInitialized = true;
+ atexit(AudioNode::printNodeCounts);
+ }
+#endif
+}
+
+AudioNode::~AudioNode()
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ --s_nodeCount[type()];
+ printf("%p: %d: AudioNode::~AudioNode() %d %d %d\n", this, type(), m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+}
+
+void AudioNode::initialize()
+{
+ m_isInitialized = true;
+}
+
+void AudioNode::uninitialize()
+{
+ m_isInitialized = false;
+}
+
+void AudioNode::setType(NodeType type)
+{
+ m_type = type;
+
+#if DEBUG_AUDIONODE_REFERENCES
+ ++s_nodeCount[type];
+#endif
+}
+
+void AudioNode::lazyInitialize()
+{
+ if (!isInitialized())
+ initialize();
+}
+
+void AudioNode::addInput(PassOwnPtr<AudioNodeInput> input)
+{
+ m_inputs.append(input);
+}
+
+void AudioNode::addOutput(PassOwnPtr<AudioNodeOutput> output)
+{
+ m_outputs.append(output);
+}
+
+AudioNodeInput* AudioNode::input(unsigned i)
+{
+ return m_inputs[i].get();
+}
+
+AudioNodeOutput* AudioNode::output(unsigned i)
+{
+ return m_outputs[i].get();
+}
+
+bool AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ // Sanity check input and output indices.
+ if (outputIndex >= numberOfOutputs())
+ return false;
+ if (destination && inputIndex >= destination->numberOfInputs())
+ return false;
+
+ AudioNodeOutput* output = this->output(outputIndex);
+ if (!destination) {
+ // Disconnect output from any inputs it may be currently connected to.
+ output->disconnectAllInputs();
+ return true;
+ }
+
+ AudioNodeInput* input = destination->input(inputIndex);
+ input->connect(output);
+
+ // Let context know that a connection has been made.
+ context()->incrementConnectionCount();
+
+ return true;
+}
+
+bool AudioNode::disconnect(unsigned outputIndex)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ return connect(0, outputIndex);
+}
+
+void AudioNode::processIfNecessary(size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ if (!isInitialized())
+ return;
+
+ // Ensure that we only process once per rendering quantum.
+ // This handles the "fanout" problem where an output is connected to multiple inputs.
+ // The first time we're called during this time slice we process, but after that we don't want to re-process,
+ // instead our output(s) will already have the results cached in their bus;
+ double currentTime = context()->currentTime();
+ if (m_lastProcessingTime != currentTime) {
+ m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph
+ pullInputs(framesToProcess);
+ process(framesToProcess);
+ }
+}
+
+void AudioNode::pullInputs(size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ // Process all of the AudioNodes connected to our inputs.
+ for (unsigned i = 0; i < m_inputs.size(); ++i)
+ input(i)->pull(0, framesToProcess);
+}
+
+void AudioNode::ref(RefType refType)
+{
+ switch (refType) {
+ case RefTypeNormal:
+ atomicIncrement(&m_normalRefCount);
+ break;
+ case RefTypeConnection:
+ atomicIncrement(&m_connectionRefCount);
+ break;
+ case RefTypeDisabled:
+ atomicIncrement(&m_disabledRefCount);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: %d: AudioNode::ref(%d) %d %d %d\n", this, type(), refType, m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+
+ if (m_connectionRefCount == 1 && refType == RefTypeConnection) {
+ // FIXME: implement wake-up - this is an advanced feature and is not necessary in a simple implementation.
+ // We should not be "actively" connected to anything, but now we're "waking up"
+ // For example, a note which has finished playing, but is now being played again.
+ // Note that if this is considered a worthwhile feature to add, then an evaluation of the locking considerations must be made.
+ }
+}
+
+void AudioNode::deref(RefType refType)
+{
+ // The actually work for deref happens completely within the audio context's graph lock.
+ // In the case of the audio thread, we must use a tryLock to avoid glitches.
+ bool hasLock = false;
+ bool mustReleaseLock = false;
+
+ if (context()->isAudioThread()) {
+ // Real-time audio thread must not contend lock (to avoid glitches).
+ hasLock = context()->tryLock(mustReleaseLock);
+ } else {
+ context()->lock(mustReleaseLock);
+ hasLock = true;
+ }
+
+ if (hasLock) {
+ // This is where the real deref work happens.
+ finishDeref(refType);
+
+ if (mustReleaseLock)
+ context()->unlock();
+ } else {
+ // We were unable to get the lock, so put this in a list to finish up later.
+ ASSERT(context()->isAudioThread());
+ context()->addDeferredFinishDeref(this, refType);
+ }
+
+ // Once AudioContext::uninitialize() is called there's no more chances for deleteMarkedNodes() to get called, so we call here.
+ // We can't call in AudioContext::~AudioContext() since it will never be called as long as any AudioNode is alive
+ // because AudioNodes keep a reference to the context.
+ if (context()->isAudioThreadFinished())
+ context()->deleteMarkedNodes();
+}
+
+void AudioNode::finishDeref(RefType refType)
+{
+ ASSERT(context()->isGraphOwner());
+
+ switch (refType) {
+ case RefTypeNormal:
+ ASSERT(m_normalRefCount > 0);
+ atomicDecrement(&m_normalRefCount);
+ break;
+ case RefTypeConnection:
+ ASSERT(m_connectionRefCount > 0);
+ atomicDecrement(&m_connectionRefCount);
+ break;
+ case RefTypeDisabled:
+ ASSERT(m_disabledRefCount > 0);
+ atomicDecrement(&m_disabledRefCount);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: %d: AudioNode::deref(%d) %d %d %d\n", this, type(), refType, m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+
+ if (!m_connectionRefCount) {
+ if (!m_normalRefCount && !m_disabledRefCount) {
+ if (!m_isMarkedForDeletion) {
+ // All references are gone - we need to go away.
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->disconnectAllInputs(); // this will deref() nodes we're connected to...
+
+ // Mark for deletion at end of each render quantum or when context shuts down.
+ context()->markForDeletion(this);
+ m_isMarkedForDeletion = true;
+ }
+ } else if (refType == RefTypeConnection) {
+ if (!m_isDisabled) {
+ // Still may have JavaScript references, but no more "active" connection references, so put all of our outputs in a "dormant" disabled state.
+ // Garbage collection may take a very long time after this time, so the "dormant" disabled nodes should not bog down the rendering...
+
+ // As far as JavaScript is concerned, our outputs must still appear to be connected.
+ // But internally our outputs should be disabled from the inputs they're connected to.
+ // disable() can recursively deref connections (and call disable()) down a whole chain of connected nodes.
+
+ // FIXME: we special case the convolver and delay since they have a significant tail-time and shouldn't be disconnected simply
+ // because they no longer have any input connections. This needs to be handled more generally where AudioNodes have
+ // a tailTime attribute. Then the AudioNode only needs to remain "active" for tailTime seconds after there are no
+ // longer any active connections.
+ if (type() != NodeTypeConvolver && type() != NodeTypeDelay) {
+ m_isDisabled = true;
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->disable();
+ }
+ }
+ }
+ }
+}
+
+#if DEBUG_AUDIONODE_REFERENCES
+
+bool AudioNode::s_isNodeCountInitialized = false;
+int AudioNode::s_nodeCount[NodeTypeEnd];
+
+void AudioNode::printNodeCounts()
+{
+ printf("\n\n");
+ printf("===========================\n");
+ printf("AudioNode: reference counts\n");
+ printf("===========================\n");
+
+ for (unsigned i = 0; i < NodeTypeEnd; ++i)
+ printf("%d: %d\n", i, s_nodeCount[i]);
+
+ printf("===========================\n\n\n");
+}
+
+#endif // DEBUG_AUDIONODE_REFERENCES
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioNode.h b/Source/WebCore/webaudio/AudioNode.h
new file mode 100644
index 0000000..069407d
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNode.h
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioNode_h
+#define AudioNode_h
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+#define DEBUG_AUDIONODE_REFERENCES 0
+
+namespace WebCore {
+
+class AudioContext;
+class AudioNodeInput;
+class AudioNodeOutput;
+
+// An AudioNode is the basic building block for handling audio within an AudioContext.
+// It may be an audio source, an intermediate processing module, or an audio destination.
+// Each AudioNode can have inputs and/or outputs. An AudioSourceNode has no inputs and a single output.
+// An AudioDestinationNode has one input and no outputs and represents the final destination to the audio hardware.
+// Most processing nodes such as filters will have one input and one output, although multiple inputs and outputs are possible.
+
+class AudioNode {
+public:
+ enum { ProcessingSizeInFrames = 128 };
+
+ AudioNode(AudioContext*, double sampleRate);
+ virtual ~AudioNode();
+
+ AudioContext* context() { return m_context.get(); }
+
+ enum NodeType {
+ NodeTypeUnknown,
+ NodeTypeDestination,
+ NodeTypeAudioBufferSource,
+ NodeTypeJavaScript,
+ NodeTypeLowPass2Filter,
+ NodeTypeHighPass2Filter,
+ NodeTypePanner,
+ NodeTypeConvolver,
+ NodeTypeDelay,
+ NodeTypeGain,
+ NodeTypeChannelSplitter,
+ NodeTypeChannelMerger,
+ NodeTypeAnalyser,
+ NodeTypeEnd
+ };
+
+ NodeType type() const { return m_type; }
+ void setType(NodeType);
+
+ // We handle our own ref-counting because of the threading issues and subtle nature of
+ // how AudioNodes can continue processing (playing one-shot sound) after there are no more
+ // JavaScript references to the object.
+ enum RefType { RefTypeNormal, RefTypeConnection, RefTypeDisabled };
+
+ // Can be called from main thread or context's audio thread.
+ void ref(RefType refType = RefTypeNormal);
+ void deref(RefType refType = RefTypeNormal);
+
+ // Can be called from main thread or context's audio thread. It must be called while the context's graph lock is held.
+ void finishDeref(RefType refType);
+
+ // The AudioNodeInput(s) (if any) will already have their input data available when process() is called.
+ // Subclasses will take this input data and put the results in the AudioBus(s) of its AudioNodeOutput(s) (if any).
+ // Called from context's audio thread.
+ virtual void process(size_t framesToProcess) = 0;
+
+ // Resets DSP processing state (clears delay lines, filter memory, etc.)
+ // Called from context's audio thread.
+ virtual void reset() = 0;
+
+ // No significant resources should be allocated until initialize() is called.
+ // Processing may not occur until a node is initialized.
+ virtual void initialize();
+ virtual void uninitialize();
+
+ bool isInitialized() const { return m_isInitialized; }
+ void lazyInitialize();
+
+ unsigned numberOfInputs() const { return m_inputs.size(); }
+ unsigned numberOfOutputs() const { return m_outputs.size(); }
+
+ AudioNodeInput* input(unsigned);
+ AudioNodeOutput* output(unsigned);
+
+ // connect() / disconnect() return true on success.
+ // Called from main thread by corresponding JavaScript methods.
+ bool connect(AudioNode* destination, unsigned outputIndex = 0, unsigned inputIndex = 0);
+ bool disconnect(unsigned outputIndex = 0);
+
+ double sampleRate() const { return m_sampleRate; }
+
+ // processIfNecessary() is called by our output(s) when the rendering graph needs this AudioNode to process.
+ // This method ensures that the AudioNode will only process once per rendering time quantum even if it's called repeatedly.
+ // This handles the case of "fanout" where an output is connected to multiple AudioNode inputs.
+ // Called from context's audio thread.
+ void processIfNecessary(size_t framesToProcess);
+
+ // Called when a new connection has been made to one of our inputs or the connection number of channels has changed.
+ // This potentially gives us enough information to perform a lazy initialization or, if necessary, a re-initialization.
+ // Called from main thread.
+ virtual void checkNumberOfChannelsForInput(AudioNodeInput*) { }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ static void printNodeCounts();
+#endif
+
+ bool isMarkedForDeletion() const { return m_isMarkedForDeletion; }
+
+protected:
+ // Inputs and outputs must be created before the AudioNode is initialized.
+ void addInput(PassOwnPtr<AudioNodeInput>);
+ void addOutput(PassOwnPtr<AudioNodeOutput>);
+
+ // Called by processIfNecessary() to cause all parts of the rendering graph connected to us to process.
+ // Each rendering quantum, the audio data for each of the AudioNode's inputs will be available after this method is called.
+ // Called from context's audio thread.
+ virtual void pullInputs(size_t framesToProcess);
+
+private:
+ volatile bool m_isInitialized;
+ NodeType m_type;
+ RefPtr<AudioContext> m_context;
+ double m_sampleRate;
+ Vector<OwnPtr<AudioNodeInput> > m_inputs;
+ Vector<OwnPtr<AudioNodeOutput> > m_outputs;
+
+ double m_lastProcessingTime;
+
+ // Ref-counting
+ volatile int m_normalRefCount;
+ volatile int m_connectionRefCount;
+ volatile int m_disabledRefCount;
+
+ bool m_isMarkedForDeletion;
+ bool m_isDisabled;
+
+#if DEBUG_AUDIONODE_REFERENCES
+ static bool s_isNodeCountInitialized;
+ static int s_nodeCount[NodeTypeEnd];
+#endif
+};
+
+} // namespace WebCore
+
+#endif // AudioNode_h
diff --git a/Source/WebCore/webaudio/AudioNode.idl b/Source/WebCore/webaudio/AudioNode.idl
new file mode 100644
index 0000000..8d903e2
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNode.idl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioNode {
+ readonly attribute AudioContext context;
+ readonly attribute unsigned long numberOfInputs;
+ readonly attribute unsigned long numberOfOutputs;
+
+ [Custom] void connect(in AudioNode destination, in unsigned long output, in unsigned long input)
+ raises(DOMException);
+
+ [Custom] void disconnect(in unsigned long output)
+ raises(DOMException);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioNodeInput.cpp b/Source/WebCore/webaudio/AudioNodeInput.cpp
new file mode 100644
index 0000000..9fd1852
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNodeInput.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "AudioNodeInput.h"
+
+#include "AudioContext.h"
+#include "AudioNode.h"
+#include "AudioNodeOutput.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace WebCore {
+
+AudioNodeInput::AudioNodeInput(AudioNode* node)
+ : m_node(node)
+ , m_renderingStateNeedUpdating(false)
+{
+ m_monoSummingBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
+ m_stereoSummingBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
+}
+
+void AudioNodeInput::connect(AudioNodeOutput* output)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(output && node());
+ if (!output || !node())
+ return;
+
+ // Check if we're already connected to this output.
+ if (m_outputs.contains(output))
+ return;
+
+ output->addInput(this);
+ m_outputs.add(output);
+ changedOutputs();
+
+ // Sombody has just connected to us, so count it as a reference.
+ node()->ref(AudioNode::RefTypeConnection);
+}
+
+void AudioNodeInput::disconnect(AudioNodeOutput* output)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(output && node());
+ if (!output || !node())
+ return;
+
+ // First try to disconnect from "active" connections.
+ if (m_outputs.contains(output)) {
+ m_outputs.remove(output);
+ changedOutputs();
+ output->removeInput(this);
+ node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
+ return;
+ }
+
+ // Otherwise, try to disconnect from disabled connections.
+ if (m_disabledOutputs.contains(output)) {
+ m_disabledOutputs.remove(output);
+ output->removeInput(this);
+ node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void AudioNodeInput::disable(AudioNodeOutput* output)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(output && node());
+ if (!output || !node())
+ return;
+
+ ASSERT(m_outputs.contains(output));
+
+ m_disabledOutputs.add(output);
+ m_outputs.remove(output);
+ changedOutputs();
+
+ node()->ref(AudioNode::RefTypeDisabled);
+ node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
+}
+
+void AudioNodeInput::enable(AudioNodeOutput* output)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(output && node());
+ if (!output || !node())
+ return;
+
+ ASSERT(m_disabledOutputs.contains(output));
+
+ // Move output from disabled list to active list.
+ m_outputs.add(output);
+ m_disabledOutputs.remove(output);
+ changedOutputs();
+
+ node()->ref(AudioNode::RefTypeConnection);
+ node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
+}
+
+void AudioNodeInput::changedOutputs()
+{
+ ASSERT(context()->isGraphOwner());
+ if (!m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) {
+ context()->markAudioNodeInputDirty(this);
+ m_renderingStateNeedUpdating = true;
+ }
+}
+
+void AudioNodeInput::updateRenderingState()
+{
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+
+ if (m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) {
+ // Copy from m_outputs to m_renderingOutputs.
+ m_renderingOutputs.resize(m_outputs.size());
+ unsigned j = 0;
+ for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) {
+ AudioNodeOutput* output = *i;
+ m_renderingOutputs[j] = output;
+ output->updateRenderingState();
+ }
+
+ node()->checkNumberOfChannelsForInput(this);
+
+ m_renderingStateNeedUpdating = false;
+ }
+}
+
+unsigned AudioNodeInput::numberOfChannels() const
+{
+ // Find the number of channels of the connection with the largest number of channels.
+ unsigned maxChannels = 1; // one channel is the minimum allowed
+
+ for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
+ AudioNodeOutput* output = *i;
+ maxChannels = max(maxChannels, output->bus()->numberOfChannels());
+ }
+
+ return maxChannels;
+}
+
+unsigned AudioNodeInput::numberOfRenderingChannels()
+{
+ ASSERT(context()->isAudioThread());
+
+ // Find the number of channels of the rendering connection with the largest number of channels.
+ unsigned maxChannels = 1; // one channel is the minimum allowed
+
+ for (unsigned i = 0; i < numberOfRenderingConnections(); ++i)
+ maxChannels = max(maxChannels, renderingOutput(i)->bus()->numberOfChannels());
+
+ return maxChannels;
+}
+
+AudioBus* AudioNodeInput::bus()
+{
+ ASSERT(context()->isAudioThread());
+
+ // Handle single connection specially to allow for in-place processing.
+ if (numberOfRenderingConnections() == 1)
+ return renderingOutput(0)->bus();
+
+ // Multiple connections case (or no connections).
+ return internalSummingBus();
+}
+
+AudioBus* AudioNodeInput::internalSummingBus()
+{
+ ASSERT(context()->isAudioThread());
+
+ // We must pick a summing bus which is the right size to handle the largest connection.
+ switch (numberOfRenderingChannels()) {
+ case 1:
+ return m_monoSummingBus.get();
+ case 2:
+ return m_stereoSummingBus.get();
+ // FIXME: could implement more than just mono and stereo mixing in the future
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ // We shouldn't be calling this method if there's only one connection, since it's less efficient.
+ ASSERT(numberOfRenderingConnections() > 1);
+
+ ASSERT(summingBus);
+ if (!summingBus)
+ return;
+
+ summingBus->zero();
+
+ for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
+ AudioNodeOutput* output = renderingOutput(i);
+ ASSERT(output);
+
+ // Render audio from this output.
+ AudioBus* connectionBus = output->pull(0, framesToProcess);
+
+ // Sum, with unity-gain.
+ summingBus->sumFrom(*connectionBus);
+ }
+}
+
+AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ // Handle single connection case.
+ if (numberOfRenderingConnections() == 1) {
+ // The output will optimize processing using inPlaceBus if it's able.
+ AudioNodeOutput* output = this->renderingOutput(0);
+ return output->pull(inPlaceBus, framesToProcess);
+ }
+
+ AudioBus* internalSummingBus = this->internalSummingBus();
+
+ if (!numberOfRenderingConnections()) {
+ // At least, generate silence if we're not connected to anything.
+ // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing.
+ internalSummingBus->zero();
+ return internalSummingBus;
+ }
+
+ // Handle multiple connections case.
+ sumAllConnections(internalSummingBus, framesToProcess);
+
+ return internalSummingBus;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioNodeInput.h b/Source/WebCore/webaudio/AudioNodeInput.h
new file mode 100644
index 0000000..1d90986
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNodeInput.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioNodeInput_h
+#define AudioNodeInput_h
+
+#include "AudioBus.h"
+#include "AudioNode.h"
+#include <wtf/HashSet.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioNode;
+class AudioNodeOutput;
+
+// An AudioNodeInput represents an input to an AudioNode and can be connected from one or more AudioNodeOutputs.
+// In the case of multiple connections, the input will act as a unity-gain summing junction, mixing all the outputs.
+// The number of channels of the input's bus is the maximum of the number of channels of all its connections.
+
+class AudioNodeInput {
+public:
+ AudioNodeInput(AudioNode*);
+
+ // Can be called from any thread.
+ AudioNode* node() const { return m_node; }
+ AudioContext* context() { return m_node->context(); }
+
+ // Must be called with the context's graph lock.
+ void connect(AudioNodeOutput*);
+ void disconnect(AudioNodeOutput*);
+
+ // disable() will take the output out of the active connections list and set aside in a disabled list.
+ // enable() will put the output back into the active connections list.
+ // Must be called with the context's graph lock.
+ void enable(AudioNodeOutput*);
+ void disable(AudioNodeOutput*);
+
+ // pull() processes all of the AudioNodes connected to us.
+ // In the case of multiple connections it sums the result into an internal summing bus.
+ // In the single connection case, it allows in-place processing where possible using inPlaceBus.
+ // It returns the bus which it rendered into, returning inPlaceBus if in-place processing was performed.
+ // Called from context's audio thread.
+ AudioBus* pull(AudioBus* inPlaceBus, size_t framesToProcess);
+
+ // bus() contains the rendered audio after pull() has been called for each time quantum.
+ // Called from context's audio thread.
+ AudioBus* bus();
+
+ // This copies m_outputs to m_renderingOutputs. Please see comments for these lists below.
+ // This must be called when we own the context's graph lock in the audio thread at the very start or end of the render quantum.
+ void updateRenderingState();
+
+ // Rendering code accesses its version of the current connections here.
+ unsigned numberOfRenderingConnections() const { return m_renderingOutputs.size(); }
+ AudioNodeOutput* renderingOutput(unsigned i) { return m_renderingOutputs[i]; }
+ const AudioNodeOutput* renderingOutput(unsigned i) const { return m_renderingOutputs[i]; }
+ bool isConnected() const { return numberOfRenderingConnections() > 0; }
+
+ // The number of channels of the connection with the largest number of channels.
+ unsigned numberOfChannels() const;
+
+private:
+ AudioNode* m_node;
+
+ // m_outputs contains the AudioNodeOutputs representing current connections which are not disabled.
+ // The rendering code should never use this directly, but instead uses m_renderingOutputs.
+ HashSet<AudioNodeOutput*> m_outputs;
+
+ // numberOfConnections() should never be called from the audio rendering thread.
+ // Instead numberOfRenderingConnections() and renderingOutput() should be used.
+ unsigned numberOfConnections() const { return m_outputs.size(); }
+
+ // This must be called whenever we modify m_outputs.
+ void changedOutputs();
+
+ // m_renderingOutputs is a copy of m_outputs which will never be modified during the graph rendering on the audio thread.
+ // This is the list which is used by the rendering code.
+ // Whenever m_outputs is modified, the context is told so it can later update m_renderingOutputs from m_outputs at a safe time.
+ // Most of the time, m_renderingOutputs is identical to m_outputs.
+ Vector<AudioNodeOutput*> m_renderingOutputs;
+
+ // m_renderingStateNeedUpdating keeps track if m_outputs is modified.
+ bool m_renderingStateNeedUpdating;
+
+ // The number of channels of the rendering connection with the largest number of channels.
+ unsigned numberOfRenderingChannels();
+
+ // m_disabledOutputs contains the AudioNodeOutputs which are disabled (will not be processed) by the audio graph rendering.
+ // But, from JavaScript's perspective, these outputs are still connected to us.
+ // Generally, these represent disabled connections from "notes" which have finished playing but are not yet garbage collected.
+ HashSet<AudioNodeOutput*> m_disabledOutputs;
+
+ // Called from context's audio thread.
+ AudioBus* internalSummingBus();
+ void sumAllConnections(AudioBus* summingBus, size_t framesToProcess);
+
+ OwnPtr<AudioBus> m_monoSummingBus;
+ OwnPtr<AudioBus> m_stereoSummingBus;
+};
+
+} // namespace WebCore
+
+#endif // AudioNodeInput_h
diff --git a/Source/WebCore/webaudio/AudioNodeOutput.cpp b/Source/WebCore/webaudio/AudioNodeOutput.cpp
new file mode 100644
index 0000000..4c777e6
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNodeOutput.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "AudioNodeOutput.h"
+
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+AudioNodeOutput::AudioNodeOutput(AudioNode* node, unsigned numberOfChannels)
+ : m_node(node)
+ , m_numberOfChannels(numberOfChannels)
+ , m_desiredNumberOfChannels(numberOfChannels)
+ , m_internalOutputBus(0)
+ , m_actualDestinationBus(0)
+ , m_isEnabled(true)
+ , m_renderingFanOutCount(0)
+{
+ m_monoInternalBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
+ m_stereoInternalBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
+ setInternalBus();
+}
+
+void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels)
+{
+ ASSERT(context()->isGraphOwner());
+
+ m_desiredNumberOfChannels = numberOfChannels;
+
+ if (context()->isAudioThread()) {
+ // If we're in the audio thread then we can take care of it right away (we should be at the very start or end of a rendering quantum).
+ updateNumberOfChannels();
+ } else {
+ // Let the context take care of it in the audio thread in the pre and post render tasks.
+ context()->markAudioNodeOutputDirty(this);
+ }
+}
+
+void AudioNodeOutput::setInternalBus()
+{
+ switch (m_numberOfChannels) {
+ case 0:
+ case 1:
+ m_internalOutputBus = m_monoInternalBus.get();
+ break;
+ case 2:
+ m_internalOutputBus = m_stereoInternalBus.get();
+ break;
+ default:
+ // FIXME: later we can fully implement more than stereo, 5.1, etc.
+ ASSERT_NOT_REACHED();
+ }
+
+ // This may later be changed in pull() to point to an in-place bus with the same number of channels.
+ m_actualDestinationBus = m_internalOutputBus;
+}
+
+void AudioNodeOutput::updateRenderingState()
+{
+ updateNumberOfChannels();
+ m_renderingFanOutCount = fanOutCount();
+}
+
+void AudioNodeOutput::updateNumberOfChannels()
+{
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+
+ if (m_numberOfChannels != m_desiredNumberOfChannels) {
+ m_numberOfChannels = m_desiredNumberOfChannels;
+ setInternalBus();
+ propagateChannelCount();
+ }
+}
+
+void AudioNodeOutput::propagateChannelCount()
+{
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+
+ if (isChannelCountKnown()) {
+ // Announce to any nodes we're connected to that we changed our channel count for its input.
+ for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
+ AudioNodeInput* input = *i;
+ AudioNode* connectionNode = input->node();
+ connectionNode->checkNumberOfChannelsForInput(input);
+ }
+ }
+}
+
+AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+ ASSERT(m_renderingFanOutCount > 0);
+
+ // Causes our AudioNode to process if it hasn't already for this render quantum.
+ // We try to do in-place processing (using inPlaceBus) if at all possible,
+ // but we can't process in-place if we're connected to more than one input (fan-out > 1).
+ // In this case pull() is called multiple times per rendering quantum, and the processIfNecessary() call below will
+ // cause our node to process() only the first time, caching the output in m_internalOutputBus for subsequent calls.
+
+ bool isInPlace = inPlaceBus && inPlaceBus->numberOfChannels() == numberOfChannels() && m_renderingFanOutCount == 1;
+
+ // Setup the actual destination bus for processing when our node's process() method gets called in processIfNecessary() below.
+ m_actualDestinationBus = isInPlace ? inPlaceBus : m_internalOutputBus;
+
+ node()->processIfNecessary(framesToProcess);
+ return m_actualDestinationBus;
+}
+
+AudioBus* AudioNodeOutput::bus() const
+{
+ ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread());
+ ASSERT(m_actualDestinationBus);
+ return m_actualDestinationBus;
+}
+
+unsigned AudioNodeOutput::renderingFanOutCount() const
+{
+ return m_renderingFanOutCount;
+}
+
+unsigned AudioNodeOutput::fanOutCount()
+{
+ ASSERT(context()->isGraphOwner());
+ return m_inputs.size();
+}
+
+void AudioNodeOutput::addInput(AudioNodeInput* input)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(input);
+ if (!input)
+ return;
+
+ m_inputs.add(input);
+}
+
+void AudioNodeOutput::removeInput(AudioNodeInput* input)
+{
+ ASSERT(context()->isGraphOwner());
+
+ ASSERT(input);
+ if (!input)
+ return;
+
+ m_inputs.remove(input);
+}
+
+void AudioNodeOutput::disconnectAllInputs()
+{
+ ASSERT(context()->isGraphOwner());
+
+ // AudioNodeInput::disconnect() changes m_inputs by calling removeInput().
+ while (!m_inputs.isEmpty()) {
+ AudioNodeInput* input = *m_inputs.begin();
+ input->disconnect(this);
+ }
+}
+
+void AudioNodeOutput::disable()
+{
+ ASSERT(context()->isGraphOwner());
+
+ if (m_isEnabled) {
+ for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
+ AudioNodeInput* input = *i;
+ input->disable(this);
+ }
+ m_isEnabled = false;
+ }
+}
+
+void AudioNodeOutput::enable()
+{
+ ASSERT(context()->isGraphOwner());
+
+ if (!m_isEnabled) {
+ for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
+ AudioNodeInput* input = *i;
+ input->enable(this);
+ }
+ m_isEnabled = true;
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioNodeOutput.h b/Source/WebCore/webaudio/AudioNodeOutput.h
new file mode 100644
index 0000000..7114b38
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNodeOutput.h
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioNodeOutput_h
+#define AudioNodeOutput_h
+
+#include "AudioBus.h"
+#include "AudioNode.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioContext;
+class AudioNodeInput;
+
+// AudioNodeOutput represents a single output for an AudioNode.
+// It may be connected to one or more AudioNodeInputs.
+
+class AudioNodeOutput {
+public:
+ // It's OK to pass 0 for numberOfChannels in which case setNumberOfChannels() must be called later on.
+ AudioNodeOutput(AudioNode*, unsigned numberOfChannels);
+
+ // Can be called from any thread.
+ AudioNode* node() const { return m_node; }
+ AudioContext* context() { return m_node->context(); }
+
+ // Causes our AudioNode to process if it hasn't already for this render quantum.
+ // It returns the bus containing the processed audio for this output, returning inPlaceBus if in-place processing was possible.
+ // Called from context's audio thread.
+ AudioBus* pull(AudioBus* inPlaceBus, size_t framesToProcess);
+
+ // bus() will contain the rendered audio after pull() is called for each rendering time quantum.
+ // Called from context's audio thread.
+ AudioBus* bus() const;
+
+ // fanOutCount() is the number of AudioNodeInputs that we're connected to.
+ // This function should not be called in audio thread rendering code, instead renderingFanOutCount() should be used.
+ // It must be called with the context's graph lock.
+ unsigned fanOutCount();
+
+ // renderingFanOutCount() is the number of AudioNodeInputs that we're connected to during rendering.
+ // Unlike fanOutCount() it will not change during the course of a render quantum.
+ unsigned renderingFanOutCount() const;
+
+ // It must be called with the context's graph lock.
+ void disconnectAllInputs();
+
+ void setNumberOfChannels(unsigned);
+ unsigned numberOfChannels() const { return m_numberOfChannels; }
+ bool isChannelCountKnown() const { return numberOfChannels() > 0; }
+
+ // Disable/Enable happens when there are still JavaScript references to a node, but it has otherwise "finished" its work.
+ // For example, when a note has finished playing. It is kept around, because it may be played again at a later time.
+ // They must be called with the context's graph lock.
+ void disable();
+ void enable();
+
+ // updateRenderingState() is called in the audio thread at the start or end of the render quantum to handle any recent changes to the graph state.
+ // It must be called with the context's graph lock.
+ void updateRenderingState();
+
+private:
+ AudioNode* m_node;
+
+ friend class AudioNodeInput;
+
+ // These are called from AudioNodeInput.
+ // They must be called with the context's graph lock.
+ void addInput(AudioNodeInput*);
+ void removeInput(AudioNodeInput*);
+
+ // setInternalBus() sets m_internalOutputBus appropriately for the number of channels.
+ // It is called in the constructor or in the audio thread with the context's graph lock.
+ void setInternalBus();
+
+ // Announce to any nodes we're connected to that we changed our channel count for its input.
+ // It must be called in the audio thread with the context's graph lock.
+ void propagateChannelCount();
+
+ // updateNumberOfChannels() is called in the audio thread at the start or end of the render quantum to pick up channel changes.
+ // It must be called with the context's graph lock.
+ void updateNumberOfChannels();
+
+ // m_numberOfChannels will only be changed in the audio thread.
+ // The main thread sets m_desiredNumberOfChannels which will later get picked up in the audio thread in updateNumberOfChannels().
+ unsigned m_numberOfChannels;
+ unsigned m_desiredNumberOfChannels;
+
+ // m_internalOutputBus will point to either m_monoInternalBus or m_stereoInternalBus.
+ // It must only be changed in the audio thread (or constructor).
+ AudioBus* m_internalOutputBus;
+ OwnPtr<AudioBus> m_monoInternalBus;
+ OwnPtr<AudioBus> m_stereoInternalBus;
+
+ // m_actualDestinationBus is set in pull() and will either point to one of our internal busses or to the in-place bus.
+ // It must only be changed in the audio thread (or constructor).
+ AudioBus* m_actualDestinationBus;
+
+ HashSet<AudioNodeInput*> m_inputs;
+ typedef HashSet<AudioNodeInput*>::iterator InputsIterator;
+ bool m_isEnabled;
+
+ // For the purposes of rendering, keeps track of the number of inputs we're connected to.
+ // This value should only be changed at the very start or end of the rendering quantum.
+ unsigned m_renderingFanOutCount;
+};
+
+} // namespace WebCore
+
+#endif // AudioNodeOutput_h
diff --git a/Source/WebCore/webaudio/AudioPannerNode.cpp b/Source/WebCore/webaudio/AudioPannerNode.cpp
new file mode 100644
index 0000000..5c94763
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioPannerNode.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 "AudioPannerNode.h"
+
+#include "AudioBufferSourceNode.h"
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "HRTFPanner.h"
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static void fixNANs(double &x)
+{
+ if (isnan(x) || isinf(x))
+ x = 0.0;
+}
+
+AudioPannerNode::AudioPannerNode(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+ , m_panningModel(Panner::PanningModelHRTF)
+ , m_lastGain(-1.0)
+ , m_connectionCount(0)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ m_distanceGain = AudioGain::create("distanceGain", 1.0, 0.0, 1.0);
+ m_coneGain = AudioGain::create("coneGain", 1.0, 0.0, 1.0);
+
+ m_position = FloatPoint3D(0, 0, 0);
+ m_orientation = FloatPoint3D(1, 0, 0);
+ m_velocity = FloatPoint3D(0, 0, 0);
+
+ setType(NodeTypePanner);
+
+ initialize();
+}
+
+AudioPannerNode::~AudioPannerNode()
+{
+ uninitialize();
+}
+
+void AudioPannerNode::pullInputs(size_t framesToProcess)
+{
+ // We override pullInputs(), so we can detect new AudioSourceNodes which have connected to us when new connections are made.
+ // These AudioSourceNodes need to be made aware of our existence in order to handle doppler shift pitch changes.
+ if (m_connectionCount != context()->connectionCount()) {
+ m_connectionCount = context()->connectionCount();
+
+ // Recursively go through all nodes connected to us.
+ notifyAudioSourcesConnectedToNode(this);
+ }
+
+ AudioNode::pullInputs(framesToProcess);
+}
+
+void AudioPannerNode::process(size_t framesToProcess)
+{
+ AudioBus* destination = output(0)->bus();
+
+ if (!isInitialized() || !input(0)->isConnected() || !m_panner.get()) {
+ destination->zero();
+ return;
+ }
+
+ AudioBus* source = input(0)->bus();
+
+ if (!source) {
+ destination->zero();
+ return;
+ }
+
+ // Apply the panning effect.
+ double azimuth;
+ double elevation;
+ getAzimuthElevation(&azimuth, &elevation);
+ m_panner->pan(azimuth, elevation, source, destination, framesToProcess);
+
+ // Get the distance and cone gain.
+ double totalGain = distanceConeGain();
+
+ // Snap to desired gain at the beginning.
+ if (m_lastGain == -1.0)
+ m_lastGain = totalGain;
+
+ // Apply gain in-place with de-zippering.
+ destination->copyWithGainFrom(*destination, &m_lastGain, totalGain);
+}
+
+void AudioPannerNode::reset()
+{
+ m_lastGain = -1.0; // force to snap to initial gain
+ if (m_panner.get())
+ m_panner->reset();
+}
+
+void AudioPannerNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ m_panner = Panner::create(m_panningModel, sampleRate());
+
+ AudioNode::initialize();
+}
+
+void AudioPannerNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_panner.clear();
+ AudioNode::uninitialize();
+}
+
+AudioListener* AudioPannerNode::listener()
+{
+ return context()->listener();
+}
+
+void AudioPannerNode::setPanningModel(unsigned short model)
+{
+ if (!m_panner.get() || model != m_panningModel) {
+ OwnPtr<Panner> newPanner = Panner::create(model, sampleRate());
+ m_panner = newPanner.release();
+ }
+}
+
+void AudioPannerNode::getAzimuthElevation(double* outAzimuth, double* outElevation)
+{
+ // FIXME: we should cache azimuth and elevation (if possible), so we only re-calculate if a change has been made.
+
+ double azimuth = 0.0;
+
+ // Calculate the source-listener vector
+ FloatPoint3D listenerPosition = listener()->position();
+ FloatPoint3D sourceListener = m_position - listenerPosition;
+
+ if (sourceListener.isZero()) {
+ // degenerate case if source and listener are at the same point
+ *outAzimuth = 0.0;
+ *outElevation = 0.0;
+ return;
+ }
+
+ sourceListener.normalize();
+
+ // Align axes
+ FloatPoint3D listenerFront = listener()->orientation();
+ FloatPoint3D listenerUp = listener()->upVector();
+ FloatPoint3D listenerRight = listenerFront.cross(listenerUp);
+ listenerRight.normalize();
+
+ FloatPoint3D listenerFrontNorm = listenerFront;
+ listenerFrontNorm.normalize();
+
+ FloatPoint3D up = listenerRight.cross(listenerFrontNorm);
+
+ double upProjection = sourceListener.dot(up);
+
+ FloatPoint3D projectedSource = sourceListener - upProjection * up;
+ projectedSource.normalize();
+
+ azimuth = 180.0 * acos(projectedSource.dot(listenerRight)) / piDouble;
+ fixNANs(azimuth); // avoid illegal values
+
+ // Source in front or behind the listener
+ double frontBack = projectedSource.dot(listenerFrontNorm);
+ if (frontBack < 0.0)
+ azimuth = 360.0 - azimuth;
+
+ // Make azimuth relative to "front" and not "right" listener vector
+ if ((azimuth >= 0.0) && (azimuth <= 270.0))
+ azimuth = 90.0 - azimuth;
+ else
+ azimuth = 450.0 - azimuth;
+
+ // Elevation
+ double elevation = 90.0 - 180.0 * acos(sourceListener.dot(up)) / piDouble;
+ fixNANs(azimuth); // avoid illegal values
+
+ if (elevation > 90.0)
+ elevation = 180.0 - elevation;
+ else if (elevation < -90.0)
+ elevation = -180.0 - elevation;
+
+ if (outAzimuth)
+ *outAzimuth = azimuth;
+ if (outElevation)
+ *outElevation = elevation;
+}
+
+float AudioPannerNode::dopplerRate()
+{
+ double dopplerShift = 1.0;
+
+ // FIXME: optimize for case when neither source nor listener has changed...
+ double dopplerFactor = listener()->dopplerFactor();
+
+ if (dopplerFactor > 0.0) {
+ double speedOfSound = listener()->speedOfSound();
+
+ const FloatPoint3D &sourceVelocity = m_velocity;
+ const FloatPoint3D &listenerVelocity = listener()->velocity();
+
+ // Don't bother if both source and listener have no velocity
+ bool sourceHasVelocity = !sourceVelocity.isZero();
+ bool listenerHasVelocity = !listenerVelocity.isZero();
+
+ if (sourceHasVelocity || listenerHasVelocity) {
+ // Calculate the source to listener vector
+ FloatPoint3D listenerPosition = listener()->position();
+ FloatPoint3D sourceToListener = m_position - listenerPosition;
+
+ double sourceListenerMagnitude = sourceToListener.length();
+
+ double listenerProjection = sourceToListener.dot(listenerVelocity) / sourceListenerMagnitude;
+ double sourceProjection = sourceToListener.dot(sourceVelocity) / sourceListenerMagnitude;
+
+ listenerProjection = -listenerProjection;
+ sourceProjection = -sourceProjection;
+
+ double scaledSpeedOfSound = speedOfSound / dopplerFactor;
+ listenerProjection = min(listenerProjection, scaledSpeedOfSound);
+ sourceProjection = min(sourceProjection, scaledSpeedOfSound);
+
+ dopplerShift = ((speedOfSound - dopplerFactor * listenerProjection) / (speedOfSound - dopplerFactor * sourceProjection));
+ fixNANs(dopplerShift); // avoid illegal values
+
+ // Limit the pitch shifting to 4 octaves up and 3 octaves down.
+ if (dopplerShift > 16.0)
+ dopplerShift = 16.0;
+ else if (dopplerShift < 0.125)
+ dopplerShift = 0.125;
+ }
+ }
+
+ return static_cast<float>(dopplerShift);
+}
+
+float AudioPannerNode::distanceConeGain()
+{
+ FloatPoint3D listenerPosition = listener()->position();
+
+ double listenerDistance = m_position.distanceTo(listenerPosition);
+ double distanceGain = m_distanceEffect.gain(listenerDistance);
+
+ m_distanceGain->setValue(static_cast<float>(distanceGain));
+
+ // FIXME: could optimize by caching coneGain
+ double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosition);
+
+ m_coneGain->setValue(static_cast<float>(coneGain));
+
+ return float(distanceGain * coneGain);
+}
+
+void AudioPannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node)
+{
+ ASSERT(node);
+ if (!node)
+ return;
+
+ // First check if this node is an AudioBufferSourceNode. If so, let it know about us so that doppler shift pitch can be taken into account.
+ if (node->type() == NodeTypeAudioBufferSource) {
+ AudioBufferSourceNode* bufferSourceNode = reinterpret_cast<AudioBufferSourceNode*>(node);
+ bufferSourceNode->setPannerNode(this);
+ } else {
+ // Go through all inputs to this node.
+ for (unsigned i = 0; i < node->numberOfInputs(); ++i) {
+ AudioNodeInput* input = node->input(i);
+
+ // For each input, go through all of its connections, looking for AudioBufferSourceNodes.
+ for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) {
+ AudioNodeOutput* connectedOutput = input->renderingOutput(j);
+ AudioNode* connectedNode = connectedOutput->node();
+ notifyAudioSourcesConnectedToNode(connectedNode); // recurse
+ }
+ }
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioPannerNode.h b/Source/WebCore/webaudio/AudioPannerNode.h
new file mode 100644
index 0000000..61e34a9
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioPannerNode.h
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioPannerNode_h
+#define AudioPannerNode_h
+
+#include "AudioBus.h"
+#include "AudioGain.h"
+#include "AudioListener.h"
+#include "AudioNode.h"
+#include "Cone.h"
+#include "Distance.h"
+#include "FloatPoint3D.h"
+#include "Panner.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+// AudioPannerNode is an AudioNode with one input and one output.
+// It positions a sound in 3D space, with the exact effect dependent on the panning model.
+// It has a position and an orientation in 3D space which is relative to the position and orientation of the context's AudioListener.
+// A distance effect will attenuate the gain as the position moves away from the listener.
+// A cone effect will attenuate the gain as the orientation moves away from the listener.
+// All of these effects follow the OpenAL specification very closely.
+
+class AudioPannerNode : public AudioNode {
+public:
+ // These must be defined as in the .idl file and must match those in the Panner class.
+ enum {
+ EQUALPOWER = 0,
+ HRTF = 1,
+ SOUNDFIELD = 2,
+ };
+
+ static PassRefPtr<AudioPannerNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new AudioPannerNode(context, sampleRate));
+ }
+
+ virtual ~AudioPannerNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void pullInputs(size_t framesToProcess);
+ virtual void reset();
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // Listener
+ AudioListener* listener();
+
+ // Panning model
+ unsigned short panningModel() const { return m_panningModel; }
+ void setPanningModel(unsigned short);
+
+ // Position
+ FloatPoint3D position() const { return m_position; }
+ void setPosition(float x, float y, float z) { m_position = FloatPoint3D(x, y, z); }
+
+ // Orientation
+ FloatPoint3D orientation() const { return m_position; }
+ void setOrientation(float x, float y, float z) { m_orientation = FloatPoint3D(x, y, z); }
+
+ // Velocity
+ FloatPoint3D velocity() const { return m_velocity; }
+ void setVelocity(float x, float y, float z) { m_velocity = FloatPoint3D(x, y, z); }
+
+ // Distance parameters
+ unsigned short distanceModel() { return m_distanceEffect.model(); }
+ void setDistanceModel(unsigned short model) { m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(model), true); }
+
+ float refDistance() { return static_cast<float>(m_distanceEffect.refDistance()); }
+ void setRefDistance(float refDistance) { m_distanceEffect.setRefDistance(refDistance); }
+
+ float maxDistance() { return static_cast<float>(m_distanceEffect.maxDistance()); }
+ void setMaxDistance(float maxDistance) { m_distanceEffect.setMaxDistance(maxDistance); }
+
+ float rolloffFactor() { return static_cast<float>(m_distanceEffect.rolloffFactor()); }
+ void setRolloffFactor(float rolloffFactor) { m_distanceEffect.setRolloffFactor(rolloffFactor); }
+
+ // Sound cones - angles in degrees
+ float coneInnerAngle() const { return static_cast<float>(m_coneEffect.innerAngle()); }
+ void setConeInnerAngle(float angle) { m_coneEffect.setInnerAngle(angle); }
+
+ float coneOuterAngle() const { return static_cast<float>(m_coneEffect.outerAngle()); }
+ void setConeOuterAngle(float angle) { m_coneEffect.setOuterAngle(angle); }
+
+ float coneOuterGain() const { return static_cast<float>(m_coneEffect.outerGain()); }
+ void setConeOuterGain(float angle) { m_coneEffect.setOuterGain(angle); }
+
+ void getAzimuthElevation(double* outAzimuth, double* outElevation);
+ float dopplerRate();
+
+ // Accessors for dynamically calculated gain values.
+ AudioGain* distanceGain() { return m_distanceGain.get(); }
+ AudioGain* coneGain() { return m_coneGain.get(); }
+
+private:
+ AudioPannerNode(AudioContext*, double sampleRate);
+
+ // Returns the combined distance and cone gain attenuation.
+ float distanceConeGain();
+
+ // Notifies any AudioBufferSourceNodes connected to us either directly or indirectly about our existence.
+ // This is in order to handle the pitch change necessary for the doppler shift.
+ void notifyAudioSourcesConnectedToNode(AudioNode*);
+
+ OwnPtr<Panner> m_panner;
+ unsigned m_panningModel;
+
+ FloatPoint3D m_position;
+ FloatPoint3D m_orientation;
+ FloatPoint3D m_velocity;
+
+ // Gain
+ RefPtr<AudioGain> m_distanceGain;
+ RefPtr<AudioGain> m_coneGain;
+ DistanceEffect m_distanceEffect;
+ ConeEffect m_coneEffect;
+ double m_lastGain;
+
+ unsigned m_connectionCount;
+};
+
+} // namespace WebCore
+
+#endif // AudioPannerNode_h
diff --git a/Source/WebCore/webaudio/AudioPannerNode.idl b/Source/WebCore/webaudio/AudioPannerNode.idl
new file mode 100644
index 0000000..2db093d
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioPannerNode.idl
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateConstructor,
+ GenerateToJS
+ ] AudioPannerNode : AudioNode {
+ // Panning model
+ const unsigned short EQUALPOWER = 0;
+ const unsigned short HRTF = 1;
+ const unsigned short SOUNDFIELD = 2;
+
+ // Default model for stereo is HRTF
+ attribute unsigned long panningModel; // FIXME: use unsigned short when glue generation supports it
+
+ // Uses a 3D cartesian coordinate system
+ void setPosition(in float x, in float y, in float z);
+ void setOrientation(in float x, in float y, in float z);
+ void setVelocity(in float x, in float y, in float z);
+
+ // Distance model
+ attribute unsigned long distanceModel; // FIXME: use unsigned short when glue generation supports it
+ attribute float refDistance;
+ attribute float maxDistance;
+ attribute float rolloffFactor;
+
+ // Directional sound cone
+ attribute float coneInnerAngle;
+ attribute float coneOuterAngle;
+ attribute float coneOuterGain;
+
+ // Dynamically calculated gain values
+ readonly attribute AudioGain coneGain;
+ readonly attribute AudioGain distanceGain;
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioParam.cpp b/Source/WebCore/webaudio/AudioParam.cpp
new file mode 100644
index 0000000..dcf918f
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 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 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 "AudioParam.h"
+
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+const double AudioParam::DefaultSmoothingConstant = 0.05;
+const double AudioParam::SnapThreshold = 0.001;
+
+void AudioParam::setValue(float value)
+{
+ // Check against JavaScript giving us bogus floating-point values.
+ // Don't ASSERT, since this can happen if somebody writes bad JS.
+ if (!isnan(value) && !isinf(value))
+ m_value = value;
+}
+
+bool AudioParam::smooth()
+{
+ if (m_smoothedValue == m_value) {
+ // Smoothed value has already approached and snapped to value.
+ return true;
+ }
+
+ // Exponential approach
+ m_smoothedValue += (m_value - m_smoothedValue) * m_smoothingConstant;
+
+ // If we get close enough then snap to actual value.
+ if (fabs(m_smoothedValue - m_value) < SnapThreshold) // FIXME: the threshold needs to be adjustable depending on range - but this is OK general purpose value.
+ m_smoothedValue = m_value;
+
+ return false;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioParam.h b/Source/WebCore/webaudio/AudioParam.h
new file mode 100644
index 0000000..88b7615
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioParam_h
+#define AudioParam_h
+
+#include "PlatformString.h"
+#include <sys/types.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class AudioParam : public RefCounted<AudioParam> {
+public:
+ static const double DefaultSmoothingConstant;
+ static const double SnapThreshold;
+
+ static PassRefPtr<AudioParam> create(const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
+ {
+ return adoptRef(new AudioParam(name, defaultValue, minValue, maxValue, units));
+ }
+
+ AudioParam(const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
+ : m_name(name)
+ , m_value(defaultValue)
+ , m_defaultValue(defaultValue)
+ , m_minValue(minValue)
+ , m_maxValue(maxValue)
+ , m_units(units)
+ , m_smoothedValue(defaultValue)
+ , m_smoothingConstant(DefaultSmoothingConstant)
+ {
+ }
+
+ float value() const { return static_cast<float>(m_value); }
+
+ void setValue(float);
+
+ String name() const { return m_name; }
+
+ float minValue() const { return static_cast<float>(m_minValue); }
+ float maxValue() const { return static_cast<float>(m_maxValue); }
+ float defaultValue() const { return static_cast<float>(m_defaultValue); }
+ unsigned units() const { return m_units; }
+
+ // Value smoothing:
+
+ // When a new value is set with setValue(), in our internal use of the parameter we don't immediately jump to it.
+ // Instead we smoothly approach this value to avoid glitching.
+ float smoothedValue() const { return static_cast<float>(m_smoothedValue); }
+
+ // Smoothly exponentially approaches to (de-zippers) the desired value.
+ // Returns true if smoothed value has already snapped exactly to value.
+ bool smooth();
+
+ void resetSmoothedValue() { m_smoothedValue = m_value; }
+ void setSmoothingConstant(double k) { m_smoothingConstant = k; }
+
+private:
+ String m_name;
+ double m_value;
+ double m_defaultValue;
+ double m_minValue;
+ double m_maxValue;
+ unsigned m_units;
+
+ // Smoothing (de-zippering)
+ double m_smoothedValue;
+ double m_smoothingConstant;
+};
+
+} // namespace WebCore
+
+#endif // AudioParam_h
diff --git a/Source/WebCore/webaudio/AudioParam.idl b/Source/WebCore/webaudio/AudioParam.idl
new file mode 100644
index 0000000..ff2598e
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.idl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module webaudio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioParam {
+ attribute float value;
+ readonly attribute float minValue;
+ readonly attribute float maxValue;
+ readonly attribute float defaultValue;
+
+ readonly attribute DOMString name;
+
+ // FIXME: Could define units constants here (seconds, decibels, cents, etc.)...
+ readonly attribute unsigned short units;
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioProcessingEvent.cpp b/Source/WebCore/webaudio/AudioProcessingEvent.cpp
new file mode 100644
index 0000000..54ce521
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioProcessingEvent.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "AudioProcessingEvent.h"
+
+#include "AudioBuffer.h"
+#include "EventNames.h"
+
+namespace WebCore {
+
+PassRefPtr<AudioProcessingEvent> AudioProcessingEvent::create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
+{
+ return adoptRef(new AudioProcessingEvent(inputBuffer, outputBuffer));
+}
+
+AudioProcessingEvent::AudioProcessingEvent(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
+ : Event(eventNames().audioprocessEvent, true, false)
+ , m_inputBuffer(inputBuffer)
+ , m_outputBuffer(outputBuffer)
+{
+}
+
+AudioProcessingEvent::~AudioProcessingEvent()
+{
+}
+
+bool AudioProcessingEvent::isAudioProcessingEvent() const
+{
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioProcessingEvent.h b/Source/WebCore/webaudio/AudioProcessingEvent.h
new file mode 100644
index 0000000..a88669c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioProcessingEvent.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioProcessingEvent_h
+#define AudioProcessingEvent_h
+
+#include "AudioBuffer.h"
+#include "Event.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+
+class AudioProcessingEvent : public Event {
+public:
+ static PassRefPtr<AudioProcessingEvent> create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer);
+
+ virtual ~AudioProcessingEvent();
+
+ virtual bool isAudioProcessingEvent() const;
+
+ AudioBuffer* inputBuffer() { return m_inputBuffer.get(); }
+ AudioBuffer* outputBuffer() { return m_outputBuffer.get(); }
+
+private:
+ AudioProcessingEvent(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer);
+
+ RefPtr<AudioBuffer> m_inputBuffer;
+ RefPtr<AudioBuffer> m_outputBuffer;
+};
+
+} // namespace WebCore
+
+#endif // AudioProcessingEvent_h
diff --git a/Source/WebCore/webaudio/AudioProcessingEvent.idl b/Source/WebCore/webaudio/AudioProcessingEvent.idl
new file mode 100644
index 0000000..c2f8a83
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioProcessingEvent.idl
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] AudioProcessingEvent : Event {
+ readonly attribute AudioBuffer inputBuffer;
+ readonly attribute AudioBuffer outputBuffer;
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioSourceNode.h b/Source/WebCore/webaudio/AudioSourceNode.h
new file mode 100644
index 0000000..6091371
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioSourceNode.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#ifndef AudioSourceNode_h
+#define AudioSourceNode_h
+
+#include "AudioNode.h"
+
+namespace WebCore {
+
+class AudioSourceNode : public AudioNode {
+public:
+ AudioSourceNode(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+ {
+ }
+};
+
+} // namespace WebCore
+
+#endif // AudioSourceNode_h
diff --git a/Source/WebCore/webaudio/AudioSourceNode.idl b/Source/WebCore/webaudio/AudioSourceNode.idl
new file mode 100644
index 0000000..ec3c356
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioSourceNode.idl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO
+ ] AudioSourceNode : AudioNode {
+ };
+}
diff --git a/Source/WebCore/webaudio/BiquadDSPKernel.cpp b/Source/WebCore/webaudio/BiquadDSPKernel.cpp
new file mode 100644
index 0000000..a4b28be
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadDSPKernel.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "BiquadDSPKernel.h"
+
+#include "BiquadProcessor.h"
+
+namespace WebCore {
+
+void BiquadDSPKernel::process(const float* source, float* destination, size_t framesToProcess)
+{
+ ASSERT(source && destination && biquadProcessor());
+
+ // Recompute filter coefficients if any of the parameters have changed.
+ // FIXME: as an optimization, implement a way that a Biquad object can simply copy its internal filter coefficients from another Biquad object.
+ // Then re-factor this code to only run for the first BiquadDSPKernel of each BiquadProcessor.
+ if (biquadProcessor()->filterCoefficientsDirty()) {
+ double value1 = biquadProcessor()->parameter1()->smoothedValue();
+ double value2 = biquadProcessor()->parameter2()->smoothedValue();
+
+ // Convert from Hertz to normalized frequency 0 -> 1.
+ double nyquist = this->nyquist();
+ double normalizedValue1 = value1 / nyquist;
+
+ // Configure the biquad with the new filter parameters for the appropriate type of filter.
+ switch (biquadProcessor()->type()) {
+ case BiquadProcessor::LowPass2:
+ m_biquad.setLowpassParams(normalizedValue1, value2);
+ break;
+
+ case BiquadProcessor::HighPass2:
+ m_biquad.setHighpassParams(normalizedValue1, value2);
+ break;
+
+ case BiquadProcessor::LowShelf:
+ m_biquad.setLowShelfParams(normalizedValue1, value2);
+ break;
+
+ // FIXME: add other biquad filter types...
+ case BiquadProcessor::Peaking:
+ case BiquadProcessor::Allpass:
+ case BiquadProcessor::HighShelf:
+ break;
+ }
+ }
+
+ m_biquad.process(source, destination, framesToProcess);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/BiquadDSPKernel.h b/Source/WebCore/webaudio/BiquadDSPKernel.h
new file mode 100644
index 0000000..47d0f34
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadDSPKernel.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef BiquadDSPKernel_h
+#define BiquadDSPKernel_h
+
+#include "AudioDSPKernel.h"
+#include "Biquad.h"
+#include "BiquadProcessor.h"
+
+namespace WebCore {
+
+class BiquadProcessor;
+
+// BiquadDSPKernel is an AudioDSPKernel and is responsible for filtering one channel of a BiquadProcessor using a Biquad object.
+
+class BiquadDSPKernel : public AudioDSPKernel {
+public:
+ BiquadDSPKernel(BiquadProcessor* processor)
+ : AudioDSPKernel(processor)
+ {
+ }
+
+ // AudioDSPKernel
+ virtual void process(const float* source, float* dest, size_t framesToProcess);
+ virtual void reset() { m_biquad.reset(); }
+
+protected:
+ Biquad m_biquad;
+ BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // BiquadDSPKernel_h
diff --git a/Source/WebCore/webaudio/BiquadProcessor.cpp b/Source/WebCore/webaudio/BiquadProcessor.cpp
new file mode 100644
index 0000000..97a480e
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadProcessor.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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 "BiquadProcessor.h"
+
+#include "BiquadDSPKernel.h"
+
+namespace WebCore {
+
+BiquadProcessor::BiquadProcessor(FilterType type, double sampleRate, size_t numberOfChannels, bool autoInitialize)
+ : AudioDSPKernelProcessor(sampleRate, numberOfChannels)
+ , m_type(type)
+ , m_parameter1(0)
+ , m_parameter2(0)
+ , m_parameter3(0)
+ , m_filterCoefficientsDirty(true)
+{
+ double nyquist = 0.5 * this->sampleRate();
+
+ switch (type) {
+ // Highpass and lowpass share the same parameters and only differ in filter type.
+ case LowPass2:
+ case HighPass2:
+ m_parameter1 = AudioParam::create("frequency", 350.0, 20.0, nyquist);
+ m_parameter2 = AudioParam::create("resonance", 0.0, -20.0, 20.0);
+ m_parameter3 = AudioParam::create("unused", 0.0, 0.0, 1.0);
+ break;
+
+ case Peaking:
+ m_parameter1 = AudioParam::create("frequency", 2500.0, 20.0, nyquist);
+ m_parameter2 = AudioParam::create("gain", 0.0, -20.0, 20.0);
+ m_parameter3 = AudioParam::create("Q", 0.5, 0.0, 1000.0);
+ break;
+ case Allpass:
+ m_parameter1 = AudioParam::create("frequency", 2500.0, 20.0, nyquist);
+ m_parameter2 = AudioParam::create("Q", 0.5, 0.0, 1000.0);
+ m_parameter3 = AudioParam::create("unused", 0.0, 0.0, 1.0);
+ break;
+ case LowShelf:
+ m_parameter1 = AudioParam::create("frequency", 80.0, 20.0, nyquist);
+ m_parameter2 = AudioParam::create("gain", 0.0, 0.0, 1.0);
+ m_parameter3 = AudioParam::create("unused", 0.0, 0.0, 1.0);
+ break;
+ case HighShelf:
+ m_parameter1 = AudioParam::create("frequency", 10000.0, 20.0, nyquist);
+ m_parameter2 = AudioParam::create("gain", 0.0, 0.0, 1.0);
+ m_parameter3 = AudioParam::create("unused", 0.0, 0.0, 1.0);
+ break;
+ }
+
+ if (autoInitialize)
+ initialize();
+}
+
+BiquadProcessor::~BiquadProcessor()
+{
+ if (isInitialized())
+ uninitialize();
+}
+
+PassOwnPtr<AudioDSPKernel> BiquadProcessor::createKernel()
+{
+ return adoptPtr(new BiquadDSPKernel(this));
+}
+
+void BiquadProcessor::process(AudioBus* source, AudioBus* destination, size_t framesToProcess)
+{
+ if (!isInitialized()) {
+ destination->zero();
+ return;
+ }
+
+ // Deal with smoothing / de-zippering. Start out assuming filter parameters are not changing.
+ // The BiquadDSPKernel objects rely on this value to see if they need to re-compute their internal filter coefficients.
+ m_filterCoefficientsDirty = false;
+
+ if (m_hasJustReset) {
+ // Snap to exact values first time after reset, then smooth for subsequent changes.
+ m_parameter1->resetSmoothedValue();
+ m_parameter2->resetSmoothedValue();
+ m_parameter3->resetSmoothedValue();
+ m_filterCoefficientsDirty = true;
+ m_hasJustReset = false;
+ } else {
+ // Smooth all of the filter parameters. If they haven't yet converged to their target value then mark coefficients as dirty.
+ bool isStable1 = m_parameter1->smooth();
+ bool isStable2 = m_parameter2->smooth();
+ bool isStable3 = m_parameter3->smooth();
+ if (!(isStable1 && isStable2 && isStable3))
+ m_filterCoefficientsDirty = true;
+ }
+
+ // For each channel of our input, process using the corresponding BiquadDSPKernel into the output channel.
+ for (unsigned i = 0; i < m_kernels.size(); ++i)
+ m_kernels[i]->process(source->channel(i)->data(), destination->channel(i)->data(), framesToProcess);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/BiquadProcessor.h b/Source/WebCore/webaudio/BiquadProcessor.h
new file mode 100644
index 0000000..55dca33
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadProcessor.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef BiquadProcessor_h
+#define BiquadProcessor_h
+
+#include "AudioDSPKernel.h"
+#include "AudioDSPKernelProcessor.h"
+#include "AudioNode.h"
+#include "AudioParam.h"
+#include "Biquad.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+// BiquadProcessor is an AudioDSPKernelProcessor which uses Biquad objects to implement several common filters.
+
+class BiquadProcessor : public AudioDSPKernelProcessor {
+public:
+ enum FilterType {
+ LowPass2,
+ HighPass2,
+ Peaking,
+ Allpass,
+ LowShelf,
+ HighShelf
+ };
+
+ BiquadProcessor(FilterType, double sampleRate, size_t numberOfChannels, bool autoInitialize = true);
+ virtual ~BiquadProcessor();
+
+ virtual PassOwnPtr<AudioDSPKernel> createKernel();
+
+ virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess);
+
+ bool filterCoefficientsDirty() const { return m_filterCoefficientsDirty; }
+
+ AudioParam* parameter1() { return m_parameter1.get(); }
+ AudioParam* parameter2() { return m_parameter2.get(); }
+ AudioParam* parameter3() { return m_parameter3.get(); }
+
+ FilterType type() const { return m_type; }
+
+private:
+ FilterType m_type;
+
+ RefPtr<AudioParam> m_parameter1;
+ RefPtr<AudioParam> m_parameter2;
+ RefPtr<AudioParam> m_parameter3;
+
+ // so DSP kernels know when to re-compute coefficients
+ bool m_filterCoefficientsDirty;
+};
+
+} // namespace WebCore
+
+#endif // BiquadProcessor_h
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)
diff --git a/Source/WebCore/webaudio/ConvolverNode.h b/Source/WebCore/webaudio/ConvolverNode.h
new file mode 100644
index 0000000..7b71ba9
--- /dev/null
+++ b/Source/WebCore/webaudio/ConvolverNode.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef ConvolverNode_h
+#define ConvolverNode_h
+
+#include "AudioNode.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class Reverb;
+
+class ConvolverNode : public AudioNode {
+public:
+ static PassRefPtr<ConvolverNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new ConvolverNode(context, sampleRate));
+ }
+
+ virtual ~ConvolverNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // Impulse responses
+ void setBuffer(AudioBuffer*);
+ AudioBuffer* buffer();
+
+private:
+ ConvolverNode(AudioContext*, double sampleRate);
+
+ OwnPtr<Reverb> m_reverb;
+ RefPtr<AudioBuffer> m_buffer;
+
+ // This synchronizes dynamic changes to the convolution impulse response with process().
+ mutable Mutex m_processLock;
+};
+
+} // namespace WebCore
+
+#endif // ConvolverNode_h
diff --git a/Source/WebCore/webaudio/ConvolverNode.idl b/Source/WebCore/webaudio/ConvolverNode.idl
new file mode 100644
index 0000000..d3eb475
--- /dev/null
+++ b/Source/WebCore/webaudio/ConvolverNode.idl
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+module audio {
+ // A linear convolution effect
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] ConvolverNode : AudioNode {
+ attribute [JSCCustomSetter] AudioBuffer buffer;
+ };
+}
diff --git a/Source/WebCore/webaudio/DelayDSPKernel.cpp b/Source/WebCore/webaudio/DelayDSPKernel.cpp
new file mode 100644
index 0000000..9cb0450
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayDSPKernel.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 "DelayDSPKernel.h"
+
+#include "AudioUtilities.h"
+#include <algorithm>
+
+using namespace std;
+
+const double DefaultMaxDelayTime = 1.0;
+const double SmoothingTimeConstant = 0.020; // 20ms
+
+namespace WebCore {
+
+DelayDSPKernel::DelayDSPKernel(DelayProcessor* processor)
+ : AudioDSPKernel(processor)
+ , m_maxDelayTime(DefaultMaxDelayTime)
+ , m_writeIndex(0)
+ , m_firstTime(true)
+{
+ ASSERT(processor && processor->sampleRate() > 0);
+ if (!processor)
+ return;
+
+ m_buffer.resize(static_cast<size_t>(processor->sampleRate() * DefaultMaxDelayTime));
+ m_buffer.zero();
+
+ m_smoothingRate = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, processor->sampleRate());
+}
+
+DelayDSPKernel::DelayDSPKernel(double maxDelayTime, double sampleRate)
+ : AudioDSPKernel(sampleRate)
+ , m_maxDelayTime(maxDelayTime)
+ , m_writeIndex(0)
+ , m_firstTime(true)
+{
+ ASSERT(maxDelayTime > 0.0);
+ if (maxDelayTime <= 0.0)
+ return;
+
+ size_t bufferLength = static_cast<size_t>(sampleRate * maxDelayTime);
+ ASSERT(bufferLength);
+ if (!bufferLength)
+ return;
+
+ m_buffer.resize(bufferLength);
+ m_buffer.zero();
+
+ m_smoothingRate = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, sampleRate);
+}
+
+void DelayDSPKernel::process(const float* source, float* destination, size_t framesToProcess)
+{
+ size_t bufferLength = m_buffer.size();
+ float* buffer = m_buffer.data();
+
+ ASSERT(bufferLength);
+ if (!bufferLength)
+ return;
+
+ ASSERT(source && destination);
+ if (!source || !destination)
+ return;
+
+ double sampleRate = this->sampleRate();
+ double delayTime = delayProcessor() ? delayProcessor()->delayTime()->value() : m_desiredDelayFrames / sampleRate;
+
+ // Make sure the delay time is in a valid range.
+ delayTime = min(maxDelayTime(), delayTime);
+ delayTime = max(0.0, delayTime);
+
+ if (m_firstTime) {
+ m_currentDelayTime = delayTime;
+ m_firstTime = false;
+ }
+
+ int n = framesToProcess;
+ while (n--) {
+ // Approach desired delay time.
+ m_currentDelayTime += (delayTime - m_currentDelayTime) * m_smoothingRate;
+
+ double desiredDelayFrames = m_currentDelayTime * sampleRate;
+
+ double readPosition = m_writeIndex + bufferLength - desiredDelayFrames;
+ if (readPosition > bufferLength)
+ readPosition -= bufferLength;
+
+ // Linearly interpolate in-between delay times.
+ int readIndex1 = static_cast<int>(readPosition);
+ int readIndex2 = (readIndex1 + 1) % bufferLength;
+ double interpolationFactor = readPosition - readIndex1;
+
+ double input = static_cast<float>(*source++);
+ buffer[m_writeIndex] = static_cast<float>(input);
+ m_writeIndex = (m_writeIndex + 1) % bufferLength;
+
+ double sample1 = buffer[readIndex1];
+ double sample2 = buffer[readIndex2];
+
+ double output = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
+
+ *destination++ = static_cast<float>(output);
+ }
+}
+
+void DelayDSPKernel::reset()
+{
+ m_firstTime = true;
+ m_buffer.zero();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/DelayDSPKernel.h b/Source/WebCore/webaudio/DelayDSPKernel.h
new file mode 100644
index 0000000..2ae36cb
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayDSPKernel.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef DelayDSPKernel_h
+#define DelayDSPKernel_h
+
+#include "AudioArray.h"
+#include "AudioDSPKernel.h"
+#include "DelayProcessor.h"
+
+namespace WebCore {
+
+class DelayProcessor;
+
+class DelayDSPKernel : public AudioDSPKernel {
+public:
+ DelayDSPKernel(DelayProcessor*);
+ DelayDSPKernel(double maxDelayTime, double sampleRate);
+
+ virtual void process(const float* source, float* destination, size_t framesToProcess);
+ virtual void reset();
+
+ double maxDelayTime() const { return m_maxDelayTime; }
+
+ void setDelayFrames(double numberOfFrames) { m_desiredDelayFrames = numberOfFrames; }
+
+private:
+ AudioFloatArray m_buffer;
+ double m_maxDelayTime;
+ int m_writeIndex;
+ double m_currentDelayTime;
+ double m_smoothingRate;
+ bool m_firstTime;
+ double m_desiredDelayFrames;
+
+ DelayProcessor* delayProcessor() { return static_cast<DelayProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // DelayDSPKernel_h
diff --git a/Source/WebCore/webaudio/DelayNode.cpp b/Source/WebCore/webaudio/DelayNode.cpp
new file mode 100644
index 0000000..29fceae
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayNode.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "DelayNode.h"
+
+namespace WebCore {
+
+DelayNode::DelayNode(AudioContext* context, double sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new DelayProcessor(sampleRate, 1));
+ setType(NodeTypeDelay);
+}
+
+AudioParam* DelayNode::delayTime()
+{
+ return delayProcessor()->delayTime();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/DelayNode.h b/Source/WebCore/webaudio/DelayNode.h
new file mode 100644
index 0000000..93ad227
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayNode.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef DelayNode_h
+#define DelayNode_h
+
+#include "AudioBasicProcessorNode.h"
+#include "DelayProcessor.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioParam;
+
+class DelayNode : public AudioBasicProcessorNode {
+public:
+ static PassRefPtr<DelayNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new DelayNode(context, sampleRate));
+ }
+
+ AudioParam* delayTime();
+
+private:
+ DelayNode(AudioContext*, double sampleRate);
+
+ DelayProcessor* delayProcessor() { return static_cast<DelayProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // DelayNode_h
diff --git a/Source/WebCore/webaudio/DelayNode.idl b/Source/WebCore/webaudio/DelayNode.idl
new file mode 100644
index 0000000..7756627
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayNode.idl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] DelayNode : AudioNode {
+ readonly attribute AudioParam delayTime;
+ };
+}
diff --git a/Source/WebCore/webaudio/DelayProcessor.cpp b/Source/WebCore/webaudio/DelayProcessor.cpp
new file mode 100644
index 0000000..5fdc8df
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayProcessor.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "DelayProcessor.h"
+
+#include "DelayDSPKernel.h"
+
+namespace WebCore {
+
+DelayProcessor::DelayProcessor(double sampleRate, unsigned numberOfChannels)
+ : AudioDSPKernelProcessor(sampleRate, numberOfChannels)
+{
+ m_delayTime = AudioParam::create("delayTime", 0.0, 0.0, 1.0);
+}
+
+DelayProcessor::~DelayProcessor()
+{
+ if (isInitialized())
+ uninitialize();
+}
+
+PassOwnPtr<AudioDSPKernel> DelayProcessor::createKernel()
+{
+ return adoptPtr(new DelayDSPKernel(this));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/DelayProcessor.h b/Source/WebCore/webaudio/DelayProcessor.h
new file mode 100644
index 0000000..4844c4b
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayProcessor.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef DelayProcessor_h
+#define DelayProcessor_h
+
+#include "AudioDSPKernelProcessor.h"
+#include "AudioParam.h"
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class AudioDSPKernel;
+
+class DelayProcessor : public AudioDSPKernelProcessor {
+public:
+ DelayProcessor(double sampleRate, unsigned numberOfChannels);
+ virtual ~DelayProcessor();
+
+ virtual PassOwnPtr<AudioDSPKernel> createKernel();
+
+ AudioParam* delayTime() const { return m_delayTime.get(); }
+
+private:
+ RefPtr<AudioParam> m_delayTime;
+};
+
+} // namespace WebCore
+
+#endif // DelayProcessor_h
diff --git a/Source/WebCore/webaudio/HighPass2FilterNode.cpp b/Source/WebCore/webaudio/HighPass2FilterNode.cpp
new file mode 100644
index 0000000..ca33784
--- /dev/null
+++ b/Source/WebCore/webaudio/HighPass2FilterNode.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "HighPass2FilterNode.h"
+
+namespace WebCore {
+
+HighPass2FilterNode::HighPass2FilterNode(AudioContext* context, double sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new BiquadProcessor(BiquadProcessor::HighPass2, sampleRate, 1, false));
+ setType(NodeTypeHighPass2Filter);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/HighPass2FilterNode.h b/Source/WebCore/webaudio/HighPass2FilterNode.h
new file mode 100644
index 0000000..be0beb6
--- /dev/null
+++ b/Source/WebCore/webaudio/HighPass2FilterNode.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef HighPass2FilterNode_h
+#define HighPass2FilterNode_h
+
+#include "AudioBasicProcessorNode.h"
+#include "BiquadProcessor.h"
+
+namespace WebCore {
+
+class AudioParam;
+
+class HighPass2FilterNode : public AudioBasicProcessorNode {
+public:
+ static PassRefPtr<HighPass2FilterNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new HighPass2FilterNode(context, sampleRate));
+ }
+
+ AudioParam* cutoff() { return biquadProcessor()->parameter1(); }
+ AudioParam* resonance() { return biquadProcessor()->parameter2(); }
+
+private:
+ HighPass2FilterNode(AudioContext*, double sampleRate);
+
+ BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // HighPass2FilterNode_h
diff --git a/Source/WebCore/webaudio/HighPass2FilterNode.idl b/Source/WebCore/webaudio/HighPass2FilterNode.idl
new file mode 100644
index 0000000..399f9b5
--- /dev/null
+++ b/Source/WebCore/webaudio/HighPass2FilterNode.idl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+module audio {
+ // Two-pole highpass filter
+ // FIXME: design BiquadNode and use instead of this
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] HighPass2FilterNode : AudioNode {
+ readonly attribute AudioParam cutoff;
+ readonly attribute AudioParam resonance;
+ };
+}
diff --git a/Source/WebCore/webaudio/JavaScriptAudioNode.cpp b/Source/WebCore/webaudio/JavaScriptAudioNode.cpp
new file mode 100644
index 0000000..15a8cf7
--- /dev/null
+++ b/Source/WebCore/webaudio/JavaScriptAudioNode.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 "JavaScriptAudioNode.h"
+
+#include "AudioBuffer.h"
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "AudioProcessingEvent.h"
+#include "Document.h"
+#include "Float32Array.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+const size_t DefaultBufferSize = 4096;
+
+PassRefPtr<JavaScriptAudioNode> JavaScriptAudioNode::create(AudioContext* context, double sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs)
+{
+ return adoptRef(new JavaScriptAudioNode(context, sampleRate, bufferSize, numberOfInputs, numberOfOutputs));
+}
+
+JavaScriptAudioNode::JavaScriptAudioNode(AudioContext* context, double sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs)
+ : AudioNode(context, sampleRate)
+ , m_doubleBufferIndex(0)
+ , m_doubleBufferIndexForEvent(0)
+ , m_bufferSize(bufferSize)
+ , m_bufferReadWriteIndex(0)
+ , m_isRequestOutstanding(false)
+{
+ // Check for valid buffer size.
+ switch (bufferSize) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ case 8192:
+ case 16384:
+ m_bufferSize = bufferSize;
+ break;
+ default:
+ m_bufferSize = DefaultBufferSize;
+ }
+
+ // Regardless of the allowed buffer sizes above, we still need to process at the granularity of the AudioNode.
+ if (m_bufferSize < AudioNode::ProcessingSizeInFrames)
+ m_bufferSize = AudioNode::ProcessingSizeInFrames;
+
+ // FIXME: Right now we're hardcoded to single input and single output.
+ // Although the specification says this is OK for a simple implementation, multiple inputs and outputs would be good.
+ ASSERT_UNUSED(numberOfInputs, numberOfInputs == 1);
+ ASSERT_UNUSED(numberOfOutputs, numberOfOutputs == 1);
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setType(NodeTypeJavaScript);
+
+ initialize();
+}
+
+JavaScriptAudioNode::~JavaScriptAudioNode()
+{
+ uninitialize();
+}
+
+void JavaScriptAudioNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ double sampleRate = context()->sampleRate();
+
+ // Create double buffers on both the input and output sides.
+ // These AudioBuffers will be directly accessed in the main thread by JavaScript.
+ for (unsigned i = 0; i < 2; ++i) {
+ m_inputBuffers.append(AudioBuffer::create(2, bufferSize(), sampleRate));
+ m_outputBuffers.append(AudioBuffer::create(2, bufferSize(), sampleRate));
+ }
+
+ AudioNode::initialize();
+}
+
+void JavaScriptAudioNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_inputBuffers.clear();
+ m_outputBuffers.clear();
+
+ AudioNode::uninitialize();
+}
+
+JavaScriptAudioNode* JavaScriptAudioNode::toJavaScriptAudioNode()
+{
+ return this;
+}
+
+void JavaScriptAudioNode::process(size_t framesToProcess)
+{
+ // Discussion about inputs and outputs:
+ // As in other AudioNodes, JavaScriptAudioNode uses an AudioBus for its input and output (see inputBus and outputBus below).
+ // Additionally, there is a double-buffering for input and output which is exposed directly to JavaScript (see inputBuffer and outputBuffer below).
+ // This node is the producer for inputBuffer and the consumer for outputBuffer.
+ // The JavaScript code is the consumer of inputBuffer and the producer for outputBuffer.
+
+ // Get input and output busses.
+ AudioBus* inputBus = this->input(0)->bus();
+ AudioBus* outputBus = this->output(0)->bus();
+
+ // Get input and output buffers. We double-buffer both the input and output sides.
+ unsigned doubleBufferIndex = this->doubleBufferIndex();
+ bool isDoubleBufferIndexGood = doubleBufferIndex < 2 && doubleBufferIndex < m_inputBuffers.size() && doubleBufferIndex < m_outputBuffers.size();
+ ASSERT(isDoubleBufferIndexGood);
+ if (!isDoubleBufferIndexGood)
+ return;
+
+ AudioBuffer* inputBuffer = m_inputBuffers[doubleBufferIndex].get();
+ AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get();
+
+ // Check the consistency of input and output buffers.
+ bool buffersAreGood = inputBuffer && outputBuffer && bufferSize() == inputBuffer->length() && bufferSize() == outputBuffer->length()
+ && m_bufferReadWriteIndex + framesToProcess <= bufferSize();
+ ASSERT(buffersAreGood);
+ if (!buffersAreGood)
+ return;
+
+ // We assume that bufferSize() is evenly divisible by framesToProcess - should always be true, but we should still check.
+ bool isFramesToProcessGood = framesToProcess && bufferSize() >= framesToProcess && !(bufferSize() % framesToProcess);
+ ASSERT(isFramesToProcessGood);
+ if (!isFramesToProcessGood)
+ return;
+
+ unsigned numberOfInputChannels = inputBus->numberOfChannels();
+
+ bool channelsAreGood = (numberOfInputChannels == 1 || numberOfInputChannels == 2) && outputBus->numberOfChannels() == 2;
+ ASSERT(channelsAreGood);
+ if (!channelsAreGood)
+ return;
+
+ float* sourceL = inputBus->channel(0)->data();
+ float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : 0;
+ float* destinationL = outputBus->channel(0)->data();
+ float* destinationR = outputBus->channel(1)->data();
+
+ // Copy from the input to the input buffer. See "buffersAreGood" check above for safety.
+ size_t bytesToCopy = sizeof(float) * framesToProcess;
+ memcpy(inputBuffer->getChannelData(0)->data() + m_bufferReadWriteIndex, sourceL, bytesToCopy);
+
+ if (numberOfInputChannels == 2)
+ memcpy(inputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, sourceR, bytesToCopy);
+ else if (numberOfInputChannels == 1) {
+ // If the input is mono, then also copy the mono input to the right channel of the AudioBuffer which the AudioProcessingEvent uses.
+ // FIXME: it is likely the audio API will evolve to present an AudioBuffer with the same number of channels as our input.
+ memcpy(inputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, sourceL, bytesToCopy);
+ }
+
+ // Copy from the output buffer to the output. See "buffersAreGood" check above for safety.
+ memcpy(destinationL, outputBuffer->getChannelData(0)->data() + m_bufferReadWriteIndex, bytesToCopy);
+ memcpy(destinationR, outputBuffer->getChannelData(1)->data() + m_bufferReadWriteIndex, bytesToCopy);
+
+ // Update the buffering index.
+ m_bufferReadWriteIndex = (m_bufferReadWriteIndex + framesToProcess) % bufferSize();
+
+ // m_bufferReadWriteIndex will wrap back around to 0 when the current input and output buffers are full.
+ // When this happens, fire an event and swap buffers.
+ if (!m_bufferReadWriteIndex) {
+ // Avoid building up requests on the main thread to fire process events when they're not being handled.
+ // This could be a problem if the main thread is very busy doing other things and is being held up handling previous requests.
+ if (m_isRequestOutstanding) {
+ // We're late in handling the previous request. The main thread must be very busy.
+ // The best we can do is clear out the buffer ourself here.
+ outputBuffer->zero();
+ } else {
+ // Reference ourself so we don't accidentally get deleted before fireProcessEvent() gets called.
+ ref();
+
+ // Fire the event on the main thread, not this one (which is the realtime audio thread).
+ m_doubleBufferIndexForEvent = m_doubleBufferIndex;
+ callOnMainThread(fireProcessEventDispatch, this);
+ m_isRequestOutstanding = true;
+ }
+
+ swapBuffers();
+ }
+}
+
+void JavaScriptAudioNode::fireProcessEventDispatch(void* userData)
+{
+ JavaScriptAudioNode* jsAudioNode = static_cast<JavaScriptAudioNode*>(userData);
+ ASSERT(jsAudioNode);
+ if (!jsAudioNode)
+ return;
+
+ jsAudioNode->fireProcessEvent();
+
+ // De-reference to match the ref() call in process().
+ jsAudioNode->deref();
+}
+
+void JavaScriptAudioNode::fireProcessEvent()
+{
+ ASSERT(isMainThread() && m_isRequestOutstanding);
+
+ bool isIndexGood = m_doubleBufferIndexForEvent < 2;
+ ASSERT(isIndexGood);
+ if (!isIndexGood)
+ return;
+
+ AudioBuffer* inputBuffer = m_inputBuffers[m_doubleBufferIndexForEvent].get();
+ AudioBuffer* outputBuffer = m_outputBuffers[m_doubleBufferIndexForEvent].get();
+ ASSERT(inputBuffer && outputBuffer);
+ if (!inputBuffer || !outputBuffer)
+ return;
+
+ // Avoid firing the event if the document has already gone away.
+ if (context()->hasDocument()) {
+ // Let the audio thread know we've gotten to the point where it's OK for it to make another request.
+ m_isRequestOutstanding = false;
+
+ // Call the JavaScript event handler which will do the audio processing.
+ dispatchEvent(AudioProcessingEvent::create(inputBuffer, outputBuffer));
+ }
+}
+
+void JavaScriptAudioNode::reset()
+{
+ m_bufferReadWriteIndex = 0;
+ m_doubleBufferIndex = 0;
+
+ for (unsigned i = 0; i < 2; ++i) {
+ m_inputBuffers[i]->zero();
+ m_outputBuffers[i]->zero();
+ }
+}
+
+ScriptExecutionContext* JavaScriptAudioNode::scriptExecutionContext() const
+{
+ return const_cast<JavaScriptAudioNode*>(this)->context()->document();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/JavaScriptAudioNode.h b/Source/WebCore/webaudio/JavaScriptAudioNode.h
new file mode 100644
index 0000000..e99a25d
--- /dev/null
+++ b/Source/WebCore/webaudio/JavaScriptAudioNode.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef JavaScriptAudioNode_h
+#define JavaScriptAudioNode_h
+
+#include "ActiveDOMObject.h"
+#include "AudioNode.h"
+#include "EventListener.h"
+#include "EventTarget.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class AudioContext;
+class AudioProcessingEvent;
+class Float32Array;
+
+// JavaScriptAudioNode is an AudioNode which allows for arbitrary synthesis or processing directly using JavaScript.
+// The API allows for a variable number of inputs and outputs, although it must have at least one input or output.
+// This basic implementation supports no more than one input and output.
+// The "onaudioprocess" attribute is an event listener which will get called periodically with an AudioProcessingEvent which has
+// AudioBuffers for each input and output.
+
+class JavaScriptAudioNode : public AudioNode, public EventTarget {
+public:
+ // bufferSize must be one of the following values: 256, 512, 1024, 2048, 4096, 8192, 16384.
+ // This value controls how frequently the onaudioprocess event handler is called and how many sample-frames need to be processed each call.
+ // Lower numbers for bufferSize will result in a lower (better) latency. Higher numbers will be necessary to avoid audio breakup and glitches.
+ // The value chosen must carefully balance between latency and audio quality.
+ static PassRefPtr<JavaScriptAudioNode> create(AudioContext*, double sampleRate, size_t bufferSize, unsigned numberOfInputs = 1, unsigned numberOfOutputs = 1);
+
+ virtual ~JavaScriptAudioNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // EventTarget
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ virtual JavaScriptAudioNode* toJavaScriptAudioNode();
+ virtual EventTargetData* eventTargetData() { return &m_eventTargetData; }
+ virtual EventTargetData* ensureEventTargetData() { return &m_eventTargetData; }
+
+ size_t bufferSize() const { return m_bufferSize; }
+
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(audioprocess);
+
+ // Reconcile ref/deref which are defined both in AudioNode and EventTarget.
+ using AudioNode::ref;
+ using AudioNode::deref;
+
+private:
+ JavaScriptAudioNode(AudioContext*, double sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs);
+
+ static void fireProcessEventDispatch(void* userData);
+ void fireProcessEvent();
+
+ // Double buffering
+ unsigned doubleBufferIndex() const { return m_doubleBufferIndex; }
+ void swapBuffers() { m_doubleBufferIndex = 1 - m_doubleBufferIndex; }
+ unsigned m_doubleBufferIndex;
+ unsigned m_doubleBufferIndexForEvent;
+ Vector<RefPtr<AudioBuffer> > m_inputBuffers;
+ Vector<RefPtr<AudioBuffer> > m_outputBuffers;
+
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+ EventTargetData m_eventTargetData;
+
+ size_t m_bufferSize;
+ unsigned m_bufferReadWriteIndex;
+ volatile bool m_isRequestOutstanding;
+};
+
+} // namespace WebCore
+
+#endif // JavaScriptAudioNode_h
diff --git a/Source/WebCore/webaudio/JavaScriptAudioNode.idl b/Source/WebCore/webaudio/JavaScriptAudioNode.idl
new file mode 100644
index 0000000..ef5359b
--- /dev/null
+++ b/Source/WebCore/webaudio/JavaScriptAudioNode.idl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+module audio {
+ // For real-time audio stream synthesis/processing in JavaScript
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS,
+ CustomMarkFunction,
+#if defined(V8_BINDING) && V8_BINDING
+ EventTarget
+#endif
+ ] JavaScriptAudioNode : AudioNode {
+ // Rendering callback
+ attribute EventListener onaudioprocess;
+
+ readonly attribute long bufferSize;
+ };
+}
diff --git a/Source/WebCore/webaudio/LowPass2FilterNode.cpp b/Source/WebCore/webaudio/LowPass2FilterNode.cpp
new file mode 100644
index 0000000..691f4ed
--- /dev/null
+++ b/Source/WebCore/webaudio/LowPass2FilterNode.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "LowPass2FilterNode.h"
+
+namespace WebCore {
+
+LowPass2FilterNode::LowPass2FilterNode(AudioContext* context, double sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new BiquadProcessor(BiquadProcessor::LowPass2, sampleRate, 1, false));
+ setType(NodeTypeLowPass2Filter);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/LowPass2FilterNode.h b/Source/WebCore/webaudio/LowPass2FilterNode.h
new file mode 100644
index 0000000..43d7051
--- /dev/null
+++ b/Source/WebCore/webaudio/LowPass2FilterNode.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef LowPass2FilterNode_h
+#define LowPass2FilterNode_h
+
+#include "AudioBasicProcessorNode.h"
+#include "BiquadProcessor.h"
+
+namespace WebCore {
+
+class AudioParam;
+
+class LowPass2FilterNode : public AudioBasicProcessorNode {
+public:
+ static PassRefPtr<LowPass2FilterNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new LowPass2FilterNode(context, sampleRate));
+ }
+
+ AudioParam* cutoff() { return biquadProcessor()->parameter1(); }
+ AudioParam* resonance() { return biquadProcessor()->parameter2(); }
+
+private:
+ LowPass2FilterNode(AudioContext*, double sampleRate);
+
+ BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // LowPass2FilterNode_h
diff --git a/Source/WebCore/webaudio/LowPass2FilterNode.idl b/Source/WebCore/webaudio/LowPass2FilterNode.idl
new file mode 100644
index 0000000..310c21e
--- /dev/null
+++ b/Source/WebCore/webaudio/LowPass2FilterNode.idl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+module audio {
+ // Two-pole lowpass filter
+ // FIXME: design BiquadNode and use instead of this
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] LowPass2FilterNode : AudioNode {
+ readonly attribute AudioParam cutoff;
+ readonly attribute AudioParam resonance;
+ };
+}
diff --git a/Source/WebCore/webaudio/RealtimeAnalyser.cpp b/Source/WebCore/webaudio/RealtimeAnalyser.cpp
new file mode 100644
index 0000000..30a7de1
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyser.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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 "RealtimeAnalyser.h"
+
+#include "AudioBus.h"
+#include "AudioUtilities.h"
+#include "FFTFrame.h"
+
+#if ENABLE(3D_CANVAS)
+#include "Float32Array.h"
+#include "Uint8Array.h"
+#endif
+
+#include <algorithm>
+#include <limits.h>
+#include <wtf/Complex.h>
+#include <wtf/MathExtras.h>
+#include <wtf/Threading.h>
+
+using namespace std;
+
+namespace WebCore {
+
+const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8;
+const double RealtimeAnalyser::DefaultMinDecibels = -100.0;
+const double RealtimeAnalyser::DefaultMaxDecibels = -30.0;
+
+const unsigned RealtimeAnalyser::DefaultFFTSize = 2048;
+const unsigned RealtimeAnalyser::MaxFFTSize = 2048;
+const unsigned RealtimeAnalyser::InputBufferSize = RealtimeAnalyser::MaxFFTSize * 2;
+
+RealtimeAnalyser::RealtimeAnalyser()
+ : m_inputBuffer(InputBufferSize)
+ , m_writeIndex(0)
+ , m_fftSize(DefaultFFTSize)
+ , m_magnitudeBuffer(DefaultFFTSize / 2)
+ , m_smoothingTimeConstant(DefaultSmoothingTimeConstant)
+ , m_minDecibels(DefaultMinDecibels)
+ , m_maxDecibels(DefaultMaxDecibels)
+{
+ m_analysisFrame = adoptPtr(new FFTFrame(DefaultFFTSize));
+}
+
+RealtimeAnalyser::~RealtimeAnalyser()
+{
+}
+
+void RealtimeAnalyser::reset()
+{
+ m_writeIndex = 0;
+ m_inputBuffer.zero();
+ m_magnitudeBuffer.zero();
+}
+
+void RealtimeAnalyser::setFftSize(size_t size)
+{
+ ASSERT(isMainThread());
+
+ // Only allow powers of two.
+ unsigned log2size = static_cast<unsigned>(log2(size));
+ bool isPOT(1UL << log2size == size);
+
+ if (!isPOT || size > MaxFFTSize) {
+ // FIXME: It would be good to also set an exception.
+ return;
+ }
+
+ if (m_fftSize != size) {
+ m_analysisFrame = adoptPtr(new FFTFrame(m_fftSize));
+ m_magnitudeBuffer.resize(size);
+ m_fftSize = size;
+ }
+}
+
+void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess)
+{
+ bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->length() >= framesToProcess;
+ ASSERT(isBusGood);
+ if (!isBusGood)
+ return;
+
+ // FIXME : allow to work with non-FFTSize divisible chunking
+ bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex + framesToProcess <= m_inputBuffer.size();
+ ASSERT(isDestinationGood);
+ if (!isDestinationGood)
+ return;
+
+ // Perform real-time analysis
+ // FIXME : for now just use left channel (must mix if stereo source)
+ float* source = bus->channel(0)->data();
+
+ // The source has already been sanity checked with isBusGood above.
+
+ memcpy(m_inputBuffer.data() + m_writeIndex, source, sizeof(float) * framesToProcess);
+
+ m_writeIndex += framesToProcess;
+ if (m_writeIndex >= InputBufferSize)
+ m_writeIndex = 0;
+}
+
+namespace {
+
+void applyWindow(float* p, size_t n)
+{
+ ASSERT(isMainThread());
+
+ // Blackman window
+ double alpha = 0.16;
+ double a0 = 0.5 * (1.0 - alpha);
+ double a1 = 0.5;
+ double a2 = 0.5 * alpha;
+
+ for (unsigned i = 0; i < n; ++i) {
+ double x = static_cast<double>(i) / static_cast<double>(n);
+ double window = a0 - a1 * cos(2.0 * piDouble * x) + a2 * cos(4.0 * piDouble * x);
+ p[i] *= float(window);
+ }
+}
+
+} // namespace
+
+void RealtimeAnalyser::doFFTAnalysis()
+{
+ ASSERT(isMainThread());
+
+ // Unroll the input buffer into a temporary buffer, where we'll apply an analysis window followed by an FFT.
+ size_t fftSize = this->fftSize();
+
+ AudioFloatArray temporaryBuffer(fftSize);
+ float* inputBuffer = m_inputBuffer.data();
+ float* tempP = temporaryBuffer.data();
+
+ // Take the previous fftSize values from the input buffer and copy into the temporary buffer.
+ // FIXME : optimize with memcpy().
+ unsigned writeIndex = m_writeIndex;
+ for (unsigned i = 0; i < fftSize; ++i)
+ tempP[i] = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize];
+
+ // Window the input samples.
+ applyWindow(tempP, fftSize);
+
+ // Do the analysis.
+ m_analysisFrame->doFFT(tempP);
+
+ size_t n = DefaultFFTSize / 2;
+
+ float* realP = m_analysisFrame->realData();
+ float* imagP = m_analysisFrame->imagData();
+
+ // Blow away the packed nyquist component.
+ imagP[0] = 0.0f;
+
+ // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
+ const double MagnitudeScale = 1.0 / DefaultFFTSize;
+
+ // A value of 0 does no averaging with the previous result. Larger values produce slower, but smoother changes.
+ double k = m_smoothingTimeConstant;
+ k = max(0.0, k);
+ k = min(1.0, k);
+
+ // Convert the analysis data from complex to magnitude and average with the previous result.
+ float* destination = magnitudeBuffer().data();
+ for (unsigned i = 0; i < n; ++i) {
+ Complex c(realP[i], imagP[i]);
+ double scalarMagnitude = abs(c) * MagnitudeScale;
+ destination[i] = float(k * destination[i] + (1.0 - k) * scalarMagnitude);
+ }
+}
+
+#if ENABLE(3D_CANVAS)
+
+void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray)
+{
+ ASSERT(isMainThread());
+
+ if (!destinationArray)
+ return;
+
+ doFFTAnalysis();
+
+ // Convert from linear magnitude to floating-point decibels.
+ const double MinDecibels = m_minDecibels;
+ unsigned sourceLength = magnitudeBuffer().size();
+ size_t len = min(sourceLength, destinationArray->length());
+ if (len > 0) {
+ const float* source = magnitudeBuffer().data();
+ float* destination = destinationArray->data();
+
+ for (unsigned i = 0; i < len; ++i) {
+ float linearValue = source[i];
+ double dbMag = !linearValue ? MinDecibels : AudioUtilities::linearToDecibels(linearValue);
+ destination[i] = float(dbMag);
+ }
+ }
+}
+
+void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray)
+{
+ ASSERT(isMainThread());
+
+ if (!destinationArray)
+ return;
+
+ doFFTAnalysis();
+
+ // Convert from linear magnitude to unsigned-byte decibels.
+ unsigned sourceLength = magnitudeBuffer().size();
+ size_t len = min(sourceLength, destinationArray->length());
+ if (len > 0) {
+ const double RangeScaleFactor = m_maxDecibels == m_minDecibels ? 1.0 : 1.0 / (m_maxDecibels - m_minDecibels);
+
+ const float* source = magnitudeBuffer().data();
+ unsigned char* destination = destinationArray->data();
+
+ for (unsigned i = 0; i < len; ++i) {
+ float linearValue = source[i];
+ double dbMag = !linearValue ? m_minDecibels : AudioUtilities::linearToDecibels(linearValue);
+
+ // The range m_minDecibels to m_maxDecibels will be scaled to byte values from 0 to UCHAR_MAX.
+ double scaledValue = UCHAR_MAX * (dbMag - m_minDecibels) * RangeScaleFactor;
+
+ // Clip to valid range.
+ if (scaledValue < 0.0)
+ scaledValue = 0.0;
+ if (scaledValue > UCHAR_MAX)
+ scaledValue = UCHAR_MAX;
+
+ destination[i] = static_cast<unsigned char>(scaledValue);
+ }
+ }
+}
+
+void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray)
+{
+ ASSERT(isMainThread());
+
+ if (!destinationArray)
+ return;
+
+ unsigned fftSize = this->fftSize();
+ size_t len = min(fftSize, destinationArray->length());
+ if (len > 0) {
+ bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize;
+ ASSERT(isInputBufferGood);
+ if (!isInputBufferGood)
+ return;
+
+ float* inputBuffer = m_inputBuffer.data();
+ unsigned char* destination = destinationArray->data();
+
+ unsigned writeIndex = m_writeIndex;
+
+ for (unsigned i = 0; i < len; ++i) {
+ // Buffer access is protected due to modulo operation.
+ float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize];
+
+ // Scale from nominal -1.0 -> +1.0 to unsigned byte.
+ double scaledValue = 128.0 * (value + 1.0);
+
+ // Clip to valid range.
+ if (scaledValue < 0.0)
+ scaledValue = 0.0;
+ if (scaledValue > UCHAR_MAX)
+ scaledValue = UCHAR_MAX;
+
+ destination[i] = static_cast<unsigned char>(scaledValue);
+ }
+ }
+}
+
+#endif // 3D_CANVAS
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/RealtimeAnalyser.h b/Source/WebCore/webaudio/RealtimeAnalyser.h
new file mode 100644
index 0000000..686c17c
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyser.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#ifndef RealtimeAnalyser_h
+#define RealtimeAnalyser_h
+
+#include "AudioArray.h"
+#include <wtf/NonCopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+class FFTFrame;
+
+#if ENABLE(3D_CANVAS)
+class Float32Array;
+class Uint8Array;
+#endif
+
+class RealtimeAnalyser : public Noncopyable {
+public:
+ RealtimeAnalyser();
+ virtual ~RealtimeAnalyser();
+
+ void reset();
+
+ size_t fftSize() const { return m_fftSize; }
+ void setFftSize(size_t size);
+
+ unsigned frequencyBinCount() const { return m_fftSize / 2; }
+
+ void setMinDecibels(float k) { m_minDecibels = k; }
+ float minDecibels() const { return static_cast<float>(m_minDecibels); }
+
+ void setMaxDecibels(float k) { m_maxDecibels = k; }
+ float maxDecibels() const { return static_cast<float>(m_maxDecibels); }
+
+ void setSmoothingTimeConstant(float k) { m_smoothingTimeConstant = k; }
+ float smoothingTimeConstant() const { return static_cast<float>(m_smoothingTimeConstant); }
+
+#if ENABLE(3D_CANVAS)
+ void getFloatFrequencyData(Float32Array*);
+ void getByteFrequencyData(Uint8Array*);
+ void getByteTimeDomainData(Uint8Array*);
+#endif
+
+ // The audio thread writes input data here.
+ void writeInput(AudioBus*, size_t framesToProcess);
+
+ static const double DefaultSmoothingTimeConstant;
+ static const double DefaultMinDecibels;
+ static const double DefaultMaxDecibels;
+
+ static const unsigned DefaultFFTSize;
+ static const unsigned MaxFFTSize;
+ static const unsigned InputBufferSize;
+
+private:
+ // The audio thread writes the input audio here.
+ AudioFloatArray m_inputBuffer;
+ unsigned m_writeIndex;
+
+ size_t m_fftSize;
+ OwnPtr<FFTFrame> m_analysisFrame;
+ void doFFTAnalysis();
+
+ // doFFTAnalysis() stores the floating-point magnitude analysis data here.
+ AudioFloatArray m_magnitudeBuffer;
+ AudioFloatArray& magnitudeBuffer() { return m_magnitudeBuffer; }
+
+ // A value between 0 and 1 which averages the previous version of m_magnitudeBuffer with the current analysis magnitude data.
+ double m_smoothingTimeConstant;
+
+ // The range used when converting when using getByteFrequencyData().
+ double m_minDecibels;
+ double m_maxDecibels;
+};
+
+} // namespace WebCore
+
+#endif // RealtimeAnalyser_h
diff --git a/Source/WebCore/webaudio/RealtimeAnalyserNode.cpp b/Source/WebCore/webaudio/RealtimeAnalyserNode.cpp
new file mode 100644
index 0000000..2ba751a
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyserNode.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "RealtimeAnalyserNode.h"
+
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+
+namespace WebCore {
+
+RealtimeAnalyserNode::RealtimeAnalyserNode(AudioContext* context, double sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setType(NodeTypeAnalyser);
+
+ initialize();
+}
+
+RealtimeAnalyserNode::~RealtimeAnalyserNode()
+{
+ uninitialize();
+}
+
+void RealtimeAnalyserNode::process(size_t framesToProcess)
+{
+ AudioBus* outputBus = output(0)->bus();
+
+ if (!isInitialized() || !input(0)->isConnected()) {
+ outputBus->zero();
+ return;
+ }
+
+ AudioBus* inputBus = input(0)->bus();
+
+ // Give the analyser the audio which is passing through this AudioNode.
+ m_analyser.writeInput(inputBus, framesToProcess);
+
+ // For in-place processing, our override of pullInputs() will just pass the audio data through unchanged if the channel count matches from input to output
+ // (resulting in inputBus == outputBus). Otherwise, do an up-mix to stereo.
+ if (inputBus != outputBus)
+ outputBus->copyFrom(*inputBus);
+}
+
+// We override pullInputs() as an optimization allowing this node to take advantage of in-place processing,
+// where the input is simply passed through unprocessed to the output.
+// Note: this only applies if the input and output channel counts match.
+void RealtimeAnalyserNode::pullInputs(size_t framesToProcess)
+{
+ // Render input stream - try to render directly into output bus for pass-through processing where process() doesn't need to do anything...
+ input(0)->pull(output(0)->bus(), framesToProcess);
+}
+
+void RealtimeAnalyserNode::reset()
+{
+ m_analyser.reset();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/RealtimeAnalyserNode.h b/Source/WebCore/webaudio/RealtimeAnalyserNode.h
new file mode 100644
index 0000000..9f62464
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyserNode.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef RealtimeAnalyserNode_h
+#define RealtimeAnalyserNode_h
+
+#include "AudioNode.h"
+#include "RealtimeAnalyser.h"
+
+namespace WebCore {
+
+class RealtimeAnalyserNode : public AudioNode {
+public:
+ static PassRefPtr<RealtimeAnalyserNode> create(AudioContext* context, double sampleRate)
+ {
+ return adoptRef(new RealtimeAnalyserNode(context, sampleRate));
+ }
+
+ virtual ~RealtimeAnalyserNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void pullInputs(size_t framesToProcess);
+ virtual void reset();
+
+ // Javascript bindings
+ unsigned int fftSize() const { return m_analyser.fftSize(); }
+ void setFftSize(unsigned int size) { m_analyser.setFftSize(size); }
+
+ unsigned frequencyBinCount() const { return m_analyser.frequencyBinCount(); }
+
+ void setMinDecibels(float k) { m_analyser.setMinDecibels(k); }
+ float minDecibels() const { return m_analyser.minDecibels(); }
+
+ void setMaxDecibels(float k) { m_analyser.setMaxDecibels(k); }
+ float maxDecibels() const { return m_analyser.maxDecibels(); }
+
+ void setSmoothingTimeConstant(float k) { m_analyser.setSmoothingTimeConstant(k); }
+ float smoothingTimeConstant() const { return m_analyser.smoothingTimeConstant(); }
+
+#if ENABLE(3D_CANVAS)
+ void getFloatFrequencyData(Float32Array* array) { m_analyser.getFloatFrequencyData(array); }
+ void getByteFrequencyData(Uint8Array* array) { m_analyser.getByteFrequencyData(array); }
+ void getByteTimeDomainData(Uint8Array* array) { m_analyser.getByteTimeDomainData(array); }
+#endif
+
+private:
+ RealtimeAnalyserNode(AudioContext*, double sampleRate);
+
+ RealtimeAnalyser m_analyser;
+};
+
+} // namespace WebCore
+
+#endif // RealtimeAnalyserNode_h
diff --git a/Source/WebCore/webaudio/RealtimeAnalyserNode.idl b/Source/WebCore/webaudio/RealtimeAnalyserNode.idl
new file mode 100644
index 0000000..5b2b223
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyserNode.idl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+module audio {
+ interface [
+ Conditional=WEB_AUDIO,
+ GenerateToJS
+ ] RealtimeAnalyserNode : AudioNode {
+ attribute unsigned long fftSize;
+ readonly attribute unsigned long frequencyBinCount;
+
+ // minDecibels / maxDecibels represent the range to scale the FFT analysis data for conversion to unsigned byte values.
+ attribute float minDecibels;
+ attribute float maxDecibels;
+
+ // A value from 0.0 -> 1.0 where 0.0 represents no time averaging with the last analysis frame.
+ attribute float smoothingTimeConstant;
+
+ // Copies the current frequency data into the passed array.
+ // If the array has fewer elements than the frequencyBinCount, the excess elements will be dropped.
+ [Conditional=3D_CANVAS] void getFloatFrequencyData(in Float32Array array);
+ [Conditional=3D_CANVAS] void getByteFrequencyData(in Uint8Array array);
+
+ // Real-time waveform data
+ [Conditional=3D_CANVAS] void getByteTimeDomainData(in Uint8Array array);
+ };
+}