summaryrefslogtreecommitdiff
path: root/Source/WebCore/webaudio
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-01-06 14:44:00 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-01-06 14:44:00 +0100
commit40736c5763bf61337c8c14e16d8587db021a87d4 (patch)
treeb17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebCore/webaudio
downloadqtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebCore/webaudio')
-rw-r--r--Source/WebCore/webaudio/AsyncAudioDecoder.cpp144
-rw-r--r--Source/WebCore/webaudio/AsyncAudioDecoder.h89
-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/AudioBufferCallback.h46
-rw-r--r--Source/WebCore/webaudio/AudioBufferCallback.idl33
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.cpp507
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.h146
-rw-r--r--Source/WebCore/webaudio/AudioBufferSourceNode.idl44
-rw-r--r--Source/WebCore/webaudio/AudioChannelMerger.cpp112
-rw-r--r--Source/WebCore/webaudio/AudioChannelMerger.h59
-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.cpp788
-rw-r--r--Source/WebCore/webaudio/AudioContext.h321
-rw-r--r--Source/WebCore/webaudio/AudioContext.idl85
-rw-r--r--Source/WebCore/webaudio/AudioDestinationNode.cpp91
-rw-r--r--Source/WebCore/webaudio/AudioDestinationNode.h63
-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.cpp125
-rw-r--r--Source/WebCore/webaudio/AudioGainNode.h72
-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.cpp342
-rw-r--r--Source/WebCore/webaudio/AudioNode.h176
-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.cpp116
-rw-r--r--Source/WebCore/webaudio/AudioParam.h123
-rw-r--r--Source/WebCore/webaudio/AudioParam.idl58
-rw-r--r--Source/WebCore/webaudio/AudioParamTimeline.cpp366
-rw-r--r--Source/WebCore/webaudio/AudioParamTimeline.h119
-rw-r--r--Source/WebCore/webaudio/AudioProcessingEvent.cpp68
-rw-r--r--Source/WebCore/webaudio/AudioProcessingEvent.h59
-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.cpp139
-rw-r--r--Source/WebCore/webaudio/BiquadDSPKernel.h71
-rw-r--r--Source/WebCore/webaudio/BiquadFilterNode.cpp77
-rw-r--r--Source/WebCore/webaudio/BiquadFilterNode.h74
-rw-r--r--Source/WebCore/webaudio/BiquadFilterNode.idl51
-rw-r--r--Source/WebCore/webaudio/BiquadProcessor.cpp155
-rw-r--r--Source/WebCore/webaudio/BiquadProcessor.h94
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.cpp155
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.h69
-rw-r--r--Source/WebCore/webaudio/ConvolverNode.idl33
-rw-r--r--Source/WebCore/webaudio/DOMWindowWebAudio.idl32
-rw-r--r--Source/WebCore/webaudio/DefaultAudioDestinationNode.cpp82
-rw-r--r--Source/WebCore/webaudio/DefaultAudioDestinationNode.h61
-rw-r--r--Source/WebCore/webaudio/DelayDSPKernel.cpp140
-rw-r--r--Source/WebCore/webaudio/DelayDSPKernel.h62
-rw-r--r--Source/WebCore/webaudio/DelayNode.cpp48
-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/DynamicsCompressorNode.cpp87
-rw-r--r--Source/WebCore/webaudio/DynamicsCompressorNode.h58
-rw-r--r--Source/WebCore/webaudio/DynamicsCompressorNode.idl31
-rw-r--r--Source/WebCore/webaudio/HighPass2FilterNode.cpp45
-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.h105
-rw-r--r--Source/WebCore/webaudio/JavaScriptAudioNode.idl40
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.cpp45
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.h53
-rw-r--r--Source/WebCore/webaudio/LowPass2FilterNode.idl35
-rw-r--r--Source/WebCore/webaudio/MediaElementAudioSourceNode.cpp157
-rw-r--r--Source/WebCore/webaudio/MediaElementAudioSourceNode.h76
-rw-r--r--Source/WebCore/webaudio/MediaElementAudioSourceNode.idl32
-rw-r--r--Source/WebCore/webaudio/OfflineAudioCompletionEvent.cpp68
-rw-r--r--Source/WebCore/webaudio/OfflineAudioCompletionEvent.h57
-rw-r--r--Source/WebCore/webaudio/OfflineAudioCompletionEvent.idl32
-rw-r--r--Source/WebCore/webaudio/OfflineAudioDestinationNode.cpp174
-rw-r--r--Source/WebCore/webaudio/OfflineAudioDestinationNode.h78
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyser.cpp299
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyser.h99
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.cpp88
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.h75
-rw-r--r--Source/WebCore/webaudio/RealtimeAnalyserNode.idl48
-rw-r--r--Source/WebCore/webaudio/WaveShaperDSPKernel.cpp76
-rw-r--r--Source/WebCore/webaudio/WaveShaperDSPKernel.h54
-rw-r--r--Source/WebCore/webaudio/WaveShaperNode.cpp54
-rw-r--r--Source/WebCore/webaudio/WaveShaperNode.h54
-rw-r--r--Source/WebCore/webaudio/WaveShaperNode.idl32
-rw-r--r--Source/WebCore/webaudio/WaveShaperProcessor.cpp82
-rw-r--r--Source/WebCore/webaudio/WaveShaperProcessor.h62
104 files changed, 10653 insertions, 0 deletions
diff --git a/Source/WebCore/webaudio/AsyncAudioDecoder.cpp b/Source/WebCore/webaudio/AsyncAudioDecoder.cpp
new file mode 100644
index 000000000..c26824398
--- /dev/null
+++ b/Source/WebCore/webaudio/AsyncAudioDecoder.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011, 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 "AsyncAudioDecoder.h"
+
+#include "AudioBuffer.h"
+#include "AudioBufferCallback.h"
+#include <wtf/ArrayBuffer.h>
+#include <wtf/MainThread.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+AsyncAudioDecoder::AsyncAudioDecoder()
+{
+ // Start worker thread.
+ MutexLocker lock(m_threadCreationMutex);
+ m_threadID = createThread(AsyncAudioDecoder::threadEntry, this, "Audio Decoder");
+}
+
+AsyncAudioDecoder::~AsyncAudioDecoder()
+{
+ m_queue.kill();
+
+ // Stop thread.
+ void* exitCode;
+ waitForThreadCompletion(m_threadID, &exitCode);
+ m_threadID = 0;
+}
+
+void AsyncAudioDecoder::decodeAsync(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
+{
+ ASSERT(isMainThread());
+ ASSERT(audioData);
+ if (!audioData)
+ return;
+
+ OwnPtr<DecodingTask> decodingTask = DecodingTask::create(audioData, sampleRate, successCallback, errorCallback);
+ m_queue.append(decodingTask.release()); // note that ownership of the task is effectively taken by the queue.
+}
+
+// Asynchronously decode in this thread.
+void* AsyncAudioDecoder::threadEntry(void* threadData)
+{
+ ASSERT(threadData);
+ AsyncAudioDecoder* decoder = reinterpret_cast<AsyncAudioDecoder*>(threadData);
+ decoder->runLoop();
+ return 0;
+}
+
+void AsyncAudioDecoder::runLoop()
+{
+ ASSERT(!isMainThread());
+
+ {
+ // Wait for until we have m_threadID established before starting the run loop.
+ MutexLocker lock(m_threadCreationMutex);
+ }
+
+ // Keep running decoding tasks until we're killed.
+ while (OwnPtr<DecodingTask> decodingTask = m_queue.waitForMessage()) {
+ // Let the task take care of its own ownership.
+ // See DecodingTask::notifyComplete() for cleanup.
+ decodingTask.leakPtr()->decode();
+ }
+}
+
+PassOwnPtr<AsyncAudioDecoder::DecodingTask> AsyncAudioDecoder::DecodingTask::create(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
+{
+ return adoptPtr(new DecodingTask(audioData, sampleRate, successCallback, errorCallback));
+}
+
+AsyncAudioDecoder::DecodingTask::DecodingTask(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
+ : m_audioData(audioData)
+ , m_sampleRate(sampleRate)
+ , m_successCallback(successCallback)
+ , m_errorCallback(errorCallback)
+{
+}
+
+void AsyncAudioDecoder::DecodingTask::decode()
+{
+ ASSERT(m_audioData.get());
+ if (!m_audioData.get())
+ return;
+
+ // Do the actual decoding and invoke the callback.
+ m_audioBuffer = AudioBuffer::createFromAudioFileData(m_audioData->data(), m_audioData->byteLength(), false, sampleRate());
+
+ // Decoding is finished, but we need to do the callbacks on the main thread.
+ callOnMainThread(notifyCompleteDispatch, this);
+}
+
+void AsyncAudioDecoder::DecodingTask::notifyCompleteDispatch(void* userData)
+{
+ AsyncAudioDecoder::DecodingTask* task = reinterpret_cast<AsyncAudioDecoder::DecodingTask*>(userData);
+ ASSERT(task);
+ if (!task)
+ return;
+
+ task->notifyComplete();
+}
+
+void AsyncAudioDecoder::DecodingTask::notifyComplete()
+{
+ if (audioBuffer() && successCallback())
+ successCallback()->handleEvent(audioBuffer());
+ else if (errorCallback())
+ errorCallback()->handleEvent(audioBuffer());
+
+ // Our ownership was given up in AsyncAudioDecoder::runLoop()
+ // Make sure to clean up here.
+ delete this;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AsyncAudioDecoder.h b/Source/WebCore/webaudio/AsyncAudioDecoder.h
new file mode 100644
index 000000000..e2fc4dcb0
--- /dev/null
+++ b/Source/WebCore/webaudio/AsyncAudioDecoder.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011, 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 AsyncAudioDecoder_h
+#define AsyncAudioDecoder_h
+
+#include <wtf/Forward.h>
+#include <wtf/MessageQueue.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class AudioBufferCallback;
+
+// AsyncAudioDecoder asynchronously decodes audio file data from an ArrayBuffer in a worker thread.
+// Upon successful decoding, a completion callback will be invoked with the decoded PCM data in an AudioBuffer.
+
+class AsyncAudioDecoder {
+ WTF_MAKE_NONCOPYABLE(AsyncAudioDecoder);
+public:
+ AsyncAudioDecoder();
+ ~AsyncAudioDecoder();
+
+ // Must be called on the main thread.
+ void decodeAsync(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
+
+private:
+ class DecodingTask {
+ WTF_MAKE_NONCOPYABLE(DecodingTask);
+ public:
+ static PassOwnPtr<DecodingTask> create(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
+
+ void decode();
+
+ private:
+ DecodingTask(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
+
+ ArrayBuffer* audioData() { return m_audioData.get(); }
+ float sampleRate() const { return m_sampleRate; }
+ AudioBufferCallback* successCallback() { return m_successCallback.get(); }
+ AudioBufferCallback* errorCallback() { return m_errorCallback.get(); }
+ AudioBuffer* audioBuffer() { return m_audioBuffer.get(); }
+
+ static void notifyCompleteDispatch(void* userData);
+ void notifyComplete();
+
+ RefPtr<ArrayBuffer> m_audioData;
+ float m_sampleRate;
+ RefPtr<AudioBufferCallback> m_successCallback;
+ RefPtr<AudioBufferCallback> m_errorCallback;
+ RefPtr<AudioBuffer> m_audioBuffer;
+ };
+
+ static void* threadEntry(void* threadData);
+ void runLoop();
+
+ WTF::ThreadIdentifier m_threadID;
+ Mutex m_threadCreationMutex;
+ MessageQueue<DecodingTask> m_queue;
+};
+
+} // namespace WebCore
+
+#endif // AsyncAudioDecoder_h
diff --git a/Source/WebCore/webaudio/AudioBasicProcessorNode.cpp b/Source/WebCore/webaudio/AudioBasicProcessorNode.cpp
new file mode 100644
index 000000000..90142f27b
--- /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, float 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 000000000..5a555dafb
--- /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*, float 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 000000000..0515a34a2
--- /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)
+
+#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, float sampleRate)
+{
+ if (sampleRate < 22050 || sampleRate > 96000 || numberOfChannels > 10 || !numberOfFrames)
+ return 0;
+
+ return adoptRef(new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate));
+}
+
+PassRefPtr<AudioBuffer> AudioBuffer::createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float 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, float 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);
+ channelDataArray->setRange(bus->channel(i)->data(), m_length, 0);
+ 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))
+ getChannelData(i)->zeroRange(0, length());
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioBuffer.h b/Source/WebCore/webaudio/AudioBuffer.h
new file mode 100644
index 000000000..4aeadca57
--- /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 <wtf/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, float sampleRate);
+
+ // Returns 0 if data is not a valid audio file.
+ static PassRefPtr<AudioBuffer> createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate);
+
+ // Format
+ size_t length() const { return m_length; }
+ double duration() const { return length() / sampleRate(); }
+ float 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, float sampleRate);
+ AudioBuffer(AudioBus* bus);
+
+ double m_gain; // scalar gain
+ float 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 000000000..8a734e28f
--- /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
+ ] 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/AudioBufferCallback.h b/Source/WebCore/webaudio/AudioBufferCallback.h
new file mode 100644
index 000000000..feebc469b
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferCallback.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011, 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 AudioBufferCallback_h
+#define AudioBufferCallback_h
+
+#if ENABLE(WEB_AUDIO)
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+
+class AudioBufferCallback : public RefCounted<AudioBufferCallback> {
+public:
+ virtual ~AudioBufferCallback() { }
+ virtual bool handleEvent(AudioBuffer*) = 0;
+};
+
+} // namespace
+
+#endif // ENABLE(WEB_AUDIO)
+
+#endif // AudioBufferCallback_h
diff --git a/Source/WebCore/webaudio/AudioBufferCallback.idl b/Source/WebCore/webaudio/AudioBufferCallback.idl
new file mode 100644
index 000000000..f2d311b92
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferCallback.idl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011, 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,
+ Callback
+ ] AudioBufferCallback {
+ boolean handleEvent(in AudioBuffer audioBuffer);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioBufferSourceNode.cpp b/Source/WebCore/webaudio/AudioBufferSourceNode.cpp
new file mode 100644
index 000000000..c8a132295
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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 "Document.h"
+#include "FloatConversion.h"
+#include "ScriptCallStack.h"
+#include <algorithm>
+#include <wtf/MainThread.h>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+const double DefaultGrainDuration = 0.020; // 20ms
+const double UnknownTime = -1;
+
+// Arbitrary upper limit on playback rate.
+// Higher than expected rates can be useful when playing back oversampled buffers
+// to minimize linear interpolation aliasing.
+const double MaxRate = 1024;
+
+PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, float sampleRate)
+{
+ return adoptRef(new AudioBufferSourceNode(context, sampleRate));
+}
+
+AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sampleRate)
+ : AudioSourceNode(context, sampleRate)
+ , m_buffer(0)
+ , m_isPlaying(false)
+ , m_isLooping(false)
+ , m_hasFinished(false)
+ , m_startTime(0.0)
+ , m_endTime(UnknownTime)
+ , m_virtualReadIndex(0)
+ , m_isGrain(false)
+ , m_grainOffset(0.0)
+ , m_grainDuration(DefaultGrainDuration)
+ , m_lastGain(1.0)
+ , m_pannerNode(0)
+{
+ setNodeType(NodeTypeAudioBufferSource);
+
+ m_gain = AudioGain::create("gain", 1.0, 0.0, 1.0);
+ m_playbackRate = AudioParam::create("playbackRate", 1.0, 0.0, MaxRate);
+
+ m_gain->setContext(context);
+ m_playbackRate->setContext(context);
+
+ // 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.
+ float sampleRate = this->sampleRate();
+ double quantumStartTime = context()->currentTime();
+ double quantumEndTime = quantumStartTime + framesToProcess / sampleRate;
+
+ // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle.
+ if (m_endTime != UnknownTime && m_endTime <= quantumStartTime) {
+ m_isPlaying = false;
+ m_virtualReadIndex = 0;
+ finish();
+ }
+
+ 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;
+ }
+
+ double quantumTimeOffset = m_startTime > quantumStartTime ? m_startTime - quantumStartTime : 0;
+ size_t quantumFrameOffset = static_cast<unsigned>(quantumTimeOffset * sampleRate);
+ quantumFrameOffset = min(quantumFrameOffset, framesToProcess); // clamp to valid range
+ size_t bufferFramesToProcess = framesToProcess - quantumFrameOffset;
+
+ // Render by reading directly from the buffer.
+ renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess);
+
+ // Apply the gain (in-place) to the output bus.
+ double totalGain = gain()->value() * m_buffer->gain();
+ outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
+
+ // If the end time is somewhere in the middle of this time quantum, then simply zero out the
+ // frames starting at the end time.
+ if (m_endTime != UnknownTime && m_endTime >= quantumStartTime && m_endTime < quantumEndTime) {
+ size_t zeroStartFrame = narrowPrecisionToFloat((m_endTime - quantumStartTime) * sampleRate);
+ size_t framesToZero = framesToProcess - zeroStartFrame;
+
+ bool isSafe = zeroStartFrame < framesToProcess && framesToZero <= framesToProcess && zeroStartFrame + framesToZero <= framesToProcess;
+ ASSERT(isSafe);
+
+ if (isSafe) {
+ for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
+ memset(outputBus->channel(i)->data() + zeroStartFrame, 0, sizeof(float) * framesToZero);
+ }
+
+ m_isPlaying = false;
+ m_virtualReadIndex = 0;
+ finish();
+ }
+
+ 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();
+ }
+}
+
+// Returns true if we're finished.
+bool AudioBufferSourceNode::renderSilenceAndFinishIfNotLooping(float* destinationL, float* destinationR, size_t framesToProcess)
+{
+ if (!loop()) {
+ // 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);
+ }
+
+ finish();
+ return true;
+ }
+ return false;
+}
+
+void AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destinationFrameOffset, 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();
+
+ bool isStereo = destinationR;
+
+ // Sanity check destinationFrameOffset, numberOfFrames.
+ size_t destinationLength = bus->length();
+
+ bool isLengthGood = destinationLength <= 4096 && numberOfFrames <= 4096;
+ ASSERT(isLengthGood);
+ if (!isLengthGood)
+ return;
+
+ bool isOffsetGood = destinationFrameOffset <= destinationLength && destinationFrameOffset + numberOfFrames <= destinationLength;
+ ASSERT(isOffsetGood);
+ if (!isOffsetGood)
+ return;
+
+ // Potentially zero out initial frames leading up to the offset.
+ if (destinationFrameOffset) {
+ memset(destinationL, 0, sizeof(float) * destinationFrameOffset);
+ if (destinationR)
+ memset(destinationR, 0, sizeof(float) * destinationFrameOffset);
+ }
+
+ // Offset the pointers to the correct offset frame.
+ destinationL += destinationFrameOffset;
+ if (destinationR)
+ destinationR += destinationFrameOffset;
+
+ 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;
+
+ ASSERT(endFrame >= startFrame);
+ if (endFrame < startFrame)
+ return;
+
+ unsigned deltaFrames = endFrame - startFrame;
+
+ // 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_virtualReadIndex >= endFrame)
+ m_virtualReadIndex = startFrame; // reset to start
+
+ // 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;
+
+ double pitchRate = totalPitchRate();
+
+ // Get local copy.
+ double virtualReadIndex = m_virtualReadIndex;
+
+ // Render loop - reading from the source buffer to the destination using linear interpolation.
+ int framesToProcess = numberOfFrames;
+
+ // Optimize for the very common case of playing back with pitchRate == 1.
+ // We can avoid the linear interpolation.
+ if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex)) {
+ unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+ while (framesToProcess > 0) {
+ int framesToEnd = endFrame - readIndex;
+ int framesThisTime = min(framesToProcess, framesToEnd);
+ framesThisTime = max(0, framesThisTime);
+
+ memcpy(destinationL, sourceL + readIndex, sizeof(*sourceL) * framesThisTime);
+ destinationL += framesThisTime;
+ if (isStereo) {
+ memcpy(destinationR, sourceR + readIndex, sizeof(*sourceR) * framesThisTime);
+ destinationR += framesThisTime;
+ }
+
+ readIndex += framesThisTime;
+ framesToProcess -= framesThisTime;
+
+ // Wrap-around.
+ if (readIndex >= endFrame) {
+ readIndex -= deltaFrames;
+ if (renderSilenceAndFinishIfNotLooping(destinationL, destinationR, framesToProcess))
+ break;
+ }
+ }
+ virtualReadIndex = readIndex;
+ } else {
+ while (framesToProcess--) {
+ unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+ double interpolationFactor = virtualReadIndex - readIndex;
+
+ // For linear interpolation we need the next sample-frame too.
+ unsigned readIndex2 = readIndex + 1;
+ if (readIndex2 >= endFrame) {
+ if (loop()) {
+ // Make sure to wrap around at the end of the buffer.
+ readIndex2 -= deltaFrames;
+ } else
+ readIndex2 = readIndex;
+ }
+
+ // Final sanity check on buffer access.
+ // FIXME: as an optimization, try to get rid of this inner-loop check and put assertions and guards before the loop.
+ if (readIndex >= bufferLength || readIndex2 >= bufferLength)
+ break;
+
+ // Linear interpolation.
+ double sampleL1 = sourceL[readIndex];
+ double sampleL2 = sourceL[readIndex2];
+ double sampleL = (1.0 - interpolationFactor) * sampleL1 + interpolationFactor * sampleL2;
+ *destinationL++ = narrowPrecisionToFloat(sampleL);
+
+ if (isStereo) {
+ double sampleR1 = sourceR[readIndex];
+ double sampleR2 = sourceR[readIndex2];
+ double sampleR = (1.0 - interpolationFactor) * sampleR1 + interpolationFactor * sampleR2;
+ *destinationR++ = narrowPrecisionToFloat(sampleR);
+ }
+
+ virtualReadIndex += pitchRate;
+
+ // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
+ if (virtualReadIndex >= endFrame) {
+ virtualReadIndex -= deltaFrames;
+
+ if (renderSilenceAndFinishIfNotLooping(destinationL, destinationR, framesToProcess))
+ break;
+ }
+ }
+ }
+ m_virtualReadIndex = virtualReadIndex;
+}
+
+
+void AudioBufferSourceNode::reset()
+{
+ m_virtualReadIndex = 0;
+ m_lastGain = gain()->value();
+}
+
+void AudioBufferSourceNode::finish()
+{
+ if (!m_hasFinished) {
+ // Let the context dereference this AudioNode.
+ context()->notifyNodeFinishedProcessing(this);
+ m_hasFinished = true;
+ }
+}
+
+bool 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();
+ if (!numberOfChannels || numberOfChannels > 2) {
+ // FIXME: implement multi-channel greater than stereo.
+ return false;
+ }
+ output(0)->setNumberOfChannels(numberOfChannels);
+ }
+
+ m_virtualReadIndex = 0;
+ m_buffer = buffer;
+
+ return true;
+}
+
+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_virtualReadIndex = 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_isGrain = true;
+ m_startTime = when;
+
+ // We call floor() here since at playbackRate == 1 we don't want to go through linear interpolation
+ // at a sub-sample position since it will degrade the quality.
+ // When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer.
+ // Since playbackRate == 1 is very common, it's worth considering quality.
+ m_virtualReadIndex = floor(m_grainOffset * buffer()->sampleRate());
+
+ m_isPlaying = true;
+}
+
+void AudioBufferSourceNode::noteOff(double when)
+{
+ ASSERT(isMainThread());
+ if (!m_isPlaying)
+ return;
+
+ when = max(0.0, when);
+ m_endTime = when;
+}
+
+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);
+ if (!totalRate)
+ totalRate = 1; // zero rate is considered illegal
+ totalRate = min(MaxRate, totalRate);
+
+ bool isTotalRateValid = !isnan(totalRate) && !isinf(totalRate);
+ ASSERT(isTotalRateValid);
+ if (!isTotalRateValid)
+ totalRate = 1.0;
+
+ return totalRate;
+}
+
+bool AudioBufferSourceNode::looping()
+{
+ static bool firstTime = true;
+ if (firstTime && context() && context()->document()) {
+ context()->document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
+ firstTime = false;
+ }
+
+ return m_isLooping;
+}
+
+void AudioBufferSourceNode::setLooping(bool looping)
+{
+ static bool firstTime = true;
+ if (firstTime && context() && context()->document()) {
+ context()->document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
+ firstTime = false;
+ }
+
+ m_isLooping = looping;
+}
+
+} // 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 000000000..777ba89e3
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.h
@@ -0,0 +1,146 @@
+/*
+ * 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 "AudioSourceNode.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:
+ static PassRefPtr<AudioBufferSourceNode> create(AudioContext*, float sampleRate);
+
+ virtual ~AudioBufferSourceNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+ // setBuffer() is called on the main thread. This is the buffer we use for playback.
+ // returns true on success.
+ bool 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);
+
+ // Note: the attribute was originally exposed as .looping, but to be more consistent in naming with <audio>
+ // and with how it's described in the specification, the proper attribute name is .loop
+ // The old attribute is kept for backwards compatibility.
+ bool loop() const { return m_isLooping; }
+ void setLoop(bool looping) { m_isLooping = looping; }
+
+ // Deprecated.
+ bool looping();
+ void setLooping(bool);
+
+ 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*, float sampleRate);
+
+ void renderFromBuffer(AudioBus*, unsigned destinationFrameOffset, size_t numberOfFrames);
+
+ inline bool renderSilenceAndFinishIfNotLooping(float* destinationL, float* destinationR, size_t framesToProcess);
+
+ // 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_endTime is the time to stop playing based on the context's timeline (0.0 or a time less than the context's current time means "now").
+ // If it hasn't been set explicitly, then the sound will not stop playing (if looping) or will stop when the end of the AudioBuffer
+ // has been reached.
+ double m_endTime; // in seconds
+
+ // m_virtualReadIndex is a sample-frame index into our buffer representing the current playback position.
+ // Since it's floating-point, it has sub-sample accuracy.
+ double m_virtualReadIndex;
+
+ // Granular playback
+ bool m_isGrain;
+ double m_grainOffset; // in seconds
+ double m_grainDuration; // in seconds
+
+ // 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_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;
+
+ // Handles the time when we reach the end of sample data (non-looping) or the noteOff() time has been reached.
+ void finish();
+};
+
+} // namespace WebCore
+
+#endif // AudioBufferSourceNode_h
diff --git a/Source/WebCore/webaudio/AudioBufferSourceNode.idl b/Source/WebCore/webaudio/AudioBufferSourceNode.idl
new file mode 100644
index 000000000..c39ec7524
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioBufferSourceNode.idl
@@ -0,0 +1,44 @@
+/*
+ * 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 [CustomSetter] AudioBuffer buffer
+ setter raises (DOMException);
+
+ readonly attribute AudioGain gain;
+ readonly attribute AudioParam playbackRate;
+
+ attribute boolean loop; // This is the proper attribute name from the specification.
+ attribute boolean looping; // This is an alias for the .loop attribute for backwards compatibility.
+
+ 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 000000000..51e785586
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelMerger.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "AudioContext.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, float 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)));
+
+ setNodeType(NodeTypeChannelMerger);
+
+ initialize();
+}
+
+void AudioChannelMerger::process(size_t framesToProcess)
+{
+ AudioNodeOutput* output = this->output(0);
+ ASSERT(output);
+ ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus()->length());
+
+ // Merge all the channels from all the inputs 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 == output->numberOfChannels());
+}
+
+void AudioChannelMerger::reset()
+{
+}
+
+// Any time a connection or disconnection happens on any of our inputs, we potentially need to change the
+// number of channels of our output.
+void AudioChannelMerger::checkNumberOfChannelsForInput(AudioNodeInput*)
+{
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+
+ // 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
+ AudioNodeOutput* output = this->output(0);
+ ASSERT(output);
+ output->setNumberOfChannels(numberOfOutputChannels);
+}
+
+} // 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 000000000..e773daeb9
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioChannelMerger.h
@@ -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.
+ * 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, float sampleRate)
+ {
+ return adoptRef(new AudioChannelMerger(context, sampleRate));
+ }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+ // Called in the audio thread (pre-rendering task) when the number of channels for an input may have changed.
+ virtual void checkNumberOfChannelsForInput(AudioNodeInput*);
+
+private:
+ AudioChannelMerger(AudioContext*, float 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 000000000..3862af9d1
--- /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 000000000..e5cc72a86
--- /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, float 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)));
+
+ setNodeType(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 000000000..71b0ef415
--- /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, float sampleRate)
+ {
+ return adoptRef(new AudioChannelSplitter(context, sampleRate));
+ }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+private:
+ AudioChannelSplitter(AudioContext*, float 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 000000000..076c0510e
--- /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 000000000..dca96063a
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.cpp
@@ -0,0 +1,788 @@
+/*
+ * 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 "AsyncAudioDecoder.h"
+#include "AudioBuffer.h"
+#include "AudioBufferCallback.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 "BiquadFilterNode.h"
+#include "ConvolverNode.h"
+#include "DefaultAudioDestinationNode.h"
+#include "DelayNode.h"
+#include "Document.h"
+#include "DynamicsCompressorNode.h"
+#include "ExceptionCode.h"
+#include "FFTFrame.h"
+#include "HRTFDatabaseLoader.h"
+#include "HRTFPanner.h"
+#include "HighPass2FilterNode.h"
+#include "JavaScriptAudioNode.h"
+#include "LowPass2FilterNode.h"
+#include "OfflineAudioCompletionEvent.h"
+#include "OfflineAudioDestinationNode.h"
+#include "PlatformString.h"
+#include "RealtimeAnalyserNode.h"
+#include "WaveShaperNode.h"
+#include "ScriptCallStack.h"
+
+#if ENABLE(VIDEO)
+#include "HTMLMediaElement.h"
+#include "MediaElementAudioSourceNode.h"
+#endif
+
+#if DEBUG_AUDIONODE_REFERENCES
+#include <stdio.h>
+#endif
+
+#include <wtf/ArrayBuffer.h>
+#include <wtf/MainThread.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 {
+
+namespace {
+
+bool isSampleRateRangeGood(float sampleRate)
+{
+ // FIXME: It would be nice if the minimum sample-rate could be less than 44.1KHz,
+ // but that will require some fixes in HRTFPanner::fftSizeForSampleRate(), and some testing there.
+ return sampleRate >= 44100 && sampleRate <= 96000;
+}
+
+}
+
+// Don't allow more than this number of simultaneous AudioContexts talking to hardware.
+const unsigned MaxHardwareContexts = 4;
+unsigned AudioContext::s_hardwareContextCount = 0;
+
+PassRefPtr<AudioContext> AudioContext::create(Document* document)
+{
+ ASSERT(document);
+ ASSERT(isMainThread());
+ if (s_hardwareContextCount >= MaxHardwareContexts)
+ return 0;
+
+ return adoptRef(new AudioContext(document));
+}
+
+PassRefPtr<AudioContext> AudioContext::createOfflineContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode& ec)
+{
+ ASSERT(document);
+
+ // FIXME: offline contexts have limitations on supported sample-rates.
+ // Currently all AudioContexts must have the same sample-rate.
+ HRTFDatabaseLoader* loader = HRTFDatabaseLoader::loader();
+ if (numberOfChannels > 10 || !isSampleRateRangeGood(sampleRate) || (loader && loader->databaseSampleRate() != sampleRate)) {
+ ec = SYNTAX_ERR;
+ return 0;
+ }
+
+ return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames, sampleRate));
+}
+
+// Constructor for rendering to the audio hardware.
+AudioContext::AudioContext(Document* document)
+ : ActiveDOMObject(document, this)
+ , m_isInitialized(false)
+ , m_isAudioThreadFinished(false)
+ , m_document(document)
+ , m_destinationNode(0)
+ , m_isDeletionScheduled(false)
+ , m_connectionCount(0)
+ , m_audioThread(0)
+ , m_graphOwnerThread(UndefinedThreadIdentifier)
+ , m_isOfflineContext(false)
+{
+ constructCommon();
+
+ m_destinationNode = DefaultAudioDestinationNode::create(this);
+
+ // 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());
+}
+
+// Constructor for offline (non-realtime) rendering.
+AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
+ : ActiveDOMObject(document, this)
+ , m_isInitialized(false)
+ , m_isAudioThreadFinished(false)
+ , m_document(document)
+ , m_destinationNode(0)
+ , m_connectionCount(0)
+ , m_audioThread(0)
+ , m_graphOwnerThread(UndefinedThreadIdentifier)
+ , m_isOfflineContext(true)
+{
+ constructCommon();
+
+ // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton.
+ m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate);
+
+ // Create a new destination for offline rendering.
+ m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
+ m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get());
+}
+
+void AudioContext::constructCommon()
+{
+ FFTFrame::initialize();
+
+ m_listener = AudioListener::create();
+ m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
+ m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
+}
+
+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()) {
+ m_destinationNode->initialize();
+
+ if (!isOfflineContext()) {
+ // 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".
+ // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript.
+ // We may want to consider requiring it for symmetry with OfflineAudioContext.
+ m_destinationNode->startRendering();
+ ++s_hardwareContextCount;
+ }
+
+ }
+ m_isInitialized = true;
+ }
+ }
+}
+
+void AudioContext::uninitialize()
+{
+ ASSERT(isMainThread());
+
+ if (m_isInitialized) {
+ // Protect this object from being deleted before we finish uninitializing.
+ RefPtr<AudioContext> protect(this);
+
+ // 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();
+
+ if (!isOfflineContext()) {
+ ASSERT(s_hardwareContextCount);
+ --s_hardwareContextCount;
+ }
+
+ // Get rid of the sources which may still be playing.
+ derefUnfinishedSourceNodes();
+
+ deleteMarkedNodes();
+
+ // 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::uninitializeDispatch(void* userData)
+{
+ AudioContext* context = reinterpret_cast<AudioContext*>(userData);
+ ASSERT(context);
+ if (!context)
+ return;
+
+ context->uninitialize();
+}
+
+void AudioContext::stop()
+{
+ m_document = 0; // document is going away
+
+ // Don't call uninitialize() immediately here because the ScriptExecutionContext is in the middle
+ // of dealing with all of its ActiveDOMObjects at this point. uninitialize() can de-reference other
+ // ActiveDOMObjects so let's schedule uninitialize() to be called later.
+ // FIXME: see if there's a more direct way to handle this issue.
+ callOnMainThread(uninitializeDispatch, this);
+}
+
+Document* AudioContext::document() const
+{
+ 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, float sampleRate, ExceptionCode& ec)
+{
+ RefPtr<AudioBuffer> audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
+ if (!audioBuffer.get()) {
+ ec = SYNTAX_ERR;
+ return 0;
+ }
+
+ return audioBuffer;
+}
+
+PassRefPtr<AudioBuffer> AudioContext::createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono, ExceptionCode& ec)
+{
+ ASSERT(arrayBuffer);
+ if (!arrayBuffer) {
+ ec = SYNTAX_ERR;
+ return 0;
+ }
+
+ RefPtr<AudioBuffer> audioBuffer = AudioBuffer::createFromAudioFileData(arrayBuffer->data(), arrayBuffer->byteLength(), mixToMono, sampleRate());
+ if (!audioBuffer.get()) {
+ ec = SYNTAX_ERR;
+ return 0;
+ }
+
+ return audioBuffer;
+}
+
+void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback, ExceptionCode& ec)
+{
+ if (!audioData) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+ m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
+}
+
+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;
+}
+
+#if ENABLE(VIDEO)
+PassRefPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionCode& ec)
+{
+ ASSERT(mediaElement);
+ if (!mediaElement) {
+ ec = INVALID_STATE_ERR;
+ return 0;
+ }
+
+ ASSERT(isMainThread());
+ lazyInitialize();
+
+ // First check if this media element already has a source node.
+ if (mediaElement->audioSourceNode()) {
+ ec = INVALID_STATE_ERR;
+ return 0;
+ }
+
+ RefPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement);
+
+ mediaElement->setAudioSourceNode(node.get());
+
+ refNode(node.get()); // context keeps reference until node is disconnected
+ return node;
+}
+#endif
+
+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<BiquadFilterNode> AudioContext::createBiquadFilter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<WaveShaperNode> AudioContext::createWaveShaper()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return WaveShaperNode::create(this);
+}
+
+PassRefPtr<LowPass2FilterNode> AudioContext::createLowPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ if (document())
+ document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "createLowPass2Filter() is deprecated. Use createBiquadFilter() instead.");
+
+ return LowPass2FilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<HighPass2FilterNode> AudioContext::createHighPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ if (document())
+ document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "createHighPass2Filter() is deprecated. Use createBiquadFilter() instead.");
+
+ 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<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return DynamicsCompressorNode::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();
+
+ // Don't delete in the real-time thread. Let the main thread do it.
+ // Ref-counted objects held by certain AudioNodes may not be thread-safe.
+ scheduleNodeDeletion();
+
+ // 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::scheduleNodeDeletion()
+{
+ bool isGood = m_isInitialized && isGraphOwner();
+ ASSERT(isGood);
+ if (!isGood)
+ return;
+
+ // Make sure to call deleteMarkedNodes() on main thread.
+ if (m_nodesToDelete.size() && !m_isDeletionScheduled) {
+ m_isDeletionScheduled = true;
+
+ // Don't let ourself get deleted before the callback.
+ // See matching deref() in deleteMarkedNodesDispatch().
+ ref();
+ callOnMainThread(deleteMarkedNodesDispatch, this);
+ }
+}
+
+void AudioContext::deleteMarkedNodesDispatch(void* userData)
+{
+ AudioContext* context = reinterpret_cast<AudioContext*>(userData);
+ ASSERT(context);
+ if (!context)
+ return;
+
+ context->deleteMarkedNodes();
+ context->deref();
+}
+
+void AudioContext::deleteMarkedNodes()
+{
+ ASSERT(isMainThread());
+
+ AutoLocker locker(this);
+
+ // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
+ 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;
+ }
+
+ m_isDeletionScheduled = false;
+}
+
+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();
+}
+
+const AtomicString& AudioContext::interfaceName() const
+{
+ return eventNames().interfaceForAudioContext;
+}
+
+ScriptExecutionContext* AudioContext::scriptExecutionContext() const
+{
+ return document();
+}
+
+void AudioContext::startRendering()
+{
+ destination()->startRendering();
+}
+
+void AudioContext::fireCompletionEvent()
+{
+ ASSERT(isMainThread());
+ if (!isMainThread())
+ return;
+
+ AudioBuffer* renderedBuffer = m_renderTarget.get();
+
+ ASSERT(renderedBuffer);
+ if (!renderedBuffer)
+ return;
+
+ // Avoid firing the event if the document has already gone away.
+ if (hasDocument()) {
+ // Call the offline rendering completion event listener.
+ dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
+ }
+}
+
+} // 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 000000000..c4eec08c0
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.h
@@ -0,0 +1,321 @@
+/*
+ * 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 "AsyncAudioDecoder.h"
+#include "AudioBus.h"
+#include "AudioDestinationNode.h"
+#include "EventListener.h"
+#include "EventTarget.h"
+#include "HRTFDatabaseLoader.h"
+#include <wtf/HashSet.h>
+#include <wtf/MainThread.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class AudioBufferCallback;
+class AudioBufferSourceNode;
+class MediaElementAudioSourceNode;
+class HTMLMediaElement;
+class AudioChannelMerger;
+class AudioChannelSplitter;
+class AudioGainNode;
+class AudioPannerNode;
+class AudioListener;
+class BiquadFilterNode;
+class DelayNode;
+class Document;
+class LowPass2FilterNode;
+class HighPass2FilterNode;
+class ConvolverNode;
+class DynamicsCompressorNode;
+class RealtimeAnalyserNode;
+class WaveShaperNode;
+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 ThreadSafeRefCounted<AudioContext>, public EventTarget {
+public:
+ // Create an AudioContext for rendering to the audio hardware.
+ static PassRefPtr<AudioContext> create(Document*);
+
+ // Create an AudioContext for offline (non-realtime) rendering.
+ static PassRefPtr<AudioContext> createOfflineContext(Document*, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode&);
+
+ virtual ~AudioContext();
+
+ bool isInitialized() const;
+
+ bool isOfflineContext() { return m_isOfflineContext; }
+
+ // Returns true when initialize() was called AND all asynchronous initialization has completed.
+ bool isRunnable() const;
+
+ // Document notification
+ virtual void stop();
+
+ Document* document() const; // ASSERTs if document no longer exists.
+ bool hasDocument();
+
+ AudioDestinationNode* destination() { return m_destinationNode.get(); }
+ double currentTime() { return m_destinationNode->currentTime(); }
+ float sampleRate() { return m_destinationNode->sampleRate(); }
+
+ PassRefPtr<AudioBuffer> createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode&);
+ PassRefPtr<AudioBuffer> createBuffer(ArrayBuffer*, bool mixToMono, ExceptionCode&);
+
+ // Asynchronous audio file data decoding.
+ void decodeAudioData(ArrayBuffer*, PassRefPtr<AudioBufferCallback>, PassRefPtr<AudioBufferCallback>, ExceptionCode& ec);
+
+ // 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();
+#if ENABLE(VIDEO)
+ PassRefPtr<MediaElementAudioSourceNode> createMediaElementSource(HTMLMediaElement*, ExceptionCode&);
+#endif
+ PassRefPtr<AudioGainNode> createGainNode();
+ PassRefPtr<BiquadFilterNode> createBiquadFilter();
+ PassRefPtr<WaveShaperNode> createWaveShaper();
+ PassRefPtr<DelayNode> createDelayNode();
+ PassRefPtr<LowPass2FilterNode> createLowPass2Filter();
+ PassRefPtr<HighPass2FilterNode> createHighPass2Filter();
+ PassRefPtr<AudioPannerNode> createPanner();
+ PassRefPtr<ConvolverNode> createConvolver();
+ PassRefPtr<DynamicsCompressorNode> createDynamicsCompressor();
+ 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 schedule deletion of all marked nodes at the end of each realtime render quantum.
+ 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*);
+
+ // EventTarget
+ virtual const AtomicString& interfaceName() const;
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ virtual EventTargetData* eventTargetData() { return &m_eventTargetData; }
+ virtual EventTargetData* ensureEventTargetData() { return &m_eventTargetData; }
+
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
+
+ // Reconcile ref/deref which are defined both in ThreadSafeRefCounted and EventTarget.
+ using ThreadSafeRefCounted<AudioContext>::ref;
+ using ThreadSafeRefCounted<AudioContext>::deref;
+
+ void startRendering();
+ void fireCompletionEvent();
+
+ static unsigned s_hardwareContextCount;
+
+private:
+ AudioContext(Document*);
+ AudioContext(Document*, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
+ void constructCommon();
+
+ void lazyInitialize();
+ void uninitialize();
+ static void uninitializeDispatch(void* userData);
+
+ void scheduleNodeDeletion();
+ static void deleteMarkedNodesDispatch(void* userData);
+
+ 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 here.
+ // They will be scheduled for deletion (on the main thread) at the end of a render cycle (in realtime thread).
+ Vector<AudioNode*> m_nodesToDelete;
+ bool m_isDeletionScheduled;
+
+ // 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;
+
+ // EventTarget
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+ EventTargetData m_eventTargetData;
+
+ RefPtr<AudioBuffer> m_renderTarget;
+
+ bool m_isOfflineContext;
+
+ AsyncAudioDecoder m_audioDecoder;
+};
+
+} // WebCore
+
+#endif // AudioContext_h
diff --git a/Source/WebCore/webaudio/AudioContext.idl b/Source/WebCore/webaudio/AudioContext.idl
new file mode 100644
index 000000000..10f9c9062
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioContext.idl
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010, Google Inc. All rights reserved.
+ * Copyright (C) 2011 Apple 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,
+ ActiveDOMObject,
+ CustomConstructor,
+ CustomMarkFunction,
+#if defined(V8_BINDING) && V8_BINDING
+ EventTarget
+#endif
+ ] 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)
+ raises(DOMException);
+ AudioBuffer createBuffer(in ArrayBuffer buffer, in boolean mixToMono)
+ raises(DOMException);
+
+ // Asynchronous audio file data decoding.
+ void decodeAudioData(in ArrayBuffer audioData, in [Callback] AudioBufferCallback successCallback, in [Optional, Callback] AudioBufferCallback errorCallback)
+ raises(DOMException);
+
+ // Sources
+ AudioBufferSourceNode createBufferSource();
+#if defined(ENABLE_VIDEO) && ENABLE_VIDEO
+ MediaElementAudioSourceNode createMediaElementSource(in HTMLMediaElement mediaElement)
+ raises(DOMException);
+#endif
+ // Processing nodes
+ AudioGainNode createGainNode();
+ DelayNode createDelayNode();
+ BiquadFilterNode createBiquadFilter();
+ WaveShaperNode createWaveShaper();
+ LowPass2FilterNode createLowPass2Filter();
+ HighPass2FilterNode createHighPass2Filter();
+ AudioPannerNode createPanner();
+ ConvolverNode createConvolver();
+ DynamicsCompressorNode createDynamicsCompressor();
+ RealtimeAnalyserNode createAnalyser();
+ JavaScriptAudioNode createJavaScriptNode(in unsigned long bufferSize);
+
+ // Channel splitting and merging
+ AudioChannelSplitter createChannelSplitter();
+ AudioChannelMerger createChannelMerger();
+
+ // Offline rendering
+ // void prepareOfflineBufferRendering(in unsigned long numberOfChannels, in unsigned long numberOfFrames, in float sampleRate);
+ attribute EventListener oncomplete;
+ void startRendering();
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioDestinationNode.cpp b/Source/WebCore/webaudio/AudioDestinationNode.cpp
new file mode 100644
index 000000000..ff9ebbd32
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioDestinationNode.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 "DenormalDisabler.h"
+
+namespace WebCore {
+
+AudioDestinationNode::AudioDestinationNode(AudioContext* context, float sampleRate)
+ : AudioNode(context, sampleRate)
+ , m_currentTime(0.0)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+
+ setNodeType(NodeTypeDestination);
+}
+
+AudioDestinationNode::~AudioDestinationNode()
+{
+ uninitialize();
+}
+
+// The audio hardware calls us back here to gets its input stream.
+void AudioDestinationNode::provideInput(AudioBus* destinationBus, size_t numberOfFrames)
+{
+ // We don't want denormals slowing down any of the audio processing
+ // since they can very seriously hurt performance.
+ // This will take care of all AudioNodes because they all process within this scope.
+ DenormalDisabler denormalDisabler;
+
+ 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 000000000..d7bc7bc12
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioDestinationNode.h
@@ -0,0 +1,63 @@
+/*
+ * 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 "AudioBuffer.h"
+#include "AudioNode.h"
+#include "AudioSourceProvider.h"
+
+namespace WebCore {
+
+class AudioBus;
+class AudioContext;
+
+class AudioDestinationNode : public AudioNode, public AudioSourceProvider {
+public:
+ AudioDestinationNode(AudioContext*, float sampleRate);
+ 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; };
+
+ // The audio hardware calls here periodically to gets its input stream.
+ virtual void provideInput(AudioBus*, size_t numberOfFrames);
+
+ double currentTime() { return m_currentTime; }
+
+ virtual float sampleRate() const = 0;
+
+ virtual unsigned numberOfChannels() const { return 2; } // FIXME: update when multi-channel (more than stereo) is supported
+
+ virtual void startRendering() = 0;
+
+protected:
+ 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 000000000..d7bf09f83
--- /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 000000000..eb3c52d87
--- /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 000000000..ead7c9ab5
--- /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 000000000..2129c85d6
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGainNode.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 "AudioGainNode.h"
+
+#include "AudioBus.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+
+namespace WebCore {
+
+AudioGainNode::AudioGainNode(AudioContext* context, float sampleRate)
+ : AudioNode(context, sampleRate)
+ , m_lastGain(1.0)
+ , m_sampleAccurateGainValues(AudioNode::ProcessingSizeInFrames) // FIXME: can probably share temp buffer in context
+{
+ m_gain = AudioGain::create("gain", 1.0, 0.0, 1.0);
+ m_gain->setContext(context);
+
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+ setNodeType(NodeTypeGain);
+
+ initialize();
+}
+
+void AudioGainNode::process(size_t framesToProcess)
+{
+ // FIXME: for some cases 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();
+
+ if (gain()->hasTimelineValues()) {
+ // Apply sample-accurate gain scaling for precise envelopes, grain windows, etc.
+ ASSERT(framesToProcess <= m_sampleAccurateGainValues.size());
+ if (framesToProcess <= m_sampleAccurateGainValues.size()) {
+ float* gainValues = m_sampleAccurateGainValues.data();
+ gain()->calculateSampleAccurateValues(gainValues, framesToProcess);
+ outputBus->copyWithSampleAccurateGainValuesFrom(*inputBus, gainValues, framesToProcess);
+ }
+ } else {
+ // 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 000000000..e6342128c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioGainNode.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 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, float 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*, float sampleRate);
+
+ double m_lastGain; // for de-zippering
+ RefPtr<AudioGain> m_gain;
+
+ AudioFloatArray m_sampleAccurateGainValues;
+
+ // 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 000000000..3d4f40faa
--- /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 000000000..2f1ef764d
--- /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, -1)
+ , m_upVector(0, 1, 0)
+ , m_velocity(0, 0, 0)
+ , m_dopplerFactor(1)
+ , 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 000000000..8b5d8ad1a
--- /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(float x, float y, float z) { setPosition(FloatPoint3D(x, y, z)); }
+ void setPosition(const FloatPoint3D &position) { m_position = position; }
+ const FloatPoint3D& position() const { return m_position; }
+
+ // Orientation
+ void setOrientation(float x, float y, float z, float upX, float upY, float 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(float x, float y, float 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 000000000..cf6d8cfce
--- /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 000000000..e2a3b783f
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNode.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 "ExceptionCode.h"
+#include <wtf/Atomics.h>
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+AudioNode::AudioNode(AudioContext* context, float sampleRate)
+ : m_isInitialized(false)
+ , m_nodeType(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[nodeType()];
+ printf("%p: %d: AudioNode::~AudioNode() %d %d %d\n", this, nodeType(), m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+}
+
+void AudioNode::initialize()
+{
+ m_isInitialized = true;
+}
+
+void AudioNode::uninitialize()
+{
+ m_isInitialized = false;
+}
+
+void AudioNode::setNodeType(NodeType type)
+{
+ m_nodeType = 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)
+{
+ if (i < m_inputs.size())
+ return m_inputs[i].get();
+ return 0;
+}
+
+AudioNodeOutput* AudioNode::output(unsigned i)
+{
+ if (i < m_outputs.size())
+ return m_outputs[i].get();
+ return 0;
+}
+
+void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionCode& ec)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ if (!destination) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ // Sanity check input and output indices.
+ if (outputIndex >= numberOfOutputs()) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+
+ if (destination && inputIndex >= destination->numberOfInputs()) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+
+ if (context() != destination->context()) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ AudioNodeInput* input = destination->input(inputIndex);
+ AudioNodeOutput* output = this->output(outputIndex);
+ input->connect(output);
+
+ // Let context know that a connection has been made.
+ context()->incrementConnectionCount();
+}
+
+void AudioNode::disconnect(unsigned outputIndex, ExceptionCode& ec)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ // Sanity check input and output indices.
+ if (outputIndex >= numberOfOutputs()) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+
+ AudioNodeOutput* output = this->output(outputIndex);
+ output->disconnectAllInputs();
+}
+
+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, nodeType(), refType, m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+
+ // See the disabling code in finishDeref() below. This handles the case where a node
+ // is being re-connected after being used at least once and disconnected.
+ // In this case, we need to re-enable.
+ if (m_isDisabled && m_connectionRefCount > 0 && refType == RefTypeConnection) {
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ m_isDisabled = false;
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->enable();
+ }
+}
+
+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, nodeType(), 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 (nodeType() != NodeTypeConvolver && nodeType() != 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 000000000..d1325e1db
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioNode.h
@@ -0,0 +1,176 @@
+/*
+ * 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;
+
+typedef int ExceptionCode;
+
+// 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*, float sampleRate);
+ virtual ~AudioNode();
+
+ AudioContext* context() { return m_context.get(); }
+
+ enum NodeType {
+ NodeTypeUnknown,
+ NodeTypeDestination,
+ NodeTypeAudioBufferSource,
+ NodeTypeMediaElementAudioSource,
+ NodeTypeJavaScript,
+ NodeTypeBiquadFilter,
+ NodeTypeLowPass2Filter,
+ NodeTypeHighPass2Filter,
+ NodeTypePanner,
+ NodeTypeConvolver,
+ NodeTypeDelay,
+ NodeTypeGain,
+ NodeTypeChannelSplitter,
+ NodeTypeChannelMerger,
+ NodeTypeAnalyser,
+ NodeTypeDynamicsCompressor,
+ NodeTypeWaveShaper,
+ NodeTypeEnd
+ };
+
+ NodeType nodeType() const { return m_nodeType; }
+ void setNodeType(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);
+
+ // Called from main thread by corresponding JavaScript methods.
+ void connect(AudioNode*, unsigned outputIndex, unsigned inputIndex, ExceptionCode&);
+ void disconnect(unsigned outputIndex, ExceptionCode&);
+
+ float 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_nodeType;
+ RefPtr<AudioContext> m_context;
+ float 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 000000000..06f873e3a
--- /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;
+
+ void connect(in AudioNode destination, in [Optional=CallWithDefaultValue] unsigned long output, in [Optional=CallWithDefaultValue] unsigned long input)
+ raises(DOMException);
+
+ void disconnect(in [Optional=CallWithDefaultValue] unsigned long output)
+ raises(DOMException);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioNodeInput.cpp b/Source/WebCore/webaudio/AudioNodeInput.cpp
new file mode 100644
index 000000000..9fd1852e0
--- /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 000000000..1d90986f9
--- /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 000000000..4c777e6bd
--- /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 000000000..7114b389d
--- /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 000000000..faf7f159e
--- /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, float 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);
+
+ setNodeType(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);
+
+ float 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->nodeType() == 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 000000000..6ecebeaae
--- /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, float 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*, float 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 000000000..2db093d7b
--- /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 000000000..be9279fdf
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "AudioNode.h"
+#include "AudioUtilities.h"
+#include "FloatConversion.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+const double AudioParam::DefaultSmoothingConstant = 0.05;
+const double AudioParam::SnapThreshold = 0.001;
+
+float AudioParam::value()
+{
+ // Update value for timeline.
+ if (context() && context()->isAudioThread()) {
+ bool hasValue;
+ float timelineValue = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), hasValue);
+
+ if (hasValue)
+ m_value = timelineValue;
+ }
+
+ return narrowPrecisionToFloat(m_value);
+}
+
+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;
+}
+
+float AudioParam::smoothedValue()
+{
+ return narrowPrecisionToFloat(m_smoothedValue);
+}
+
+bool AudioParam::smooth()
+{
+ // If values have been explicitly scheduled on the timeline, then use the exact value.
+ // Smoothing effectively is performed by the timeline.
+ bool useTimelineValue = false;
+ if (context())
+ m_value = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), useTimelineValue);
+
+ if (m_smoothedValue == m_value) {
+ // Smoothed value has already approached and snapped to value.
+ return true;
+ }
+
+ if (useTimelineValue)
+ m_smoothedValue = m_value;
+ else {
+ // Dezipper - 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;
+}
+
+void AudioParam::calculateSampleAccurateValues(float* values, unsigned numberOfValues)
+{
+ bool isSafe = context() && context()->isAudioThread() && values;
+ ASSERT(isSafe);
+ if (!isSafe)
+ return;
+
+ // Calculate values for this render quantum.
+ // Normally numberOfValues will equal AudioNode::ProcessingSizeInFrames (the render quantum size).
+ float sampleRate = context()->sampleRate();
+ float startTime = narrowPrecisionToFloat(context()->currentTime());
+ float endTime = startTime + numberOfValues / sampleRate;
+
+ // Note we're running control rate at the sample-rate.
+ // Pass in the current value as default value.
+ m_value = m_timeline.valuesForTimeRange(startTime, endTime, narrowPrecisionToFloat(m_value), values, numberOfValues, sampleRate, sampleRate);
+}
+
+} // 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 000000000..2758487da
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.h
@@ -0,0 +1,123 @@
+/*
+ * 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 "AudioContext.h"
+#include "AudioParamTimeline.h"
+#include "PlatformString.h"
+#include <sys/types.h>
+#include <wtf/Float32Array.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)
+ {
+ }
+
+ void setContext(AudioContext* context) { m_context = context; }
+ AudioContext* context() { return m_context.get(); }
+
+ float 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();
+
+ // 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; }
+
+ // Parameter automation.
+ void setValueAtTime(float value, float time) { m_timeline.setValueAtTime(value, time); }
+ void linearRampToValueAtTime(float value, float time) { m_timeline.linearRampToValueAtTime(value, time); }
+ void exponentialRampToValueAtTime(float value, float time) { m_timeline.exponentialRampToValueAtTime(value, time); }
+ void setTargetValueAtTime(float targetValue, float time, float timeConstant) { m_timeline.setTargetValueAtTime(targetValue, time, timeConstant); }
+ void setValueCurveAtTime(Float32Array* curve, float time, float duration) { m_timeline.setValueCurveAtTime(curve, time, duration); }
+ void cancelScheduledValues(float startTime) { m_timeline.cancelScheduledValues(startTime); }
+
+ bool hasTimelineValues() { return m_timeline.hasValues(); }
+
+ // Calculates numberOfValues parameter values starting at the context's current time.
+ // Must be called in the context's render thread.
+ void calculateSampleAccurateValues(float* values, unsigned numberOfValues);
+
+private:
+ RefPtr<AudioContext> m_context;
+ 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;
+
+ AudioParamTimeline m_timeline;
+};
+
+} // namespace WebCore
+
+#endif // AudioParam_h
diff --git a/Source/WebCore/webaudio/AudioParam.idl b/Source/WebCore/webaudio/AudioParam.idl
new file mode 100644
index 000000000..d9946f839
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParam.idl
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+ // Parameter automation.
+ void setValueAtTime(in float value, in float time);
+ void linearRampToValueAtTime(in float value, in float time);
+ void exponentialRampToValueAtTime(in float value, in float time);
+
+ // Exponentially approach the target value with a rate having the given time constant.
+ void setTargetValueAtTime(in float targetValue, in float time, in float timeConstant);
+
+ // Sets an array of arbitrary parameter values starting at time for the given duration.
+ // The number of values will be scaled to fit into the desired duration.
+ void setValueCurveAtTime(in Float32Array values, in float time, in float duration);
+
+ // Cancels all scheduled parameter changes with times greater than or equal to startTime.
+ void cancelScheduledValues(in float startTime);
+ };
+}
diff --git a/Source/WebCore/webaudio/AudioParamTimeline.cpp b/Source/WebCore/webaudio/AudioParamTimeline.cpp
new file mode 100644
index 000000000..30d6192c8
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParamTimeline.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2011 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 "AudioParamTimeline.h"
+
+#include "AudioUtilities.h"
+#include "FloatConversion.h"
+#include <algorithm>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+void AudioParamTimeline::setValueAtTime(float value, float time)
+{
+ insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, 0));
+}
+
+void AudioParamTimeline::linearRampToValueAtTime(float value, float time)
+{
+ insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, 0));
+}
+
+void AudioParamTimeline::exponentialRampToValueAtTime(float value, float time)
+{
+ insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, 0));
+}
+
+void AudioParamTimeline::setTargetValueAtTime(float targetValue, float time, float timeConstant)
+{
+ insertEvent(ParamEvent(ParamEvent::SetTargetValue, targetValue, time, timeConstant, 0, 0));
+}
+
+void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, float time, float duration)
+{
+ insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
+}
+
+static bool isValidNumber(float x)
+{
+ return !isnan(x) && !isinf(x);
+}
+
+void AudioParamTimeline::insertEvent(const ParamEvent& event)
+{
+ // Sanity check the event. Be super careful we're not getting infected with NaN or Inf.
+ bool isValid = event.type() < ParamEvent::LastType
+ && isValidNumber(event.value())
+ && isValidNumber(event.time())
+ && isValidNumber(event.timeConstant())
+ && isValidNumber(event.duration())
+ && event.duration() >= 0;
+
+ ASSERT(isValid);
+ if (!isValid)
+ return;
+
+ MutexLocker locker(m_eventsLock);
+
+ unsigned i = 0;
+ float insertTime = event.time();
+ for (i = 0; i < m_events.size(); ++i) {
+ // Overwrite same event type and time.
+ if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
+ m_events[i] = event;
+ return;
+ }
+
+ if (m_events[i].time() > insertTime)
+ break;
+ }
+
+ m_events.insert(i, event);
+}
+
+void AudioParamTimeline::cancelScheduledValues(float startTime)
+{
+ MutexLocker locker(m_eventsLock);
+
+ // Remove all events starting at startTime.
+ for (unsigned i = 0; i < m_events.size(); ++i) {
+ if (m_events[i].time() >= startTime) {
+ m_events.remove(i, m_events.size() - i);
+ break;
+ }
+ }
+}
+
+float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
+{
+ ASSERT(context);
+
+ if (!context || !m_events.size() || context->currentTime() < m_events[0].time()) {
+ hasValue = false;
+ return defaultValue;
+ }
+
+ // Ask for just a single value.
+ float value;
+ float sampleRate = context->sampleRate();
+ float startTime = narrowPrecisionToFloat(context->currentTime());
+ float endTime = startTime + 1.1f / sampleRate; // time just beyond one sample-frame
+ float controlRate = sampleRate / AudioNode::ProcessingSizeInFrames; // one parameter change per render quantum
+ value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
+
+ hasValue = true;
+ return value;
+}
+
+float AudioParamTimeline::valuesForTimeRange(float startTime,
+ float endTime,
+ float defaultValue,
+ float* values,
+ unsigned numberOfValues,
+ float sampleRate,
+ float controlRate)
+{
+ // We can't contend the lock in the realtime audio thread.
+ if (!m_eventsLock.tryLock()) {
+ if (values) {
+ for (unsigned i = 0; i < numberOfValues; ++i)
+ values[i] = defaultValue;
+ }
+ return defaultValue;
+ }
+
+ float value = valuesForTimeRangeImpl(startTime, endTime, defaultValue, values, numberOfValues, sampleRate, controlRate);
+ m_eventsLock.unlock();
+
+ return value;
+}
+
+// Returns the rounded down integer sample-frame for the time and sample-rate.
+static unsigned timeToSampleFrame(double time, float sampleRate)
+{
+ double k = 0.5 / sampleRate;
+ return static_cast<unsigned>((time + k) * sampleRate);
+}
+
+float AudioParamTimeline::valuesForTimeRangeImpl(float startTime,
+ float endTime,
+ float defaultValue,
+ float* values,
+ unsigned numberOfValues,
+ float sampleRate,
+ float controlRate)
+{
+ ASSERT(values);
+ if (!values)
+ return defaultValue;
+
+ // Return default value if there are no events matching the desired time range.
+ ASSERT(m_events.size());
+ if (!m_events.size() || endTime <= m_events[0].time()) {
+ for (unsigned i = 0; i < numberOfValues; ++i)
+ values[i] = defaultValue;
+ return defaultValue;
+ }
+
+ // Maintain a running time and index for writing the values buffer.
+ float currentTime = startTime;
+ unsigned writeIndex = 0;
+
+ // If first event is after startTime then fill initial part of values buffer with defaultValue
+ // until we reach the first event time.
+ float firstEventTime = m_events[0].time();
+ if (firstEventTime > startTime) {
+ float fillToTime = min(endTime, firstEventTime);
+ unsigned fillToFrame = timeToSampleFrame(fillToTime - startTime, sampleRate);
+ fillToFrame = min(fillToFrame, numberOfValues);
+ for (; writeIndex < fillToFrame; ++writeIndex)
+ values[writeIndex] = defaultValue;
+
+ currentTime = fillToTime;
+ }
+
+ float value = defaultValue;
+
+ // Go through each event and render the value buffer where the times overlap,
+ // stopping when we've rendered all the requested values.
+ // FIXME: could try to optimize by avoiding having to iterate starting from the very first event
+ // and keeping track of a "current" event index.
+ int n = m_events.size();
+ for (int i = 0; i < n && writeIndex < numberOfValues; ++i) {
+ ParamEvent& event = m_events[i];
+ ParamEvent* nextEvent = i < n - 1 ? &(m_events[i + 1]) : 0;
+
+ // Wait until we get a more recent event.
+ if (nextEvent && nextEvent->time() < currentTime)
+ continue;
+
+ float value1 = event.value();
+ float time1 = event.time();
+ float value2 = nextEvent ? nextEvent->value() : value1;
+ float time2 = nextEvent ? nextEvent->time() : endTime + 1;
+
+ float deltaTime = time2 - time1;
+ float k = deltaTime > 0 ? 1 / deltaTime : 0;
+ float sampleFrameTimeIncr = 1 / sampleRate;
+
+ float fillToTime = min(endTime, time2);
+ unsigned fillToFrame = timeToSampleFrame(fillToTime - startTime, sampleRate);
+ fillToFrame = min(fillToFrame, numberOfValues);
+
+ ParamEvent::Type nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType /* unknown */;
+
+ // First handle linear and exponential ramps which require looking ahead to the next event.
+ if (nextEventType == ParamEvent::LinearRampToValue) {
+ for (; writeIndex < fillToFrame; ++writeIndex) {
+ float x = (currentTime - time1) * k;
+ value = (1 - x) * value1 + x * value2;
+ values[writeIndex] = value;
+ currentTime += sampleFrameTimeIncr;
+ }
+ } else if (nextEventType == ParamEvent::ExponentialRampToValue) {
+ if (value1 <= 0 || value2 <= 0) {
+ // Handle negative values error case by propagating previous value.
+ for (; writeIndex < fillToFrame; ++writeIndex)
+ values[writeIndex] = value;
+ } else {
+ float numSampleFrames = deltaTime * sampleRate;
+ // The value goes exponentially from value1 to value2 in a duration of deltaTime seconds (corresponding to numSampleFrames).
+ // Compute the per-sample multiplier.
+ float multiplier = powf(value2 / value1, 1 / numSampleFrames);
+ for (; writeIndex < fillToFrame; ++writeIndex) {
+ values[writeIndex] = value;
+ value *= multiplier;
+ currentTime += sampleFrameTimeIncr;
+ }
+ }
+ } else {
+ // Handle event types not requiring looking ahead to the next event.
+ switch (event.type()) {
+ case ParamEvent::SetValue:
+ case ParamEvent::LinearRampToValue:
+ case ParamEvent::ExponentialRampToValue:
+ {
+ currentTime = fillToTime;
+
+ // Simply stay at a constant value.
+ value = event.value();
+ for (; writeIndex < fillToFrame; ++writeIndex)
+ values[writeIndex] = value;
+
+ break;
+ }
+
+ case ParamEvent::SetTargetValue:
+ {
+ currentTime = fillToTime;
+
+ // Exponential approach to target value with given time constant.
+ float targetValue = event.value();
+ float timeConstant = event.timeConstant();
+ float discreteTimeConstant = AudioUtilities::discreteTimeConstantForSampleRate(timeConstant, controlRate);
+
+ for (; writeIndex < fillToFrame; ++writeIndex) {
+ values[writeIndex] = value;
+ value += (targetValue - value) * discreteTimeConstant;
+ }
+
+ break;
+ }
+
+ case ParamEvent::SetValueCurve:
+ {
+ Float32Array* curve = event.curve();
+ float* curveData = curve ? curve->data() : 0;
+ unsigned numberOfCurvePoints = curve ? curve->length() : 0;
+
+ // Curve events have duration, so don't just use next event time.
+ float duration = event.duration();
+ float durationFrames = duration * sampleRate;
+ float curvePointsPerFrame = static_cast<float>(numberOfCurvePoints) / durationFrames;
+
+ if (!curve || !curveData || !numberOfCurvePoints || duration <= 0 || sampleRate <= 0) {
+ // Error condition - simply propagate previous value.
+ currentTime = fillToTime;
+ for (; writeIndex < fillToFrame; ++writeIndex)
+ values[writeIndex] = value;
+ break;
+ }
+
+ // Save old values and recalculate information based on the curve's duration
+ // instead of the next event time.
+ unsigned nextEventFillToFrame = fillToFrame;
+ float nextEventFillToTime = fillToTime;
+ fillToTime = min(endTime, time1 + duration);
+ fillToFrame = timeToSampleFrame(fillToTime - startTime, sampleRate);
+ fillToFrame = min(fillToFrame, numberOfValues);
+
+ // Index into the curve data using a floating-point value.
+ // We're scaling the number of curve points by the duration (see curvePointsPerFrame).
+ float curveVirtualIndex = 0;
+ if (time1 < currentTime) {
+ // Index somewhere in the middle of the curve data.
+ // Don't use timeToSampleFrame() since we want the exact floating-point frame.
+ float frameOffset = (currentTime - time1) * sampleRate;
+ curveVirtualIndex = curvePointsPerFrame * frameOffset;
+ }
+
+ // Render the stretched curve data using nearest neighbor sampling.
+ // Oversampled curve data can be provided if smoothness is desired.
+ for (; writeIndex < fillToFrame; ++writeIndex) {
+ unsigned curveIndex = static_cast<unsigned>(curveVirtualIndex);
+ curveVirtualIndex += curvePointsPerFrame;
+
+ // Bounds check.
+ if (curveIndex < numberOfCurvePoints)
+ value = curveData[curveIndex];
+
+ values[writeIndex] = value;
+ }
+
+ // If there's any time left after the duration of this event and the start
+ // of the next, then just propagate the last value.
+ for (; writeIndex < nextEventFillToFrame; ++writeIndex)
+ values[writeIndex] = value;
+
+ // Re-adjust current time
+ currentTime = nextEventFillToTime;
+
+ break;
+ }
+ }
+ }
+ }
+
+ // If there's any time left after processing the last event then just propagate the last value
+ // to the end of the values buffer.
+ for (; writeIndex < numberOfValues; ++writeIndex)
+ values[writeIndex] = value;
+
+ return value;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/AudioParamTimeline.h b/Source/WebCore/webaudio/AudioParamTimeline.h
new file mode 100644
index 000000000..22ea89081
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioParamTimeline.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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 AudioParamTimeline_h
+#define AudioParamTimeline_h
+
+#include "AudioContext.h"
+#include <wtf/Float32Array.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioParamTimeline {
+public:
+ AudioParamTimeline()
+ : m_currentEventIndex(0)
+ , m_value(0)
+ {
+ }
+
+ void setValueAtTime(float value, float time);
+ void linearRampToValueAtTime(float value, float time);
+ void exponentialRampToValueAtTime(float value, float time);
+ void setTargetValueAtTime(float targetValue, float time, float timeConstant);
+ void setValueCurveAtTime(Float32Array* curve, float time, float duration);
+ void cancelScheduledValues(float startTime);
+
+ // hasValue is set to true if a valid timeline value is returned.
+ // otherwise defaultValue is returned.
+ float valueForContextTime(AudioContext*, float defaultValue, bool& hasValue);
+
+ // Given the time range, calculates parameter values into the values buffer
+ // and returns the last parameter value calculated for "values" or the defaultValue if none were calculated.
+ // controlRate is the rate (number per second) at which parameter values will be calculated.
+ // It should equal sampleRate for sample-accurate parameter changes, and otherwise will usually match
+ // the render quantum size such that the parameter value changes once per render quantum.
+ float valuesForTimeRange(float startTime, float endTime, float defaultValue, float* values, unsigned numberOfValues, float sampleRate, float controlRate);
+
+ bool hasValues() { return m_events.size(); }
+
+private:
+ class ParamEvent {
+ public:
+ enum Type {
+ SetValue,
+ LinearRampToValue,
+ ExponentialRampToValue,
+ SetTargetValue,
+ SetValueCurve,
+ LastType
+ };
+
+ ParamEvent(Type type, float value, float time, float timeConstant, float duration, PassRefPtr<Float32Array> curve)
+ : m_type(type)
+ , m_value(value)
+ , m_time(time)
+ , m_timeConstant(timeConstant)
+ , m_duration(duration)
+ , m_curve(curve)
+ {
+ }
+
+ unsigned type() const { return m_type; }
+ float value() const { return m_value; }
+ float time() const { return m_time; }
+ float timeConstant() const { return m_timeConstant; }
+ float duration() const { return m_duration; }
+ Float32Array* curve() { return m_curve.get(); }
+
+ private:
+ unsigned m_type;
+ float m_value;
+ float m_time;
+ float m_timeConstant;
+ float m_duration;
+ RefPtr<Float32Array> m_curve;
+ };
+
+ void insertEvent(const ParamEvent&);
+ float valuesForTimeRangeImpl(float startTime, float endTime, float defaultValue, float* values, unsigned numberOfValues, float sampleRate, float controlRate);
+
+ Vector<ParamEvent> m_events;
+ unsigned m_currentEventIndex;
+ float m_value;
+
+ Mutex m_eventsLock;
+};
+
+} // namespace WebCore
+
+#endif // AudioParamTimeline_h
diff --git a/Source/WebCore/webaudio/AudioProcessingEvent.cpp b/Source/WebCore/webaudio/AudioProcessingEvent.cpp
new file mode 100644
index 000000000..fd5431ce4
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioProcessingEvent.cpp
@@ -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.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "AudioProcessingEvent.h"
+
+#include "AudioBuffer.h"
+#include "EventNames.h"
+
+namespace WebCore {
+
+PassRefPtr<AudioProcessingEvent> AudioProcessingEvent::create()
+{
+ return adoptRef(new AudioProcessingEvent);
+}
+
+PassRefPtr<AudioProcessingEvent> AudioProcessingEvent::create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
+{
+ return adoptRef(new AudioProcessingEvent(inputBuffer, outputBuffer));
+}
+
+AudioProcessingEvent::AudioProcessingEvent()
+{
+}
+
+AudioProcessingEvent::AudioProcessingEvent(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
+ : Event(eventNames().audioprocessEvent, true, false)
+ , m_inputBuffer(inputBuffer)
+ , m_outputBuffer(outputBuffer)
+{
+}
+
+AudioProcessingEvent::~AudioProcessingEvent()
+{
+}
+
+const AtomicString& AudioProcessingEvent::interfaceName() const
+{
+ return eventNames().interfaceForAudioProcessingEvent;
+}
+
+} // 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 000000000..3a4a1446c
--- /dev/null
+++ b/Source/WebCore/webaudio/AudioProcessingEvent.h
@@ -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.
+ */
+
+#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();
+ static PassRefPtr<AudioProcessingEvent> create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer);
+
+ virtual ~AudioProcessingEvent();
+
+ AudioBuffer* inputBuffer() { return m_inputBuffer.get(); }
+ AudioBuffer* outputBuffer() { return m_outputBuffer.get(); }
+
+ virtual const AtomicString& interfaceName() const;
+
+private:
+ AudioProcessingEvent();
+ 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 000000000..c2f8a8331
--- /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 000000000..a6bdd427b
--- /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, float 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 000000000..ec3c35681
--- /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 000000000..9faac6598
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadDSPKernel.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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"
+#include "FloatConversion.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+void BiquadDSPKernel::updateCoefficientsIfNecessary(bool useSmoothing, bool forceUpdate)
+{
+ if (forceUpdate || biquadProcessor()->filterCoefficientsDirty()) {
+ double value1;
+ double value2;
+ double gain;
+
+ if (useSmoothing) {
+ value1 = biquadProcessor()->parameter1()->smoothedValue();
+ value2 = biquadProcessor()->parameter2()->smoothedValue();
+ gain = biquadProcessor()->parameter3()->smoothedValue();
+ } else {
+ value1 = biquadProcessor()->parameter1()->value();
+ value2 = biquadProcessor()->parameter2()->value();
+ gain = biquadProcessor()->parameter3()->value();
+ }
+
+ // Convert from Hertz to normalized frequency 0 -> 1.
+ double nyquist = this->nyquist();
+ double normalizedFrequency = value1 / nyquist;
+
+ // Configure the biquad with the new filter parameters for the appropriate type of filter.
+ switch (biquadProcessor()->type()) {
+ case BiquadProcessor::LowPass:
+ m_biquad.setLowpassParams(normalizedFrequency, value2);
+ break;
+
+ case BiquadProcessor::HighPass:
+ m_biquad.setHighpassParams(normalizedFrequency, value2);
+ break;
+
+ case BiquadProcessor::BandPass:
+ m_biquad.setBandpassParams(normalizedFrequency, value2);
+ break;
+
+ case BiquadProcessor::LowShelf:
+ m_biquad.setLowShelfParams(normalizedFrequency, gain);
+ break;
+
+ case BiquadProcessor::HighShelf:
+ m_biquad.setHighShelfParams(normalizedFrequency, gain);
+ break;
+
+ case BiquadProcessor::Peaking:
+ m_biquad.setPeakingParams(normalizedFrequency, value2, gain);
+ break;
+
+ case BiquadProcessor::Notch:
+ m_biquad.setNotchParams(normalizedFrequency, value2);
+ break;
+
+ case BiquadProcessor::Allpass:
+ m_biquad.setAllpassParams(normalizedFrequency, value2);
+ break;
+ }
+ }
+}
+
+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.
+
+ updateCoefficientsIfNecessary(true, false);
+
+ m_biquad.process(source, destination, framesToProcess);
+}
+
+void BiquadDSPKernel::getFrequencyResponse(int nFrequencies,
+ const float* frequencyHz,
+ float* magResponse,
+ float* phaseResponse)
+{
+ bool isGood = nFrequencies > 0 && frequencyHz && magResponse && phaseResponse;
+ ASSERT(isGood);
+ if (!isGood)
+ return;
+
+ Vector<float> frequency(nFrequencies);
+
+ double nyquist = this->nyquist();
+
+ // Convert from frequency in Hz to normalized frequency (0 -> 1),
+ // with 1 equal to the Nyquist frequency.
+ for (int k = 0; k < nFrequencies; ++k)
+ frequency[k] = narrowPrecisionToFloat(frequencyHz[k] / nyquist);
+
+ // We want to get the final values of the coefficients and compute
+ // the response from that instead of some intermediate smoothed
+ // set. Forcefully update the coefficients even if they are not
+ // dirty.
+
+ updateCoefficientsIfNecessary(false, true);
+
+ m_biquad.getFrequencyResponse(nFrequencies, frequency.data(), magResponse, phaseResponse);
+}
+
+} // 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 000000000..a21e24c0d
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadDSPKernel.h
@@ -0,0 +1,71 @@
+/*
+ * 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(); }
+
+ // Get the magnitude and phase response of the filter at the given
+ // set of frequencies (in Hz). The phase response is in radians.
+ void getFrequencyResponse(int nFrequencies,
+ const float* frequencyHz,
+ float* magResponse,
+ float* phaseResponse);
+protected:
+ Biquad m_biquad;
+ BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
+
+ // To prevent audio glitches when parameters are changed,
+ // dezippering is used to slowly change the parameters.
+ // |useSmoothing| implies that we want to update using the
+ // smoothed values. Otherwise the final target values are
+ // used. If |forceUpdate| is true, we update the coefficients even
+ // if they are not dirty. (Used when computing the frequency
+ // response.)
+ void updateCoefficientsIfNecessary(bool useSmoothing, bool forceUpdate);
+};
+
+} // namespace WebCore
+
+#endif // BiquadDSPKernel_h
diff --git a/Source/WebCore/webaudio/BiquadFilterNode.cpp b/Source/WebCore/webaudio/BiquadFilterNode.cpp
new file mode 100644
index 000000000..80c3f938c
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadFilterNode.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011, 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 "BiquadFilterNode.h"
+
+#include "ExceptionCode.h"
+
+namespace WebCore {
+
+BiquadFilterNode::BiquadFilterNode(AudioContext* context, float sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ // Initially setup as lowpass filter.
+ m_processor = adoptPtr(new BiquadProcessor(BiquadProcessor::LowPass, sampleRate, 1, false));
+ biquadProcessor()->parameter1()->setContext(context);
+ biquadProcessor()->parameter2()->setContext(context);
+ biquadProcessor()->parameter3()->setContext(context);
+ setNodeType(NodeTypeBiquadFilter);
+}
+
+void BiquadFilterNode::setType(unsigned short type, ExceptionCode& ec)
+{
+ if (type > BiquadProcessor::Allpass) {
+ ec = NOT_SUPPORTED_ERR;
+ return;
+ }
+
+ biquadProcessor()->setType(static_cast<BiquadProcessor::FilterType>(type));
+}
+
+
+void BiquadFilterNode::getFrequencyResponse(const Float32Array* frequencyHz,
+ Float32Array* magResponse,
+ Float32Array* phaseResponse)
+{
+ if (!frequencyHz || !magResponse || !phaseResponse)
+ return;
+
+ int n = std::min(frequencyHz->length(),
+ std::min(magResponse->length(), phaseResponse->length()));
+
+ if (n) {
+ biquadProcessor()->getFrequencyResponse(n,
+ frequencyHz->data(),
+ magResponse->data(),
+ phaseResponse->data());
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/BiquadFilterNode.h b/Source/WebCore/webaudio/BiquadFilterNode.h
new file mode 100644
index 000000000..545b060be
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadFilterNode.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011, 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 BiquadFilterNode_h
+#define BiquadFilterNode_h
+
+#include "AudioBasicProcessorNode.h"
+#include "BiquadProcessor.h"
+
+namespace WebCore {
+
+class AudioParam;
+
+class BiquadFilterNode : public AudioBasicProcessorNode {
+public:
+ // These must be defined as in the .idl file and must match those in the BiquadProcessor class.
+ enum {
+ LOWPASS = 0,
+ HIGHPASS = 1,
+ BANDPASS = 2,
+ LOWSHELF = 3,
+ HIGHSHELF = 4,
+ PEAKING = 5,
+ NOTCH = 6,
+ ALLPASS = 7
+ };
+
+ static PassRefPtr<BiquadFilterNode> create(AudioContext* context, float sampleRate)
+ {
+ return adoptRef(new BiquadFilterNode(context, sampleRate));
+ }
+
+ unsigned short type() { return biquadProcessor()->type(); }
+ void setType(unsigned short type, ExceptionCode&);
+
+ AudioParam* frequency() { return biquadProcessor()->parameter1(); }
+ AudioParam* q() { return biquadProcessor()->parameter2(); }
+ AudioParam* gain() { return biquadProcessor()->parameter3(); }
+
+ // Get the magnitude and phase response of the filter at the given
+ // set of frequencies (in Hz). The phase response is in radians.
+ void getFrequencyResponse(const Float32Array* frequencyHz,
+ Float32Array* magResponse,
+ Float32Array* phaseResponse);
+private:
+ BiquadFilterNode(AudioContext*, float sampleRate);
+
+ BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // BiquadFilterNode_h
diff --git a/Source/WebCore/webaudio/BiquadFilterNode.idl b/Source/WebCore/webaudio/BiquadFilterNode.idl
new file mode 100644
index 000000000..84e015aa3
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadFilterNode.idl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011, 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
+ ] BiquadFilterNode : AudioNode {
+ // Filter type.
+ const unsigned short LOWPASS = 0;
+ const unsigned short HIGHPASS = 1;
+ const unsigned short BANDPASS = 2;
+ const unsigned short LOWSHELF = 3;
+ const unsigned short HIGHSHELF = 4;
+ const unsigned short PEAKING = 5;
+ const unsigned short NOTCH = 6;
+ const unsigned short ALLPASS = 7;
+
+ attribute unsigned short type
+ setter raises(DOMException);
+
+ readonly attribute AudioParam frequency; // in Hertz
+ readonly attribute AudioParam Q; // Quality factor
+ readonly attribute AudioParam gain; // in Decibels
+
+ void getFrequencyResponse(in Float32Array frequencyHz,
+ in Float32Array magResponse,
+ in Float32Array phaseResponse);
+ };
+}
diff --git a/Source/WebCore/webaudio/BiquadProcessor.cpp b/Source/WebCore/webaudio/BiquadProcessor.cpp
new file mode 100644
index 000000000..12243c0c9
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadProcessor.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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(float sampleRate, size_t numberOfChannels, bool autoInitialize)
+ : AudioDSPKernelProcessor(sampleRate, numberOfChannels)
+ , m_type(LowPass)
+ , m_parameter1(0)
+ , m_parameter2(0)
+ , m_parameter3(0)
+ , m_filterCoefficientsDirty(true)
+{
+ double nyquist = 0.5 * this->sampleRate();
+
+ // Create parameters for BiquadFilterNode.
+ m_parameter1 = AudioParam::create("frequency", 350.0, 10.0, nyquist);
+ m_parameter2 = AudioParam::create("Q", 1, 0.0001, 1000.0);
+ m_parameter3 = AudioParam::create("gain", 0.0, -40, 40);
+
+ if (autoInitialize)
+ initialize();
+}
+
+BiquadProcessor::BiquadProcessor(FilterType type, float 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();
+
+ // Handle the deprecated LowPass2FilterNode and HighPass2FilterNode.
+ switch (type) {
+ // Highpass and lowpass share the same parameters and only differ in filter type.
+ case LowPass:
+ case HighPass:
+ 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;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ if (autoInitialize)
+ initialize();
+}
+
+BiquadProcessor::~BiquadProcessor()
+{
+ if (isInitialized())
+ uninitialize();
+}
+
+PassOwnPtr<AudioDSPKernel> BiquadProcessor::createKernel()
+{
+ return adoptPtr(new BiquadDSPKernel(this));
+}
+
+void BiquadProcessor::checkForDirtyCoefficients()
+{
+ // 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;
+ }
+}
+
+void BiquadProcessor::process(AudioBus* source, AudioBus* destination, size_t framesToProcess)
+{
+ if (!isInitialized()) {
+ destination->zero();
+ return;
+ }
+
+ checkForDirtyCoefficients();
+
+ // 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);
+}
+
+void BiquadProcessor::setType(FilterType type)
+{
+ if (type != m_type) {
+ m_type = type;
+ reset(); // The filter state must be reset only if the type has changed.
+ }
+}
+
+void BiquadProcessor::getFrequencyResponse(int nFrequencies,
+ const float* frequencyHz,
+ float* magResponse,
+ float* phaseResponse)
+{
+ // Compute the frequency response on a separate temporary kernel
+ // to avoid interfering with the processing running in the audio
+ // thread on the main kernels.
+
+ OwnPtr<BiquadDSPKernel> responseKernel = adoptPtr(new BiquadDSPKernel(this));
+
+ responseKernel->getFrequencyResponse(nFrequencies, frequencyHz, magResponse, phaseResponse);
+}
+
+} // 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 000000000..c7f7d3068
--- /dev/null
+++ b/Source/WebCore/webaudio/BiquadProcessor.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.
+ *
+ * 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 {
+ LowPass = 0,
+ HighPass = 1,
+ BandPass = 2,
+ LowShelf = 3,
+ HighShelf = 4,
+ Peaking = 5,
+ Notch = 6,
+ Allpass = 7
+ };
+
+ BiquadProcessor(float sampleRate, size_t numberOfChannels, bool autoInitialize);
+
+ // Old constructor used by deprecated LowPass2FilterNode and HighPass2FilterNode
+ BiquadProcessor(FilterType, float sampleRate, size_t numberOfChannels, bool autoInitialize = true);
+
+ virtual ~BiquadProcessor();
+
+ virtual PassOwnPtr<AudioDSPKernel> createKernel();
+
+ virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess);
+
+ // Get the magnitude and phase response of the filter at the given
+ // set of frequencies (in Hz). The phase response is in radians.
+ void getFrequencyResponse(int nFrequencies,
+ const float* frequencyHz,
+ float* magResponse,
+ float* phaseResponse);
+
+ void checkForDirtyCoefficients();
+
+ 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; }
+ void setType(FilterType);
+
+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 000000000..315835619
--- /dev/null
+++ b/Source/WebCore/webaudio/ConvolverNode.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "Reverb.h"
+#include <wtf/MainThread.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 32768 is a good compromise.
+const size_t MaxFFTSize = 32768;
+
+namespace WebCore {
+
+ConvolverNode::ConvolverNode(AudioContext* context, float sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setNodeType(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.
+ bool useBackgroundThreads = !context()->isOfflineContext();
+ OwnPtr<Reverb> reverb = adoptPtr(new Reverb(&bufferBus, AudioNode::ProcessingSizeInFrames, MaxFFTSize, 2, useBackgroundThreads));
+
+ {
+ // 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 000000000..fb29d5c5d
--- /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, float 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*, float 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 000000000..d3eb475f0
--- /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/DOMWindowWebAudio.idl b/Source/WebCore/webaudio/DOMWindowWebAudio.idl
new file mode 100644
index 000000000..dd108efb7
--- /dev/null
+++ b/Source/WebCore/webaudio/DOMWindowWebAudio.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+module window {
+
+ interface [
+ Conditional=WEB_AUDIO,
+ Supplemental=DOMWindow
+ ] DOMWindowWebAudio {
+ attribute [JSCCustomGetter, EnabledAtRuntime] AudioContextConstructor webkitAudioContext;
+ attribute AudioPannerNodeConstructor webkitAudioPannerNode;
+ attribute AudioProcessingEventConstructor AudioProcessingEvent;
+ attribute OfflineAudioCompletionEventConstructor OfflineAudioCompletionEvent;
+ };
+
+} \ No newline at end of file
diff --git a/Source/WebCore/webaudio/DefaultAudioDestinationNode.cpp b/Source/WebCore/webaudio/DefaultAudioDestinationNode.cpp
new file mode 100644
index 000000000..f7db34e05
--- /dev/null
+++ b/Source/WebCore/webaudio/DefaultAudioDestinationNode.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011, 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 "DefaultAudioDestinationNode.h"
+
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+namespace WebCore {
+
+DefaultAudioDestinationNode::DefaultAudioDestinationNode(AudioContext* context)
+ : AudioDestinationNode(context, AudioDestination::hardwareSampleRate())
+{
+ initialize();
+}
+
+DefaultAudioDestinationNode::~DefaultAudioDestinationNode()
+{
+ uninitialize();
+}
+
+void DefaultAudioDestinationNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ float hardwareSampleRate = AudioDestination::hardwareSampleRate();
+#ifndef NDEBUG
+ fprintf(stderr, ">>>> hardwareSampleRate = %f\n", hardwareSampleRate);
+#endif
+
+ m_destination = AudioDestination::create(*this, hardwareSampleRate);
+
+ AudioNode::initialize();
+}
+
+void DefaultAudioDestinationNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_destination->stop();
+
+ AudioNode::uninitialize();
+}
+
+void DefaultAudioDestinationNode::startRendering()
+{
+ ASSERT(isInitialized());
+ if (isInitialized())
+ m_destination->start();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/DefaultAudioDestinationNode.h b/Source/WebCore/webaudio/DefaultAudioDestinationNode.h
new file mode 100644
index 000000000..630bdc335
--- /dev/null
+++ b/Source/WebCore/webaudio/DefaultAudioDestinationNode.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011, 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 DefaultAudioDestinationNode_h
+#define DefaultAudioDestinationNode_h
+
+#include "AudioDestination.h"
+#include "AudioDestinationNode.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+class DefaultAudioDestinationNode : public AudioDestinationNode {
+public:
+ static PassRefPtr<DefaultAudioDestinationNode> create(AudioContext* context)
+ {
+ return adoptRef(new DefaultAudioDestinationNode(context));
+ }
+
+ virtual ~DefaultAudioDestinationNode();
+
+ // AudioNode
+ virtual void initialize();
+ virtual void uninitialize();
+
+ float sampleRate() const { return m_destination->sampleRate(); }
+
+ virtual void startRendering();
+
+private:
+ DefaultAudioDestinationNode(AudioContext*);
+
+ OwnPtr<AudioDestination> m_destination;
+};
+
+} // namespace WebCore
+
+#endif // DefaultAudioDestinationNode_h
diff --git a/Source/WebCore/webaudio/DelayDSPKernel.cpp b/Source/WebCore/webaudio/DelayDSPKernel.cpp
new file mode 100644
index 000000000..aeda189c5
--- /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 float DefaultMaxDelayTime = 1;
+const float SmoothingTimeConstant = 0.020f; // 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.allocate(static_cast<size_t>(processor->sampleRate() * DefaultMaxDelayTime));
+ m_buffer.zero();
+
+ m_smoothingRate = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, processor->sampleRate());
+}
+
+DelayDSPKernel::DelayDSPKernel(double maxDelayTime, float 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.allocate(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;
+
+ float 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 000000000..79a39568b
--- /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, float 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 000000000..2a525dd1a
--- /dev/null
+++ b/Source/WebCore/webaudio/DelayNode.cpp
@@ -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.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "DelayNode.h"
+
+namespace WebCore {
+
+DelayNode::DelayNode(AudioContext* context, float sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new DelayProcessor(sampleRate, 1));
+ delayTime()->setContext(context);
+ setNodeType(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 000000000..3d58a8913
--- /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, float sampleRate)
+ {
+ return adoptRef(new DelayNode(context, sampleRate));
+ }
+
+ AudioParam* delayTime();
+
+private:
+ DelayNode(AudioContext*, float 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 000000000..77566270c
--- /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 000000000..8ed3e4337
--- /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(float 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 000000000..15428ce75
--- /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(float 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/DynamicsCompressorNode.cpp b/Source/WebCore/webaudio/DynamicsCompressorNode.cpp
new file mode 100644
index 000000000..ea8b58922
--- /dev/null
+++ b/Source/WebCore/webaudio/DynamicsCompressorNode.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011, 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 "DynamicsCompressorNode.h"
+
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include "DynamicsCompressor.h"
+
+namespace WebCore {
+
+DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* context, float sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setNodeType(NodeTypeDynamicsCompressor);
+
+ initialize();
+}
+
+DynamicsCompressorNode::~DynamicsCompressorNode()
+{
+ uninitialize();
+}
+
+void DynamicsCompressorNode::process(size_t framesToProcess)
+{
+ AudioBus* outputBus = output(0)->bus();
+ ASSERT(outputBus);
+
+ m_dynamicsCompressor->process(input(0)->bus(), outputBus, framesToProcess);
+}
+
+void DynamicsCompressorNode::reset()
+{
+ m_dynamicsCompressor->reset();
+}
+
+void DynamicsCompressorNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ AudioNode::initialize();
+ m_dynamicsCompressor = adoptPtr(new DynamicsCompressor(true, sampleRate()));
+}
+
+void DynamicsCompressorNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_dynamicsCompressor.clear();
+ AudioNode::uninitialize();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/DynamicsCompressorNode.h b/Source/WebCore/webaudio/DynamicsCompressorNode.h
new file mode 100644
index 000000000..d718ab341
--- /dev/null
+++ b/Source/WebCore/webaudio/DynamicsCompressorNode.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011, 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 DynamicsCompressorNode_h
+#define DynamicsCompressorNode_h
+
+#include "AudioNode.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class DynamicsCompressor;
+
+class DynamicsCompressorNode : public AudioNode {
+public:
+ static PassRefPtr<DynamicsCompressorNode> create(AudioContext* context, float sampleRate)
+ {
+ return adoptRef(new DynamicsCompressorNode(context, sampleRate));
+ }
+
+ virtual ~DynamicsCompressorNode();
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+ virtual void initialize();
+ virtual void uninitialize();
+
+private:
+ DynamicsCompressorNode(AudioContext*, float sampleRate);
+
+ OwnPtr<DynamicsCompressor> m_dynamicsCompressor;
+};
+
+} // namespace WebCore
+
+#endif // DynamicsCompressorNode_h
diff --git a/Source/WebCore/webaudio/DynamicsCompressorNode.idl b/Source/WebCore/webaudio/DynamicsCompressorNode.idl
new file mode 100644
index 000000000..74997eae5
--- /dev/null
+++ b/Source/WebCore/webaudio/DynamicsCompressorNode.idl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011, 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
+ ] DynamicsCompressorNode : AudioNode {
+ };
+}
diff --git a/Source/WebCore/webaudio/HighPass2FilterNode.cpp b/Source/WebCore/webaudio/HighPass2FilterNode.cpp
new file mode 100644
index 000000000..520785f80
--- /dev/null
+++ b/Source/WebCore/webaudio/HighPass2FilterNode.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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, float sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new BiquadProcessor(BiquadProcessor::HighPass, sampleRate, 1, false));
+ biquadProcessor()->parameter1()->setContext(context);
+ biquadProcessor()->parameter2()->setContext(context);
+ biquadProcessor()->parameter3()->setContext(context);
+ setNodeType(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 000000000..bfd76a0d5
--- /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, float sampleRate)
+ {
+ return adoptRef(new HighPass2FilterNode(context, sampleRate));
+ }
+
+ AudioParam* cutoff() { return biquadProcessor()->parameter1(); }
+ AudioParam* resonance() { return biquadProcessor()->parameter2(); }
+
+private:
+ HighPass2FilterNode(AudioContext*, float 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 000000000..399f9b500
--- /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 000000000..4221509cf
--- /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 <wtf/Float32Array.h>
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+const size_t DefaultBufferSize = 4096;
+
+PassRefPtr<JavaScriptAudioNode> JavaScriptAudioNode::create(AudioContext* context, float sampleRate, size_t bufferSize, unsigned numberOfInputs, unsigned numberOfOutputs)
+{
+ return adoptRef(new JavaScriptAudioNode(context, sampleRate, bufferSize, numberOfInputs, numberOfOutputs));
+}
+
+JavaScriptAudioNode::JavaScriptAudioNode(AudioContext* context, float 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)));
+
+ setNodeType(NodeTypeJavaScript);
+
+ initialize();
+}
+
+JavaScriptAudioNode::~JavaScriptAudioNode()
+{
+ uninitialize();
+}
+
+void JavaScriptAudioNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ float 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();
+}
+
+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;
+ m_isRequestOutstanding = true;
+ callOnMainThread(fireProcessEventDispatch, this);
+ }
+
+ 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();
+ }
+}
+
+const AtomicString& JavaScriptAudioNode::interfaceName() const
+{
+ return eventNames().interfaceForJavaScriptAudioNode;
+}
+
+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 000000000..57435885d
--- /dev/null
+++ b/Source/WebCore/webaudio/JavaScriptAudioNode.h
@@ -0,0 +1,105 @@
+/*
+ * 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/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class AudioContext;
+class AudioProcessingEvent;
+
+// 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.
+
+// FIXME: EventTarget should be introduced at the base of the inheritance hierarchy (i.e., as a base class for AudioNode).
+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*, float 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 const AtomicString& interfaceName() const;
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ 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*, float 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 000000000..ef5359b97
--- /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 000000000..e2d669acd
--- /dev/null
+++ b/Source/WebCore/webaudio/LowPass2FilterNode.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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, float sampleRate)
+ : AudioBasicProcessorNode(context, sampleRate)
+{
+ m_processor = adoptPtr(new BiquadProcessor(BiquadProcessor::LowPass, sampleRate, 1, false));
+ biquadProcessor()->parameter1()->setContext(context);
+ biquadProcessor()->parameter2()->setContext(context);
+ biquadProcessor()->parameter3()->setContext(context);
+ setNodeType(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 000000000..3342c6f96
--- /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, float sampleRate)
+ {
+ return adoptRef(new LowPass2FilterNode(context, sampleRate));
+ }
+
+ AudioParam* cutoff() { return biquadProcessor()->parameter1(); }
+ AudioParam* resonance() { return biquadProcessor()->parameter2(); }
+
+private:
+ LowPass2FilterNode(AudioContext*, float 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 000000000..310c21e47
--- /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/MediaElementAudioSourceNode.cpp b/Source/WebCore/webaudio/MediaElementAudioSourceNode.cpp
new file mode 100644
index 000000000..360ec1b8b
--- /dev/null
+++ b/Source/WebCore/webaudio/MediaElementAudioSourceNode.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011, 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) && ENABLE(VIDEO)
+
+#include "MediaElementAudioSourceNode.h"
+
+#include "AudioContext.h"
+#include "AudioNodeOutput.h"
+#include "Logging.h"
+#include "MediaPlayer.h"
+#include <wtf/Locker.h>
+
+// These are somewhat arbitrary limits, but we need to do some kind of sanity-checking.
+const unsigned minSampleRate = 8000;
+const unsigned maxSampleRate = 192000;
+
+namespace WebCore {
+
+PassRefPtr<MediaElementAudioSourceNode> MediaElementAudioSourceNode::create(AudioContext* context, HTMLMediaElement* mediaElement)
+{
+ return adoptRef(new MediaElementAudioSourceNode(context, mediaElement));
+}
+
+MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* context, HTMLMediaElement* mediaElement)
+ : AudioSourceNode(context, context->sampleRate())
+ , m_mediaElement(mediaElement)
+ , m_sourceNumberOfChannels(0)
+ , m_sourceSampleRate(0)
+{
+ // Default to stereo. This could change depending on what the media element .src is set to.
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setNodeType(NodeTypeMediaElementAudioSource);
+
+ initialize();
+}
+
+MediaElementAudioSourceNode::~MediaElementAudioSourceNode()
+{
+ m_mediaElement->setAudioSourceNode(0);
+ uninitialize();
+}
+
+void MediaElementAudioSourceNode::setFormat(size_t numberOfChannels, float sourceSampleRate)
+{
+ if (numberOfChannels != m_sourceNumberOfChannels || sourceSampleRate != m_sourceSampleRate) {
+ // FIXME: implement multi-channel greater than stereo.
+ // https://bugs.webkit.org/show_bug.cgi?id=75119
+ if (!numberOfChannels || numberOfChannels > 2 || sourceSampleRate < minSampleRate || sourceSampleRate > maxSampleRate) {
+ // process() will generate silence for these uninitialized values.
+ LOG(Media, "MediaElementAudioSourceNode::setFormat(%u, %f) - unhandled format change", static_cast<unsigned>(numberOfChannels), sourceSampleRate);
+ m_sourceNumberOfChannels = 0;
+ m_sourceSampleRate = 0;
+ return;
+ }
+
+ m_sourceNumberOfChannels = numberOfChannels;
+ m_sourceSampleRate = sourceSampleRate;
+
+ // Synchronize with process().
+ Locker<MediaElementAudioSourceNode> locker(*this);
+
+ if (sourceSampleRate != sampleRate()) {
+ double scaleFactor = sourceSampleRate / sampleRate();
+ m_multiChannelResampler = adoptPtr(new MultiChannelResampler(scaleFactor, numberOfChannels));
+ } else {
+ // Bypass resampling.
+ m_multiChannelResampler.clear();
+ }
+
+ {
+ // The context must be locked when changing the number of output channels.
+ AudioContext::AutoLocker contextLocker(context());
+
+ // Do any necesssary re-configuration to the output's number of channels.
+ output(0)->setNumberOfChannels(numberOfChannels);
+ }
+ }
+}
+
+void MediaElementAudioSourceNode::process(size_t numberOfFrames)
+{
+ AudioBus* outputBus = output(0)->bus();
+
+ if (!mediaElement() || !m_sourceNumberOfChannels || !m_sourceSampleRate) {
+ outputBus->zero();
+ return;
+ }
+
+ // Use a tryLock() to avoid contention in the real-time audio thread.
+ // If we fail to acquire the lock then the HTMLMediaElement must be in the middle of
+ // reconfiguring its playback engine, so we output silence in this case.
+ if (m_processLock.tryLock()) {
+ if (AudioSourceProvider* provider = mediaElement()->audioSourceProvider()) {
+ if (m_multiChannelResampler.get()) {
+ ASSERT(m_sourceSampleRate != sampleRate());
+ m_multiChannelResampler->process(provider, outputBus, numberOfFrames);
+ } else {
+ // Bypass the resampler completely if the source is at the context's sample-rate.
+ ASSERT(m_sourceSampleRate == sampleRate());
+ provider->provideInput(outputBus, numberOfFrames);
+ }
+ } else {
+ // Either this port doesn't yet support HTMLMediaElement audio stream access,
+ // or the stream is not yet available.
+ outputBus->zero();
+ }
+ m_processLock.unlock();
+ } else {
+ // We failed to acquire the lock.
+ outputBus->zero();
+ }
+}
+
+void MediaElementAudioSourceNode::reset()
+{
+}
+
+void MediaElementAudioSourceNode::lock()
+{
+ ref();
+ m_processLock.lock();
+}
+
+void MediaElementAudioSourceNode::unlock()
+{
+ m_processLock.unlock();
+ deref();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/MediaElementAudioSourceNode.h b/Source/WebCore/webaudio/MediaElementAudioSourceNode.h
new file mode 100644
index 000000000..06ec7e16b
--- /dev/null
+++ b/Source/WebCore/webaudio/MediaElementAudioSourceNode.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011, 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 MediaElementAudioSourceNode_h
+#define MediaElementAudioSourceNode_h
+
+#if ENABLE(VIDEO)
+
+#include "AudioSourceNode.h"
+#include "AudioSourceProviderClient.h"
+#include "HTMLMediaElement.h"
+#include "MultiChannelResampler.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioContext;
+
+class MediaElementAudioSourceNode : public AudioSourceNode, public AudioSourceProviderClient {
+public:
+ static PassRefPtr<MediaElementAudioSourceNode> create(AudioContext*, HTMLMediaElement*);
+
+ virtual ~MediaElementAudioSourceNode();
+
+ HTMLMediaElement* mediaElement() { return m_mediaElement.get(); }
+
+ // AudioNode
+ virtual void process(size_t framesToProcess);
+ virtual void reset();
+
+ // AudioSourceProviderClient
+ virtual void setFormat(size_t numberOfChannels, float sampleRate);
+
+ void lock();
+ void unlock();
+
+private:
+ MediaElementAudioSourceNode(AudioContext*, HTMLMediaElement*);
+
+ RefPtr<HTMLMediaElement> m_mediaElement;
+ Mutex m_processLock;
+
+ unsigned m_sourceNumberOfChannels;
+ double m_sourceSampleRate;
+
+ OwnPtr<MultiChannelResampler> m_multiChannelResampler;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO)
+
+#endif // MediaElementAudioSourceNode_h
diff --git a/Source/WebCore/webaudio/MediaElementAudioSourceNode.idl b/Source/WebCore/webaudio/MediaElementAudioSourceNode.idl
new file mode 100644
index 000000000..27c67d3a3
--- /dev/null
+++ b/Source/WebCore/webaudio/MediaElementAudioSourceNode.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011, 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&VIDEO,
+ GenerateToJS
+ ] MediaElementAudioSourceNode : AudioSourceNode {
+ readonly attribute HTMLMediaElement mediaElement;
+ };
+}
diff --git a/Source/WebCore/webaudio/OfflineAudioCompletionEvent.cpp b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.cpp
new file mode 100644
index 000000000..7f8243f40
--- /dev/null
+++ b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011, 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 "OfflineAudioCompletionEvent.h"
+
+#include "AudioBuffer.h"
+#include "EventNames.h"
+
+namespace WebCore {
+
+PassRefPtr<OfflineAudioCompletionEvent> OfflineAudioCompletionEvent::create()
+{
+ return adoptRef(new OfflineAudioCompletionEvent);
+}
+
+PassRefPtr<OfflineAudioCompletionEvent> OfflineAudioCompletionEvent::create(PassRefPtr<AudioBuffer> renderedBuffer)
+{
+ return adoptRef(new OfflineAudioCompletionEvent(renderedBuffer));
+}
+
+OfflineAudioCompletionEvent::OfflineAudioCompletionEvent()
+{
+}
+
+
+OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(PassRefPtr<AudioBuffer> renderedBuffer)
+ : Event(eventNames().completeEvent, true, false)
+ , m_renderedBuffer(renderedBuffer)
+{
+}
+
+OfflineAudioCompletionEvent::~OfflineAudioCompletionEvent()
+{
+}
+
+const AtomicString& OfflineAudioCompletionEvent::interfaceName() const
+{
+ return eventNames().interfaceForOfflineAudioCompletionEvent;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/OfflineAudioCompletionEvent.h b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.h
new file mode 100644
index 000000000..409aeaf67
--- /dev/null
+++ b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011, 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 OfflineAudioCompletionEvent_h
+#define OfflineAudioCompletionEvent_h
+
+#include "AudioBuffer.h"
+#include "Event.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+
+class OfflineAudioCompletionEvent : public Event {
+public:
+ static PassRefPtr<OfflineAudioCompletionEvent> create();
+ static PassRefPtr<OfflineAudioCompletionEvent> create(PassRefPtr<AudioBuffer> renderedBuffer);
+
+ virtual ~OfflineAudioCompletionEvent();
+
+ AudioBuffer* renderedBuffer() { return m_renderedBuffer.get(); }
+
+ virtual const AtomicString& interfaceName() const;
+
+private:
+ OfflineAudioCompletionEvent();
+ OfflineAudioCompletionEvent(PassRefPtr<AudioBuffer> renderedBuffer);
+
+ RefPtr<AudioBuffer> m_renderedBuffer;
+};
+
+} // namespace WebCore
+
+#endif // OfflineAudioCompletionEvent_h
diff --git a/Source/WebCore/webaudio/OfflineAudioCompletionEvent.idl b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.idl
new file mode 100644
index 000000000..cd5ccb091
--- /dev/null
+++ b/Source/WebCore/webaudio/OfflineAudioCompletionEvent.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011, 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
+ ] OfflineAudioCompletionEvent : Event {
+ readonly attribute AudioBuffer renderedBuffer;
+ };
+}
diff --git a/Source/WebCore/webaudio/OfflineAudioDestinationNode.cpp b/Source/WebCore/webaudio/OfflineAudioDestinationNode.cpp
new file mode 100644
index 000000000..ae1835b9a
--- /dev/null
+++ b/Source/WebCore/webaudio/OfflineAudioDestinationNode.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011, 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 "OfflineAudioDestinationNode.h"
+
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "HRTFDatabaseLoader.h"
+#include <algorithm>
+#include <wtf/MainThread.h>
+
+using namespace std;
+
+namespace WebCore {
+
+const size_t renderQuantumSize = 128;
+
+OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext* context, AudioBuffer* renderTarget)
+ : AudioDestinationNode(context, renderTarget->sampleRate())
+ , m_renderTarget(renderTarget)
+ , m_renderThread(0)
+ , m_startedRendering(false)
+{
+ m_renderBus = adoptPtr(new AudioBus(renderTarget->numberOfChannels(), renderQuantumSize));
+
+ initialize();
+}
+
+OfflineAudioDestinationNode::~OfflineAudioDestinationNode()
+{
+ uninitialize();
+}
+
+void OfflineAudioDestinationNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ AudioNode::initialize();
+}
+
+void OfflineAudioDestinationNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ if (m_renderThread) {
+ waitForThreadCompletion(m_renderThread, 0);
+ m_renderThread = 0;
+ }
+
+ AudioNode::uninitialize();
+}
+
+void OfflineAudioDestinationNode::startRendering()
+{
+ ASSERT(isMainThread());
+ ASSERT(m_renderTarget.get());
+ if (!m_renderTarget.get())
+ return;
+
+ if (!m_startedRendering) {
+ m_startedRendering = true;
+ ref(); // See corresponding deref() call in notifyCompleteDispatch().
+ m_renderThread = createThread(OfflineAudioDestinationNode::renderEntry, this, "offline renderer");
+ }
+}
+
+// Do offline rendering in this thread.
+void* OfflineAudioDestinationNode::renderEntry(void* threadData)
+{
+ OfflineAudioDestinationNode* destinationNode = reinterpret_cast<OfflineAudioDestinationNode*>(threadData);
+ ASSERT(destinationNode);
+ destinationNode->render();
+
+ return 0;
+}
+
+void OfflineAudioDestinationNode::render()
+{
+ ASSERT(!isMainThread());
+ ASSERT(m_renderBus.get());
+ if (!m_renderBus.get())
+ return;
+
+ bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numberOfChannels();
+ ASSERT(channelsMatch);
+ if (!channelsMatch)
+ return;
+
+ bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize;
+ ASSERT(isRenderBusAllocated);
+ if (!isRenderBusAllocated)
+ return;
+
+ // Synchronize with HRTFDatabaseLoader.
+ // The database must be loaded before we can proceed.
+ HRTFDatabaseLoader* loader = HRTFDatabaseLoader::loader();
+ ASSERT(loader);
+ if (!loader)
+ return;
+
+ loader->waitForLoaderThreadCompletion();
+
+ // Break up the render target into smaller "render quantize" sized pieces.
+ // Render until we're finished.
+ size_t framesToProcess = m_renderTarget->length();
+ unsigned numberOfChannels = m_renderTarget->numberOfChannels();
+
+ unsigned n = 0;
+ while (framesToProcess > 0) {
+ // Render one render quantum.
+ provideInput(m_renderBus.get(), renderQuantumSize);
+
+ size_t framesAvailableToCopy = min(framesToProcess, renderQuantumSize);
+
+ for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
+ float* source = m_renderBus->channel(channelIndex)->data();
+ float* destination = m_renderTarget->getChannelData(channelIndex)->data();
+ memcpy(destination + n, source, sizeof(float) * framesAvailableToCopy);
+ }
+
+ n += framesAvailableToCopy;
+ framesToProcess -= framesAvailableToCopy;
+ }
+
+ // Our work is done. Let the AudioContext know.
+ callOnMainThread(notifyCompleteDispatch, this);
+}
+
+void OfflineAudioDestinationNode::notifyCompleteDispatch(void* userData)
+{
+ OfflineAudioDestinationNode* destinationNode = static_cast<OfflineAudioDestinationNode*>(userData);
+ ASSERT(destinationNode);
+ if (!destinationNode)
+ return;
+
+ destinationNode->notifyComplete();
+ destinationNode->deref();
+}
+
+void OfflineAudioDestinationNode::notifyComplete()
+{
+ context()->fireCompletionEvent();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/OfflineAudioDestinationNode.h b/Source/WebCore/webaudio/OfflineAudioDestinationNode.h
new file mode 100644
index 000000000..c4d567a5d
--- /dev/null
+++ b/Source/WebCore/webaudio/OfflineAudioDestinationNode.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011, 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 OfflineAudioDestinationNode_h
+#define OfflineAudioDestinationNode_h
+
+#include "AudioBuffer.h"
+#include "AudioDestinationNode.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioBus;
+class AudioContext;
+
+class OfflineAudioDestinationNode : public AudioDestinationNode {
+public:
+ static PassRefPtr<OfflineAudioDestinationNode> create(AudioContext* context, AudioBuffer* renderTarget)
+ {
+ return adoptRef(new OfflineAudioDestinationNode(context, renderTarget));
+ }
+
+ virtual ~OfflineAudioDestinationNode();
+
+ // AudioNode
+ virtual void initialize();
+ virtual void uninitialize();
+
+ float sampleRate() const { return m_renderTarget->sampleRate(); }
+
+ void startRendering();
+
+private:
+ OfflineAudioDestinationNode(AudioContext*, AudioBuffer* renderTarget);
+
+ // This AudioNode renders into this AudioBuffer.
+ RefPtr<AudioBuffer> m_renderTarget;
+
+ // Temporary AudioBus for each render quantum.
+ OwnPtr<AudioBus> m_renderBus;
+
+ // Rendering thread.
+ volatile ThreadIdentifier m_renderThread;
+ bool m_startedRendering;
+ static void* renderEntry(void* threadData);
+ void render();
+
+ // For completion callback on main thread.
+ static void notifyCompleteDispatch(void* userData);
+ void notifyComplete();
+};
+
+} // namespace WebCore
+
+#endif // OfflineAudioDestinationNode_h
diff --git a/Source/WebCore/webaudio/RealtimeAnalyser.cpp b/Source/WebCore/webaudio/RealtimeAnalyser.cpp
new file mode 100644
index 000000000..ccd865522
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyser.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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"
+
+#include <algorithm>
+#include <limits.h>
+#include <wtf/Complex.h>
+#include <wtf/Float32Array.h>
+#include <wtf/MainThread.h>
+#include <wtf/MathExtras.h>
+#include <wtf/Uint8Array.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;
+// All FFT implementations are expected to handle power-of-two sizes MinFFTSize <= size <= MaxFFTSize.
+const unsigned RealtimeAnalyser::MinFFTSize = 128;
+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 || size < MinFFTSize) {
+ // FIXME: It would be good to also set an exception.
+ return;
+ }
+
+ if (m_fftSize != size) {
+ m_analysisFrame = adoptPtr(new FFTFrame(size));
+ // m_magnitudeBuffer has size = fftSize / 2 because it contains floats reduced from complex values in m_analysisFrame.
+ m_magnitudeBuffer.allocate(size / 2);
+ 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.
+ unsigned writeIndex = m_writeIndex;
+ if (writeIndex < fftSize) {
+ memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, sizeof(*tempP) * (fftSize - writeIndex));
+ memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * writeIndex);
+ } else
+ memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize);
+
+
+ // Window the input samples.
+ applyWindow(tempP, fftSize);
+
+ // Do the analysis.
+ m_analysisFrame->doFFT(tempP);
+
+ 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();
+ size_t n = magnitudeBuffer().size();
+ for (size_t 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);
+ }
+}
+
+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);
+ }
+ }
+}
+
+} // 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 000000000..242eacaf0
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyser.h
@@ -0,0 +1,99 @@
+/*
+ * 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/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+class FFTFrame;
+
+class RealtimeAnalyser {
+ WTF_MAKE_NONCOPYABLE(RealtimeAnalyser);
+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); }
+
+ void getFloatFrequencyData(Float32Array*);
+ void getByteFrequencyData(Uint8Array*);
+ void getByteTimeDomainData(Uint8Array*);
+
+ // 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 MinFFTSize;
+ 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 000000000..8d74a67a8
--- /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, float sampleRate)
+ : AudioNode(context, sampleRate)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+ addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
+
+ setNodeType(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 000000000..c37c6c1b5
--- /dev/null
+++ b/Source/WebCore/webaudio/RealtimeAnalyserNode.h
@@ -0,0 +1,75 @@
+/*
+ * 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"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class RealtimeAnalyserNode : public AudioNode {
+public:
+ static PassRefPtr<RealtimeAnalyserNode> create(AudioContext* context, float 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(); }
+
+ void getFloatFrequencyData(Float32Array* array) { m_analyser.getFloatFrequencyData(array); }
+ void getByteFrequencyData(Uint8Array* array) { m_analyser.getByteFrequencyData(array); }
+ void getByteTimeDomainData(Uint8Array* array) { m_analyser.getByteTimeDomainData(array); }
+
+private:
+ RealtimeAnalyserNode(AudioContext*, float 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 000000000..1f7bf7a7d
--- /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.
+ void getFloatFrequencyData(in Float32Array array);
+ void getByteFrequencyData(in Uint8Array array);
+
+ // Real-time waveform data
+ void getByteTimeDomainData(in Uint8Array array);
+ };
+}
diff --git a/Source/WebCore/webaudio/WaveShaperDSPKernel.cpp b/Source/WebCore/webaudio/WaveShaperDSPKernel.cpp
new file mode 100644
index 000000000..5f3cf3476
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperDSPKernel.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011, 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 "WaveShaperDSPKernel.h"
+
+#include "WaveShaperProcessor.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace WebCore {
+
+void WaveShaperDSPKernel::process(const float* source, float* destination, size_t framesToProcess)
+{
+ ASSERT(source && destination && waveShaperProcessor());
+
+ Float32Array* curve = waveShaperProcessor()->curve();
+ if (!curve) {
+ // Act as "straight wire" pass-through if no curve is set.
+ memcpy(destination, source, sizeof(float) * framesToProcess);
+ return;
+ }
+
+ float* curveData = curve->data();
+ int curveLength = curve->length();
+
+ ASSERT(curveData);
+
+ if (!curveData || !curveLength) {
+ memcpy(destination, source, sizeof(float) * framesToProcess);
+ return;
+ }
+
+ // Apply waveshaping curve.
+ for (unsigned i = 0; i < framesToProcess; ++i) {
+ const float input = source[i];
+
+ // Calculate an index based on input -1 -> +1 with 0 being at the center of the curve data.
+ int index = (curveLength * (input + 1)) / 2;
+
+ // Clip index to the input range of the curve.
+ // This takes care of input outside of nominal range -1 -> +1
+ index = max(index, 0);
+ index = min(index, curveLength - 1);
+ destination[i] = curveData[index];
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/WaveShaperDSPKernel.h b/Source/WebCore/webaudio/WaveShaperDSPKernel.h
new file mode 100644
index 000000000..c725f4dfe
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperDSPKernel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011, 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 WaveShaperDSPKernel_h
+#define WaveShaperDSPKernel_h
+
+#include "AudioDSPKernel.h"
+#include "WaveShaperProcessor.h"
+
+namespace WebCore {
+
+class WaveShaperProcessor;
+
+// WaveShaperDSPKernel is an AudioDSPKernel and is responsible for non-linear distortion on one channel.
+
+class WaveShaperDSPKernel : public AudioDSPKernel {
+public:
+ WaveShaperDSPKernel(WaveShaperProcessor* processor)
+ : AudioDSPKernel(processor)
+ {
+ }
+
+ // AudioDSPKernel
+ virtual void process(const float* source, float* dest, size_t framesToProcess);
+ virtual void reset() { }
+
+protected:
+ WaveShaperProcessor* waveShaperProcessor() { return static_cast<WaveShaperProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // WaveShaperDSPKernel_h
diff --git a/Source/WebCore/webaudio/WaveShaperNode.cpp b/Source/WebCore/webaudio/WaveShaperNode.cpp
new file mode 100644
index 000000000..03504e777
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperNode.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011, 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 "WaveShaperNode.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+WaveShaperNode::WaveShaperNode(AudioContext* context)
+ : AudioBasicProcessorNode(context, context->sampleRate())
+{
+ m_processor = adoptPtr(new WaveShaperProcessor(context->sampleRate(), 1));
+ setNodeType(NodeTypeWaveShaper);
+}
+
+void WaveShaperNode::setCurve(Float32Array* curve)
+{
+ ASSERT(isMainThread());
+ waveShaperProcessor()->setCurve(curve);
+}
+
+Float32Array* WaveShaperNode::curve()
+{
+ return waveShaperProcessor()->curve();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/WaveShaperNode.h b/Source/WebCore/webaudio/WaveShaperNode.h
new file mode 100644
index 000000000..cb1ccf603
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperNode.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011, 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 WaveShaperNode_h
+#define WaveShaperNode_h
+
+#include "AudioBasicProcessorNode.h"
+#include "BiquadProcessor.h"
+#include "WaveShaperProcessor.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class WaveShaperNode : public AudioBasicProcessorNode {
+public:
+ static PassRefPtr<WaveShaperNode> create(AudioContext* context)
+ {
+ return adoptRef(new WaveShaperNode(context));
+ }
+
+ // setCurve() is called on the main thread.
+ void setCurve(Float32Array*);
+ Float32Array* curve();
+
+private:
+ WaveShaperNode(AudioContext*);
+
+ WaveShaperProcessor* waveShaperProcessor() { return static_cast<WaveShaperProcessor*>(processor()); }
+};
+
+} // namespace WebCore
+
+#endif // WaveShaperNode_h
diff --git a/Source/WebCore/webaudio/WaveShaperNode.idl b/Source/WebCore/webaudio/WaveShaperNode.idl
new file mode 100644
index 000000000..19c61a6d0
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperNode.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011, 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
+ ] WaveShaperNode : AudioNode {
+ attribute [JSCCustomSetter] Float32Array curve;
+ };
+}
diff --git a/Source/WebCore/webaudio/WaveShaperProcessor.cpp b/Source/WebCore/webaudio/WaveShaperProcessor.cpp
new file mode 100644
index 000000000..f7571debb
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperProcessor.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011, 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 "WaveShaperProcessor.h"
+
+#include "WaveShaperDSPKernel.h"
+
+namespace WebCore {
+
+WaveShaperProcessor::WaveShaperProcessor(float sampleRate, size_t numberOfChannels)
+ : AudioDSPKernelProcessor(sampleRate, numberOfChannels)
+{
+}
+
+WaveShaperProcessor::~WaveShaperProcessor()
+{
+ if (isInitialized())
+ uninitialize();
+}
+
+PassOwnPtr<AudioDSPKernel> WaveShaperProcessor::createKernel()
+{
+ return adoptPtr(new WaveShaperDSPKernel(this));
+}
+
+void WaveShaperProcessor::setCurve(Float32Array* curve)
+{
+ // This synchronizes with process().
+ MutexLocker processLocker(m_processLock);
+
+ m_curve = curve;
+}
+
+void WaveShaperProcessor::process(AudioBus* source, AudioBus* destination, size_t framesToProcess)
+{
+ if (!isInitialized()) {
+ destination->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()) {
+ // For each channel of our input, process using the corresponding WaveShaperDSPKernel 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);
+
+ m_processLock.unlock();
+ } else {
+ // Too bad - the tryLock() failed. We must be in the middle of a setCurve() call.
+ destination->zero();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/webaudio/WaveShaperProcessor.h b/Source/WebCore/webaudio/WaveShaperProcessor.h
new file mode 100644
index 000000000..4016e8bde
--- /dev/null
+++ b/Source/WebCore/webaudio/WaveShaperProcessor.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011, 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 WaveShaperProcessor_h
+#define WaveShaperProcessor_h
+
+#include "AudioDSPKernel.h"
+#include "AudioDSPKernelProcessor.h"
+#include "AudioNode.h"
+#include <wtf/Float32Array.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+// WaveShaperProcessor is an AudioDSPKernelProcessor which uses WaveShaperDSPKernel objects to implement non-linear distortion effects.
+
+class WaveShaperProcessor : public AudioDSPKernelProcessor {
+public:
+ WaveShaperProcessor(float sampleRate, size_t numberOfChannels);
+
+ virtual ~WaveShaperProcessor();
+
+ virtual PassOwnPtr<AudioDSPKernel> createKernel();
+
+ virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess);
+
+ void setCurve(Float32Array*);
+ Float32Array* curve() { return m_curve.get(); }
+
+private:
+ // m_curve represents the non-linear shaping curve.
+ RefPtr<Float32Array> m_curve;
+
+ // This synchronizes process() with setCurve().
+ mutable Mutex m_processLock;
+};
+
+} // namespace WebCore
+
+#endif // WaveShaperProcessor_h