summaryrefslogtreecommitdiffstats
path: root/WebCore/webaudio
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/webaudio')
-rw-r--r--WebCore/webaudio/AudioContext.cpp467
-rw-r--r--WebCore/webaudio/AudioContext.h248
-rw-r--r--WebCore/webaudio/AudioContext.idl70
-rw-r--r--WebCore/webaudio/AudioDestinationNode.cpp115
-rw-r--r--WebCore/webaudio/AudioDestinationNode.h76
-rw-r--r--WebCore/webaudio/AudioDestinationNode.idl36
-rw-r--r--WebCore/webaudio/AudioGain.h53
-rw-r--r--WebCore/webaudio/AudioGain.idl35
-rw-r--r--WebCore/webaudio/AudioNode.cpp311
-rw-r--r--WebCore/webaudio/AudioNode.h174
-rw-r--r--WebCore/webaudio/AudioNode.idl40
-rw-r--r--WebCore/webaudio/AudioParam.h9
12 files changed, 1633 insertions, 1 deletions
diff --git a/WebCore/webaudio/AudioContext.cpp b/WebCore/webaudio/AudioContext.cpp
new file mode 100644
index 0000000..6ca8ee1
--- /dev/null
+++ b/WebCore/webaudio/AudioContext.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 "AudioContext.h"
+
+#include "AudioBuffer.h"
+#include "AudioBufferSourceNode.h"
+#include "AudioChannelMerger.h"
+#include "AudioChannelSplitter.h"
+#include "AudioGainNode.h"
+#include "AudioListener.h"
+#include "AudioPannerNode.h"
+#include "CachedAudio.h"
+#include "ConvolverNode.h"
+#include "DelayNode.h"
+#include "Document.h"
+#include "HRTFDatabaseLoader.h"
+#include "HRTFPanner.h"
+#include "HTMLNames.h"
+#include "HighPass2FilterNode.h"
+#include "JavaScriptAudioNode.h"
+#include "LowPass2FilterNode.h"
+#include "PlatformString.h"
+#include "RealtimeAnalyserNode.h"
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefCounted.h>
+
+// FIXME: check the proper way to reference an undefined thread ID
+const int UndefinedThreadIdentifier = 0xffffffff;
+
+namespace WebCore {
+
+PassRefPtr<CachedAudio> AudioContext::createAudioRequest(const String &url, bool mixToMono)
+{
+ lazyInitialize();
+
+ // Convert relative URL to absolute
+ KURL completedURL = document()->completeURL(url);
+ String completedURLString = completedURL.string();
+
+ RefPtr<CachedAudio> cachedAudio = CachedAudio::create(completedURLString, this, document(), sampleRate(), mixToMono);
+ CachedAudio* c = cachedAudio.get();
+
+ m_cachedAudioReferences.append(c);
+
+ return cachedAudio;
+}
+
+PassRefPtr<AudioContext> AudioContext::create(Document* document)
+{
+ return adoptRef(new AudioContext(document));
+}
+
+AudioContext::AudioContext(Document* document)
+ : ActiveDOMObject(document, this)
+ , m_isInitialized(false)
+ , m_isAudioThreadFinished(false)
+ , m_document(document)
+ , m_destinationNode(0)
+ , m_connectionCount(0)
+ , m_audioThread(0)
+ , m_graphOwnerThread(UndefinedThreadIdentifier)
+{
+ // Note: because adoptRef() won't be called until we leave this constructor, but code in this constructor needs to reference this context,
+ // relax the check.
+ relaxAdoptionRequirement();
+
+ m_destinationNode = AudioDestinationNode::create(this);
+ m_listener = AudioListener::create();
+ m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
+ m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
+
+ // This sets in motion an asynchronous loading mechanism on another thread.
+ // We can check 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).
+ hrtfDatabaseLoader()->loadAsynchronously(sampleRate());
+}
+
+AudioContext::~AudioContext()
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: AudioContext::~AudioContext()\n", this);
+#endif
+ // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
+ ASSERT(!m_nodesToDelete.size());
+ ASSERT(!m_referencedNodes.size());
+ ASSERT(!m_finishedNodes.size());
+}
+
+void AudioContext::lazyInitialize()
+{
+ if (!m_isInitialized) {
+ // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
+ ASSERT(!m_isAudioThreadFinished);
+ if (!m_isAudioThreadFinished) {
+ if (m_destinationNode.get()) {
+ // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
+ // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
+ m_destinationNode->initialize();
+ }
+ m_isInitialized = true;
+ }
+ }
+}
+
+void AudioContext::uninitialize()
+{
+ if (m_isInitialized) {
+ // This stops the audio thread and all audio rendering.
+ m_destinationNode->uninitialize();
+
+ // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
+ m_isAudioThreadFinished = true;
+
+ // We have to release our reference to the destination node before the context will ever be deleted since the destination node holds a reference to the context.
+ m_destinationNode.clear();
+
+ // Get rid of the sources which may still be playing.
+ derefUnfinishedSourceNodes();
+
+ // Because the AudioBuffers are garbage collected, we can't delete them here.
+ // Instead, at least release the potentially large amount of allocated memory for the audio data.
+ // Note that we do this *after* the context is uninitialized and stops processing audio.
+ for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i)
+ m_allocatedBuffers[i]->releaseMemory();
+ m_allocatedBuffers.clear();
+
+ m_isInitialized = false;
+ }
+}
+
+bool AudioContext::isInitialized() const
+{
+ return m_isInitialized;
+}
+
+bool AudioContext::isRunnable() const
+{
+ if (!isInitialized())
+ return false;
+
+ // Check with the HRTF spatialization system to see if it's finished loading.
+ return hrtfDatabaseLoader()->isLoaded();
+}
+
+void AudioContext::stop()
+{
+ m_document = 0; // document is going away
+ uninitialize();
+}
+
+Document* AudioContext::document()
+{
+ ASSERT(m_document);
+ return m_document;
+}
+
+bool AudioContext::hasDocument()
+{
+ return m_document;
+}
+
+void AudioContext::refBuffer(PassRefPtr<AudioBuffer> buffer)
+{
+ m_allocatedBuffers.append(buffer);
+}
+
+PassRefPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
+{
+ return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
+}
+
+PassRefPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
+
+ refNode(node.get()); // context keeps reference until source has finished playing
+ return node;
+}
+
+PassRefPtr<JavaScriptAudioNode> AudioContext::createJavaScriptNode(size_t bufferSize)
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ RefPtr<JavaScriptAudioNode> node = JavaScriptAudioNode::create(this, m_destinationNode->sampleRate(), bufferSize);
+
+ refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
+ return node;
+}
+
+PassRefPtr<LowPass2FilterNode> AudioContext::createLowPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return LowPass2FilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<HighPass2FilterNode> AudioContext::createHighPass2Filter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return HighPass2FilterNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioPannerNode> AudioContext::createPanner()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioPannerNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<ConvolverNode> AudioContext::createConvolver()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return ConvolverNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<RealtimeAnalyserNode> AudioContext::createAnalyser()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return RealtimeAnalyserNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioGainNode> AudioContext::createGainNode()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioGainNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<DelayNode> AudioContext::createDelayNode()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return DelayNode::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioChannelSplitter> AudioContext::createChannelSplitter()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioChannelSplitter::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<AudioChannelMerger> AudioContext::createChannelMerger()
+{
+ ASSERT(isMainThread());
+ lazyInitialize();
+ return AudioChannelMerger::create(this, m_destinationNode->sampleRate());
+}
+
+void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
+{
+ ASSERT(isAudioThread());
+ m_finishedNodes.append(node);
+}
+
+void AudioContext::derefFinishedSourceNodes()
+{
+ ASSERT(isGraphOwner());
+ ASSERT(isAudioThread() || isAudioThreadFinished());
+ for (unsigned i = 0; i < m_finishedNodes.size(); i++)
+ derefNode(m_finishedNodes[i]);
+
+ m_finishedNodes.clear();
+}
+
+void AudioContext::refNode(AudioNode* node)
+{
+ ASSERT(isMainThread());
+ AutoLocker locker(this);
+
+ node->ref(AudioNode::RefTypeConnection);
+ m_referencedNodes.append(node);
+}
+
+void AudioContext::derefNode(AudioNode* node)
+{
+ ASSERT(isGraphOwner());
+
+ node->deref(AudioNode::RefTypeConnection);
+
+ for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
+ if (node == m_referencedNodes[i]) {
+ m_referencedNodes.remove(i);
+ break;
+ }
+ }
+}
+
+void AudioContext::derefUnfinishedSourceNodes()
+{
+ ASSERT(isMainThread() && isAudioThreadFinished());
+ for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
+ m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
+
+ m_referencedNodes.clear();
+}
+
+void AudioContext::lock(bool& mustReleaseLock)
+{
+ // Don't allow regular lock in real-time audio thread.
+ ASSERT(isMainThread());
+
+ ThreadIdentifier thisThread = currentThread();
+
+ if (thisThread == m_graphOwnerThread) {
+ // We already have the lock.
+ mustReleaseLock = false;
+ } else {
+ // Acquire the lock.
+ m_contextGraphMutex.lock();
+ mustReleaseLock = true;
+ }
+
+ m_graphOwnerThread = thisThread;
+}
+
+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()
+{
+ return currentThread() == m_audioThread;
+}
+
+bool AudioContext::isGraphOwner()
+{
+ return currentThread() == m_graphOwnerThread;
+}
+
+void AudioContext::addDeferredFinishDeref(AudioNode* node, AudioNode::RefType refType)
+{
+ ASSERT(isAudioThread());
+ m_deferredFinishDerefList.append(AudioContext::RefInfo(node, refType));
+}
+
+void AudioContext::handlePostRenderTasks()
+{
+ ASSERT(isAudioThread());
+
+ // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently.
+ // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed
+ // from the render graph (in which case they'll render silence).
+ bool mustReleaseLock;
+ if (tryLock(mustReleaseLock)) {
+ // Take care of finishing any derefs where the tryLock() failed previously.
+ handleDeferredFinishDerefs();
+
+ // Dynamically clean up nodes which are no longer needed.
+ derefFinishedSourceNodes();
+
+ // Finally actually delete.
+ deleteMarkedNodes();
+
+ if (mustReleaseLock)
+ unlock();
+ }
+}
+
+void AudioContext::handleDeferredFinishDerefs()
+{
+ ASSERT(isAudioThread());
+ for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
+ AudioNode* node = m_deferredFinishDerefList[i].m_node;
+ AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType;
+ node->finishDeref(refType);
+ }
+
+ m_deferredFinishDerefList.clear();
+}
+
+void AudioContext::markForDeletion(AudioNode* node)
+{
+ ASSERT(isGraphOwner());
+ m_nodesToDelete.append(node);
+}
+
+void AudioContext::deleteMarkedNodes()
+{
+ ASSERT(isGraphOwner() || isAudioThreadFinished());
+
+ // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
+ while (size_t n = m_nodesToDelete.size()) {
+ AudioNode* node = m_nodesToDelete[n - 1];
+ m_nodesToDelete.removeLast();
+ delete node;
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/webaudio/AudioContext.h b/WebCore/webaudio/AudioContext.h
new file mode 100644
index 0000000..f175bfe
--- /dev/null
+++ b/WebCore/webaudio/AudioContext.h
@@ -0,0 +1,248 @@
+/*
+ * 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 AudioContext_h
+#define AudioContext_h
+
+#include "ActiveDOMObject.h"
+#include "AudioBus.h"
+#include "AudioDestinationNode.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class AudioBuffer;
+class AudioBufferSourceNode;
+class AudioChannelMerger;
+class AudioChannelSplitter;
+class AudioGainNode;
+class AudioPannerNode;
+class AudioListener;
+class CachedAudio;
+class DelayNode;
+class Document;
+class LowPass2FilterNode;
+class HighPass2FilterNode;
+class ConvolverNode;
+class RealtimeAnalyserNode;
+class JavaScriptAudioNode;
+
+// AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
+// For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism.
+
+class AudioContext : public ActiveDOMObject, public RefCounted<AudioContext> {
+public:
+ static PassRefPtr<AudioContext> create(Document*);
+
+ virtual ~AudioContext();
+
+ bool isInitialized() const;
+
+ // Returns true when initialize() was called AND all asynchronous initialization has completed.
+ bool isRunnable() const;
+
+ // Document notification
+ virtual void stop();
+
+ Document* document(); // ASSERTs if document no longer exists.
+ bool hasDocument();
+
+ AudioDestinationNode* destination() { return m_destinationNode.get(); }
+ double currentTime() { return m_destinationNode->currentTime(); }
+ double sampleRate() { return m_destinationNode->sampleRate(); }
+
+ PassRefPtr<AudioBuffer> createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate);
+
+ PassRefPtr<CachedAudio> createAudioRequest(const String &url, bool mixToMono);
+
+ // Keep track of this buffer so we can release memory after the context is shut down...
+ void refBuffer(PassRefPtr<AudioBuffer> buffer);
+
+ AudioListener* listener() { return m_listener.get(); }
+
+ // The AudioNode create methods are called on the main thread (from JavaScript).
+ PassRefPtr<AudioBufferSourceNode> createBufferSource();
+ PassRefPtr<AudioGainNode> createGainNode();
+ PassRefPtr<DelayNode> createDelayNode();
+ PassRefPtr<LowPass2FilterNode> createLowPass2Filter();
+ PassRefPtr<HighPass2FilterNode> createHighPass2Filter();
+ PassRefPtr<AudioPannerNode> createPanner();
+ PassRefPtr<ConvolverNode> createConvolver();
+ PassRefPtr<RealtimeAnalyserNode> createAnalyser();
+ PassRefPtr<JavaScriptAudioNode> createJavaScriptNode(size_t bufferSize);
+ PassRefPtr<AudioChannelSplitter> createChannelSplitter();
+ PassRefPtr<AudioChannelMerger> createChannelMerger();
+
+ AudioBus* temporaryMonoBus() { return m_temporaryMonoBus.get(); }
+ AudioBus* temporaryStereoBus() { return m_temporaryStereoBus.get(); }
+
+ // When a source node has no more processing to do (has finished playing), then it tells the context to dereference it.
+ void notifyNodeFinishedProcessing(AudioNode*);
+
+ // Called at the end of each render quantum.
+ void handlePostRenderTasks();
+
+ // Called periodically at the end of each render quantum to dereference finished source nodes.
+ void derefFinishedSourceNodes();
+
+ // We reap all marked nodes at the end of each realtime render quantum in deleteMarkedNodes().
+ void markForDeletion(AudioNode*);
+ void deleteMarkedNodes();
+
+ // Keeps track of the number of connections made.
+ void incrementConnectionCount()
+ {
+ ASSERT(isMainThread());
+ m_connectionCount++;
+ }
+
+ unsigned connectionCount() const { return m_connectionCount; }
+
+ //
+ // Thread Safety and Graph Locking:
+ //
+
+ void setAudioThread(ThreadIdentifier thread) { m_audioThread = thread; } // FIXME: check either not initialized or the same
+ ThreadIdentifier audioThread() const { return m_audioThread; }
+ bool isAudioThread();
+
+ // 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();
+
+ 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();
+
+private:
+ AudioContext(Document*);
+ void lazyInitialize();
+ void uninitialize();
+
+ bool m_isInitialized;
+ bool m_isAudioThreadFinished;
+ bool m_isAudioThreadShutdown;
+
+ Document* m_document;
+
+ // The context itself keeps a reference to all source nodes. The source nodes, then reference all nodes they're connected to.
+ // In turn, these nodes reference all nodes they're connected to. All nodes are ultimately connected to the AudioDestinationNode.
+ // When the context dereferences a source node, it will be deactivated from the rendering graph along with all other nodes it is
+ // uniquely connected to. See the AudioNode::ref() and AudioNode::deref() methods for more details.
+ void refNode(AudioNode*);
+ void derefNode(AudioNode*);
+
+ // When the context goes away, there might still be some sources which haven't finished playing.
+ // Make sure to dereference them here.
+ void derefUnfinishedSourceNodes();
+
+ RefPtr<AudioDestinationNode> m_destinationNode;
+ RefPtr<AudioListener> m_listener;
+
+ // Only accessed in the main thread.
+ Vector<RefPtr<AudioBuffer> > m_allocatedBuffers;
+
+ // Only accessed in the audio thread.
+ Vector<AudioNode*> m_finishedNodes;
+
+ // We don't use RefPtr<AudioNode> here because AudioNode has a more complex ref() / deref() implementation
+ // with an optional argument for refType. We need to use the special refType: RefTypeConnection
+ // Either accessed when the graph lock is held, or on the main thread when the audio thread has finished.
+ Vector<AudioNode*> m_referencedNodes;
+
+ // Accumulate nodes which need to be deleted at the end of a render cycle (in realtime thread) here.
+ Vector<AudioNode*> m_nodesToDelete;
+
+ Vector<RefPtr<CachedAudio> > m_cachedAudioReferences;
+
+ 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;
+};
+
+} // WebCore
+
+#endif // AudioContext_h
diff --git a/WebCore/webaudio/AudioContext.idl b/WebCore/webaudio/AudioContext.idl
new file mode 100644
index 0000000..8951121
--- /dev/null
+++ b/WebCore/webaudio/AudioContext.idl
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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,
+ CanBeConstructed,
+ CustomConstructFunction,
+ V8CustomConstructor
+ ] AudioContext {
+ // All rendered audio ultimately connects to destination, which represents the audio hardware.
+ readonly attribute AudioDestinationNode destination;
+
+ // All scheduled times are relative to this time in seconds.
+ readonly attribute float currentTime;
+
+ // All AudioNodes in the context run at this sample-rate (in sample-frames per second).
+ readonly attribute float sampleRate;
+
+ // All panning is relative to this listener.
+ readonly attribute AudioListener listener;
+
+ AudioBuffer createBuffer(in unsigned long numberOfChannels, in unsigned long numberOfFrames, in float sampleRate);
+
+ // Source
+ AudioBufferSourceNode createBufferSource();
+
+ // Processing nodes
+ AudioGainNode createGainNode();
+ DelayNode createDelayNode();
+ LowPass2FilterNode createLowPass2Filter();
+ HighPass2FilterNode createHighPass2Filter();
+ AudioPannerNode createPanner();
+ ConvolverNode createConvolver();
+ RealtimeAnalyserNode createAnalyser();
+ JavaScriptAudioNode createJavaScriptNode(in unsigned long bufferSize);
+
+ // Channel splitting and merging
+ AudioChannelSplitter createChannelSplitter();
+ AudioChannelMerger createChannelMerger();
+
+ // FIXME: Temporary - to be replaced with XHR.
+ CachedAudio createAudioRequest(in DOMString url, in boolean mixToMono);
+ };
+}
diff --git a/WebCore/webaudio/AudioDestinationNode.cpp b/WebCore/webaudio/AudioDestinationNode.cpp
new file mode 100644
index 0000000..82f5145
--- /dev/null
+++ b/WebCore/webaudio/AudioDestinationNode.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 "AudioDestinationNode.h"
+
+#include "AudioBus.h"
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+AudioDestinationNode::AudioDestinationNode(AudioContext* context)
+ : AudioNode(context, AudioDestination::hardwareSampleRate())
+ , m_currentTime(0.0)
+{
+ addInput(adoptPtr(new AudioNodeInput(this)));
+
+ setType(NodeTypeDestination);
+
+ initialize();
+}
+
+AudioDestinationNode::~AudioDestinationNode()
+{
+ uninitialize();
+}
+
+void AudioDestinationNode::initialize()
+{
+ if (isInitialized())
+ return;
+
+ double hardwareSampleRate = AudioDestination::hardwareSampleRate();
+#ifndef NDEBUG
+ fprintf(stderr, ">>>> hardwareSampleRate = %f\n", hardwareSampleRate);
+#endif
+
+ m_destination = AudioDestination::create(*this, hardwareSampleRate);
+ m_destination->start();
+
+ m_isInitialized = true;
+}
+
+void AudioDestinationNode::uninitialize()
+{
+ if (!isInitialized())
+ return;
+
+ m_destination->stop();
+
+ m_isInitialized = false;
+}
+
+// The audio hardware calls us back here to gets its input stream.
+void AudioDestinationNode::provideInput(AudioBus* destinationBus, size_t numberOfFrames)
+{
+ context()->setAudioThread(currentThread());
+
+ if (!context()->isRunnable()) {
+ destinationBus->zero();
+ return;
+ }
+
+ // 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/WebCore/webaudio/AudioDestinationNode.h b/WebCore/webaudio/AudioDestinationNode.h
new file mode 100644
index 0000000..b130518
--- /dev/null
+++ b/WebCore/webaudio/AudioDestinationNode.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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 AudioDestinationNode_h
+#define AudioDestinationNode_h
+
+#include "AudioDestination.h"
+#include "AudioNode.h"
+#include "AudioSourceProvider.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+class AudioContext;
+
+class AudioDestinationNode : public AudioNode, public AudioSourceProvider {
+public:
+ static PassRefPtr<AudioDestinationNode> create(AudioContext* context)
+ {
+ return adoptRef(new AudioDestinationNode(context));
+ }
+
+ virtual ~AudioDestinationNode();
+
+ // AudioNode
+ virtual void process(size_t) { }; // we're pulled by hardware so this is never called
+ virtual void reset() { m_currentTime = 0.0; };
+ virtual void initialize();
+ virtual void uninitialize();
+
+ // The audio hardware calls here periodically to gets its input stream.
+ virtual void provideInput(AudioBus*, size_t numberOfFrames);
+
+ double currentTime() { return m_currentTime; }
+
+ double sampleRate() const { return m_destination->sampleRate(); }
+
+ unsigned numberOfChannels() const { return 2; } // FIXME: update when multi-channel (more than stereo) is supported
+
+private:
+ AudioDestinationNode(AudioContext*);
+
+ OwnPtr<AudioDestination> m_destination;
+ double m_currentTime;
+};
+
+} // namespace WebCore
+
+#endif // AudioDestinationNode_h
diff --git a/WebCore/webaudio/AudioDestinationNode.idl b/WebCore/webaudio/AudioDestinationNode.idl
new file mode 100644
index 0000000..1d2a235
--- /dev/null
+++ b/WebCore/webaudio/AudioDestinationNode.idl
@@ -0,0 +1,36 @@
+/*
+ * 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
+ ] AudioDestinationNode : AudioNode {
+ readonly attribute long numberOfChannels;
+ };
+}
diff --git a/WebCore/webaudio/AudioGain.h b/WebCore/webaudio/AudioGain.h
new file mode 100644
index 0000000..eb3c52d
--- /dev/null
+++ b/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/WebCore/webaudio/AudioGain.idl b/WebCore/webaudio/AudioGain.idl
new file mode 100644
index 0000000..ead7c9a
--- /dev/null
+++ b/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/WebCore/webaudio/AudioNode.cpp b/WebCore/webaudio/AudioNode.cpp
new file mode 100644
index 0000000..497ac95
--- /dev/null
+++ b/WebCore/webaudio/AudioNode.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "AudioNode.h"
+
+#include "AudioContext.h"
+#include "AudioNodeInput.h"
+#include "AudioNodeOutput.h"
+#include <wtf/Atomics.h>
+
+namespace WebCore {
+
+AudioNode::AudioNode(AudioContext* context, double sampleRate)
+ : m_isInitialized(false)
+ , m_type(NodeTypeUnknown)
+ , m_context(context)
+ , m_sampleRate(sampleRate)
+ , m_lastProcessingTime(-1.0)
+ , m_normalRefCount(1) // start out with normal refCount == 1 (like WTF::RefCounted class)
+ , m_connectionRefCount(0)
+ , m_disabledRefCount(0)
+ , m_isMarkedForDeletion(false)
+ , m_isDisabled(false)
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ if (!s_isNodeCountInitialized) {
+ s_isNodeCountInitialized = true;
+ atexit(AudioNode::printNodeCounts);
+ }
+#endif
+}
+
+AudioNode::~AudioNode()
+{
+#if DEBUG_AUDIONODE_REFERENCES
+ --s_nodeCount[type()];
+ printf("%p: %d: AudioNode::~AudioNode() %d %d %d\n", this, type(), m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+}
+
+void AudioNode::setType(NodeType type)
+{
+ m_type = type;
+
+#if DEBUG_AUDIONODE_REFERENCES
+ ++s_nodeCount[type];
+#endif
+}
+
+void AudioNode::lazyInitialize()
+{
+ if (!isInitialized())
+ initialize();
+}
+
+void AudioNode::addInput(PassOwnPtr<AudioNodeInput> input)
+{
+ m_inputs.append(input);
+}
+
+void AudioNode::addOutput(PassOwnPtr<AudioNodeOutput> output)
+{
+ m_outputs.append(output);
+}
+
+AudioNodeInput* AudioNode::input(unsigned i)
+{
+ return m_inputs[i].get();
+}
+
+AudioNodeOutput* AudioNode::output(unsigned i)
+{
+ return m_outputs[i].get();
+}
+
+bool AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ // Sanity check input and output indices.
+ if (outputIndex >= numberOfOutputs())
+ return false;
+ if (destination && inputIndex >= destination->numberOfInputs())
+ return false;
+
+ AudioNodeOutput* output = this->output(outputIndex);
+ if (!destination) {
+ // Disconnect output from any inputs it may be currently connected to.
+ output->disconnectAllInputs();
+ return true;
+ }
+
+ AudioNodeInput* input = destination->input(inputIndex);
+ input->connect(output);
+
+ // Let context know that a connection has been made.
+ context()->incrementConnectionCount();
+
+ return true;
+}
+
+bool AudioNode::disconnect(unsigned outputIndex)
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(context());
+
+ return connect(0, outputIndex);
+}
+
+void AudioNode::processIfNecessary(size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ if (!isInitialized())
+ return;
+
+ // Ensure that we only process once per rendering quantum.
+ // This handles the "fanout" problem where an output is connected to multiple inputs.
+ // The first time we're called during this time slice we process, but after that we don't want to re-process,
+ // instead our output(s) will already have the results cached in their bus;
+ double currentTime = context()->currentTime();
+ if (m_lastProcessingTime != currentTime) {
+ m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph
+ pullInputs(framesToProcess);
+ process(framesToProcess);
+ }
+}
+
+void AudioNode::pullInputs(size_t framesToProcess)
+{
+ ASSERT(context()->isAudioThread());
+
+ // Process all of the AudioNodes connected to our inputs.
+ for (unsigned i = 0; i < m_inputs.size(); ++i)
+ input(i)->pull(0, framesToProcess);
+}
+
+void AudioNode::ref(RefType refType)
+{
+ switch (refType) {
+ case RefTypeNormal:
+ atomicIncrement(&m_normalRefCount);
+ break;
+ case RefTypeConnection:
+ atomicIncrement(&m_connectionRefCount);
+ break;
+ case RefTypeDisabled:
+ atomicIncrement(&m_disabledRefCount);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: %d: AudioNode::ref(%d) %d %d %d\n", this, type(), refType, m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+
+ if (m_connectionRefCount == 1 && refType == RefTypeConnection) {
+ // FIXME: implement wake-up - this is an advanced feature and is not necessary in a simple implementation.
+ // We should not be "actively" connected to anything, but now we're "waking up"
+ // For example, a note which has finished playing, but is now being played again.
+ // Note that if this is considered a worthwhile feature to add, then an evaluation of the locking considerations must be made.
+ }
+}
+
+void AudioNode::deref(RefType refType)
+{
+ // The actually work for deref happens completely within the audio context's graph lock.
+ // In the case of the audio thread, we must use a tryLock to avoid glitches.
+ bool hasLock = false;
+ bool mustReleaseLock = false;
+
+ if (context()->isAudioThread()) {
+ // Real-time audio thread must not contend lock (to avoid glitches).
+ hasLock = context()->tryLock(mustReleaseLock);
+ } else {
+ context()->lock(mustReleaseLock);
+ hasLock = true;
+ }
+
+ if (hasLock) {
+ // This is where the real deref work happens.
+ finishDeref(refType);
+
+ if (mustReleaseLock)
+ context()->unlock();
+ } else {
+ // We were unable to get the lock, so put this in a list to finish up later.
+ ASSERT(context()->isAudioThread());
+ context()->addDeferredFinishDeref(this, refType);
+ }
+
+ // Once AudioContext::uninitialize() is called there's no more chances for deleteMarkedNodes() to get called, so we call here.
+ // We can't call in AudioContext::~AudioContext() since it will never be called as long as any AudioNode is alive
+ // because AudioNodes keep a reference to the context.
+ if (context()->isAudioThreadFinished())
+ context()->deleteMarkedNodes();
+}
+
+void AudioNode::finishDeref(RefType refType)
+{
+ ASSERT(context()->isGraphOwner());
+
+ switch (refType) {
+ case RefTypeNormal:
+ ASSERT(m_normalRefCount > 0);
+ atomicDecrement(&m_normalRefCount);
+ break;
+ case RefTypeConnection:
+ ASSERT(m_connectionRefCount > 0);
+ atomicDecrement(&m_connectionRefCount);
+ break;
+ case RefTypeDisabled:
+ ASSERT(m_disabledRefCount > 0);
+ atomicDecrement(&m_disabledRefCount);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ printf("%p: %d: AudioNode::deref(%d) %d %d %d\n", this, type(), refType, m_normalRefCount, m_connectionRefCount, m_disabledRefCount);
+#endif
+
+ if (!m_connectionRefCount) {
+ if (!m_normalRefCount && !m_disabledRefCount) {
+ if (!m_isMarkedForDeletion) {
+ // All references are gone - we need to go away.
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->disconnectAllInputs(); // this will deref() nodes we're connected to...
+
+ // Mark for deletion at end of each render quantum or when context shuts down.
+ context()->markForDeletion(this);
+ m_isMarkedForDeletion = true;
+ }
+ } else if (refType == RefTypeConnection) {
+ if (!m_isDisabled) {
+ // Still may have JavaScript references, but no more "active" connection references, so put all of our outputs in a "dormant" disabled state.
+ // Garbage collection may take a very long time after this time, so the "dormant" disabled nodes should not bog down the rendering...
+
+ // As far as JavaScript is concerned, our outputs must still appear to be connected.
+ // But internally our outputs should be disabled from the inputs they're connected to.
+ // disable() can recursively deref connections (and call disable()) down a whole chain of connected nodes.
+
+ // FIXME: we special case the convolver and delay since they have a significant tail-time and shouldn't be disconnected simply
+ // because they no longer have any input connections. This needs to be handled more generally where AudioNodes have
+ // a tailTime attribute. Then the AudioNode only needs to remain "active" for tailTime seconds after there are no
+ // longer any active connections.
+ if (type() != NodeTypeConvolver && type() != NodeTypeDelay) {
+ m_isDisabled = true;
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->disable();
+ }
+ }
+ }
+ }
+}
+
+#if DEBUG_AUDIONODE_REFERENCES
+
+bool AudioNode::s_isNodeCountInitialized = false;
+int AudioNode::s_nodeCount[NodeTypeEnd];
+
+void AudioNode::printNodeCounts()
+{
+ printf("\n\n");
+ printf("===========================\n");
+ printf("AudioNode: reference counts\n");
+ printf("===========================\n");
+
+ for (unsigned i = 0; i < NodeTypeEnd; ++i)
+ printf("%d: %d\n", i, s_nodeCount[i]);
+
+ printf("===========================\n\n\n");
+}
+
+#endif // DEBUG_AUDIONODE_REFERENCES
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/webaudio/AudioNode.h b/WebCore/webaudio/AudioNode.h
new file mode 100644
index 0000000..b697457
--- /dev/null
+++ b/WebCore/webaudio/AudioNode.h
@@ -0,0 +1,174 @@
+/*
+ * 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 AudioNode_h
+#define AudioNode_h
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+#define DEBUG_AUDIONODE_REFERENCES 0
+
+namespace WebCore {
+
+class AudioContext;
+class AudioNodeInput;
+class AudioNodeOutput;
+
+// An AudioNode is the basic building block for handling audio within an AudioContext.
+// It may be an audio source, an intermediate processing module, or an audio destination.
+// Each AudioNode can have inputs and/or outputs. An AudioSourceNode has no inputs and a single output.
+// An AudioDestinationNode has one input and no outputs and represents the final destination to the audio hardware.
+// Most processing nodes such as filters will have one input and one output, although multiple inputs and outputs are possible.
+
+class AudioNode {
+public:
+ enum { ProcessingSizeInFrames = 128 };
+
+ AudioNode(AudioContext*, double sampleRate);
+ virtual ~AudioNode();
+
+ AudioContext* context() { return m_context.get(); }
+
+ enum NodeType {
+ NodeTypeUnknown,
+ NodeTypeDestination,
+ NodeTypeAudioBufferSource,
+ NodeTypeJavaScript,
+ NodeTypeLowPass2Filter,
+ NodeTypeHighPass2Filter,
+ NodeTypePanner,
+ NodeTypeConvolver,
+ NodeTypeDelay,
+ NodeTypeGain,
+ NodeTypeChannelSplitter,
+ NodeTypeChannelMerger,
+ NodeTypeAnalyser,
+ NodeTypeEnd
+ };
+
+ NodeType type() const { return m_type; }
+ void setType(NodeType);
+
+ // We handle our own ref-counting because of the threading issues and subtle nature of
+ // how AudioNodes can continue processing (playing one-shot sound) after there are no more
+ // JavaScript references to the object.
+ enum RefType { RefTypeNormal, RefTypeConnection, RefTypeDisabled };
+
+ // Can be called from main thread or context's audio thread.
+ void ref(RefType refType = RefTypeNormal);
+ void deref(RefType refType = RefTypeNormal);
+
+ // Can be called from main thread or context's audio thread. It must be called while the context's graph lock is held.
+ void finishDeref(RefType refType);
+
+ // The AudioNodeInput(s) (if any) will already have their input data available when process() is called.
+ // Subclasses will take this input data and put the results in the AudioBus(s) of its AudioNodeOutput(s) (if any).
+ // Called from context's audio thread.
+ virtual void process(size_t framesToProcess) = 0;
+
+ // Resets DSP processing state (clears delay lines, filter memory, etc.)
+ // Called from context's audio thread.
+ virtual void reset() = 0;
+
+ // No significant resources should be allocated until initialize() is called.
+ // Processing may not occur until a node is initialized.
+ virtual void initialize() = 0;
+ virtual void uninitialize() = 0;
+
+ bool isInitialized() const { return m_isInitialized; }
+ void lazyInitialize();
+
+ unsigned numberOfInputs() const { return m_inputs.size(); }
+ unsigned numberOfOutputs() const { return m_outputs.size(); }
+
+ AudioNodeInput* input(unsigned);
+ AudioNodeOutput* output(unsigned);
+
+ // connect() / disconnect() return true on success.
+ // Called from main thread by corresponding JavaScript methods.
+ bool connect(AudioNode* destination, unsigned outputIndex = 0, unsigned inputIndex = 0);
+ bool disconnect(unsigned outputIndex = 0);
+
+ double sampleRate() const { return m_sampleRate; }
+
+ // processIfNecessary() is called by our output(s) when the rendering graph needs this AudioNode to process.
+ // This method ensures that the AudioNode will only process once per rendering time quantum even if it's called repeatedly.
+ // This handles the case of "fanout" where an output is connected to multiple AudioNode inputs.
+ // Called from context's audio thread.
+ void processIfNecessary(size_t framesToProcess);
+
+ // Called when a new connection has been made to one of our inputs or the connection number of channels has changed.
+ // This potentially gives us enough information to perform a lazy initialization or, if necessary, a re-initialization.
+ // Called from main thread.
+ virtual void checkNumberOfChannelsForInput(AudioNodeInput*) { }
+
+#if DEBUG_AUDIONODE_REFERENCES
+ static void printNodeCounts();
+#endif
+
+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);
+
+ bool m_isInitialized;
+
+private:
+ NodeType m_type;
+ RefPtr<AudioContext> m_context;
+ double m_sampleRate;
+ Vector<OwnPtr<AudioNodeInput> > m_inputs;
+ Vector<OwnPtr<AudioNodeOutput> > m_outputs;
+
+ double m_lastProcessingTime;
+
+ // Ref-counting
+ volatile int m_normalRefCount;
+ volatile int m_connectionRefCount;
+ volatile int m_disabledRefCount;
+
+ bool m_isMarkedForDeletion;
+ bool m_isDisabled;
+
+#if DEBUG_AUDIONODE_REFERENCES
+ static bool s_isNodeCountInitialized;
+ static int s_nodeCount[NodeTypeEnd];
+#endif
+};
+
+} // namespace WebCore
+
+#endif // AudioNode_h
diff --git a/WebCore/webaudio/AudioNode.idl b/WebCore/webaudio/AudioNode.idl
new file mode 100644
index 0000000..5ed47cb
--- /dev/null
+++ b/WebCore/webaudio/AudioNode.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
+ ] AudioNode {
+ readonly attribute AudioContext context;
+ readonly attribute unsigned long numberOfInputs;
+ readonly attribute unsigned long numberOfOutputs;
+
+ [Custom] void connect(in AudioNode destination, in unsigned long output, in unsigned long input);
+ [Custom] void disconnect(in unsigned long output);
+ };
+}
diff --git a/WebCore/webaudio/AudioParam.h b/WebCore/webaudio/AudioParam.h
index 2fbd805..7643cf3 100644
--- a/WebCore/webaudio/AudioParam.h
+++ b/WebCore/webaudio/AudioParam.h
@@ -60,7 +60,14 @@ public:
}
float value() const { return static_cast<float>(m_value); }
- void setValue(float value) { m_value = value; }
+
+ void 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;
+ }
String name() const { return m_name; }