#include "modules/webaudio/AudioContext.h"
-#include "bindings/v8/ExceptionMessages.h"
-#include "bindings/v8/ExceptionState.h"
+#include "bindings/core/v8/ExceptionMessages.h"
+#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/HTMLMediaElement.h"
#include "wtf/text/WTFString.h"
// FIXME: check the proper way to reference an undefined thread ID
-const int UndefinedThreadIdentifier = 0xffffffff;
+const WTF::ThreadIdentifier UndefinedThreadIdentifier = 0xffffffff;
-namespace WebCore {
+namespace blink {
bool AudioContext::isSampleRateRangeGood(float sampleRate)
{
, m_isCleared(false)
, m_isInitialized(false)
, m_destinationNode(nullptr)
+#if !ENABLE(OILPAN)
, m_isDeletionScheduled(false)
+#endif
, m_automaticPullNodesNeedUpdating(false)
, m_connectionCount(0)
, m_audioThread(0)
m_destinationNode = DefaultAudioDestinationNode::create(this);
initialize();
+#if DEBUG_AUDIONODE_REFERENCES
+ fprintf(stderr, "%p: AudioContext::AudioContext() #%u\n", this, AudioContext::s_hardwareContextCount);
+#endif
}
// Constructor for offline (non-realtime) rendering.
, m_isCleared(false)
, m_isInitialized(false)
, m_destinationNode(nullptr)
+#if !ENABLE(OILPAN)
, m_isDeletionScheduled(false)
+#endif
, m_automaticPullNodesNeedUpdating(false)
, m_connectionCount(0)
, m_audioThread(0)
#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_isInitialized);
+#if !ENABLE(OILPAN)
ASSERT(!m_nodesToDelete.size());
ASSERT(!m_referencedNodes.size());
+#endif
ASSERT(!m_finishedNodes.size());
ASSERT(!m_automaticPullNodes.size());
if (m_automaticPullNodesNeedUpdating)
void AudioContext::clear()
{
+#if ENABLE(OILPAN)
+ // We need to run disposers before destructing m_contextGraphMutex.
+ m_liveAudioSummingJunctions.clear();
+ m_liveNodes.clear();
+#else
+
// 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.
if (m_destinationNode)
m_destinationNode.clear();
m_nodesMarkedForDeletion.clear();
deleteMarkedNodes();
} while (m_nodesToDelete.size());
+#endif
m_isCleared = true;
}
derefUnfinishedSourceNodes();
m_isInitialized = false;
-}
-
-void AudioContext::stopDispatch(void* userData)
-{
- AudioContext* context = reinterpret_cast<AudioContext*>(userData);
- ASSERT(context);
- if (!context)
- return;
-
- context->uninitialize();
- context->clear();
+ clear();
}
void AudioContext::stop()
// 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(stopDispatch, this);
+ callOnMainThread(bind(&AudioContext::uninitialize, PassRefPtrWillBeRawPtr<AudioContext>(this)));
}
bool AudioContext::hasPendingActivity() const
}
// Use the first audio track in the media stream.
- RefPtrWillBeRawPtr<MediaStreamTrack> audioTrack = audioTracks[0];
+ MediaStreamTrack* audioTrack = audioTracks[0];
OwnPtr<AudioSourceProvider> provider = audioTrack->createWebAudioSource();
- RefPtrWillBeRawPtr<MediaStreamAudioSourceNode> node = MediaStreamAudioSourceNode::create(this, mediaStream, audioTrack.get(), provider.release());
+ RefPtrWillBeRawPtr<MediaStreamAudioSourceNode> node = MediaStreamAudioSourceNode::create(this, mediaStream, audioTrack, provider.release());
// FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams.
node->setFormat(2, sampleRate());
ASSERT(isMainThread());
AutoLocker locker(this);
- node->ref(AudioNode::RefTypeConnection);
m_referencedNodes.append(node);
+ node->makeConnection();
}
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]) {
+ if (node == m_referencedNodes[i].get()) {
+ node->breakConnection();
m_referencedNodes.remove(i);
break;
}
{
ASSERT(isMainThread());
for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
- m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
+ m_referencedNodes[i]->breakConnection();
m_referencedNodes.clear();
}
return currentThread() == m_graphOwnerThread;
}
+void AudioContext::addDeferredBreakConnection(AudioNode& node)
+{
+ ASSERT(isAudioThread());
+ m_deferredBreakConnectionList.append(&node);
+}
+
+#if !ENABLE(OILPAN)
void AudioContext::addDeferredFinishDeref(AudioNode* node)
{
ASSERT(isAudioThread());
m_deferredFinishDerefList.append(node);
}
+#endif
void AudioContext::handlePreRenderTasks()
{
// 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();
+ // Take care of AudioNode tasks where the tryLock() failed previously.
+ handleDeferredAudioNodeTasks();
// Dynamically clean up nodes which are no longer needed.
derefFinishedSourceNodes();
+#if !ENABLE(OILPAN)
// 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();
+#endif
// Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs.
handleDirtyAudioSummingJunctions();
}
}
-void AudioContext::handleDeferredFinishDerefs()
+void AudioContext::handleDeferredAudioNodeTasks()
{
ASSERT(isAudioThread() && isGraphOwner());
- for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
- AudioNode* node = m_deferredFinishDerefList[i];
- node->finishDeref(AudioNode::RefTypeConnection);
- }
+ for (unsigned i = 0; i < m_deferredBreakConnectionList.size(); ++i)
+ m_deferredBreakConnectionList[i]->breakConnectionWithLock();
+ m_deferredBreakConnectionList.clear();
+
+#if !ENABLE(OILPAN)
+ for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i)
+ m_deferredFinishDerefList[i]->finishDeref();
m_deferredFinishDerefList.clear();
+#endif
}
+#if ENABLE(OILPAN)
+void AudioContext::registerLiveNode(AudioNode& node)
+{
+ ASSERT(isMainThread());
+ m_liveNodes.add(&node, adoptPtr(new AudioNodeDisposer(node)));
+}
+
+AudioContext::AudioNodeDisposer::~AudioNodeDisposer()
+{
+ ASSERT(isMainThread());
+ AudioContext::AutoLocker locker(m_node.context());
+ m_node.dispose();
+}
+
+void AudioContext::registerLiveAudioSummingJunction(AudioSummingJunction& junction)
+{
+ ASSERT(isMainThread());
+ m_liveAudioSummingJunctions.add(&junction, adoptPtr(new AudioSummingJunctionDisposer(junction)));
+}
+
+AudioContext::AudioSummingJunctionDisposer::~AudioSummingJunctionDisposer()
+{
+ ASSERT(isMainThread());
+ m_junction.context()->removeMarkedSummingJunction(&m_junction);
+}
+#else
+
void AudioContext::markForDeletion(AudioNode* node)
{
ASSERT(isGraphOwner());
m_nodesToDelete.append(node);
else
m_nodesMarkedForDeletion.append(node);
-
- // This is probably the best time for us to remove the node from automatic pull list,
- // since all connections are gone and we hold the graph lock. Then when handlePostRenderTasks()
- // gets a chance to schedule the deletion work, updateAutomaticPullNodes() also gets a chance to
- // modify m_renderingAutomaticPullNodes.
- removeAutomaticPullNode(node);
}
void AudioContext::scheduleNodeDeletion()
AudioNode* node = m_nodesToDelete[n - 1];
m_nodesToDelete.removeLast();
- // Before deleting the node, clear out any AudioNodeInputs from m_dirtySummingJunctions.
- unsigned numberOfInputs = node->numberOfInputs();
- for (unsigned i = 0; i < numberOfInputs; ++i)
- m_dirtySummingJunctions.remove(node->input(i));
+ node->dispose();
- // 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));
-#if ENABLE(OILPAN)
- // Finally, clear the keep alive handle that keeps this
- // object from being collected.
- node->clearKeepAlive();
-#else
// Finally, delete it.
delete node;
-#endif
}
m_isDeletionScheduled = false;
}
}
+#endif
+
+void AudioContext::unmarkDirtyNode(AudioNode& node)
+{
+ ASSERT(isGraphOwner());
+#if !ENABLE(OILPAN)
+ // Before deleting the node, clear out any AudioNodeInputs from
+ // m_dirtySummingJunctions.
+ unsigned numberOfInputs = node.numberOfInputs();
+ for (unsigned i = 0; i < numberOfInputs; ++i)
+ m_dirtySummingJunctions.remove(node.input(i));
+#endif
+
+ // 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));
+}
void AudioContext::markSummingJunctionDirty(AudioSummingJunction* summingJunction)
{
void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
{
ASSERT(isGraphOwner());
+ ASSERT(isMainThread());
m_dirtyAudioNodeOutputs.add(output);
}
visitor->trace(m_renderTarget);
visitor->trace(m_destinationNode);
visitor->trace(m_listener);
- visitor->trace(m_dirtySummingJunctions);
+#if ENABLE(OILPAN)
+ visitor->trace(m_referencedNodes);
+ visitor->trace(m_liveNodes);
+ visitor->trace(m_liveAudioSummingJunctions);
+#endif
EventTargetWithInlineData::trace(visitor);
}
-} // namespace WebCore
+} // namespace blink
#endif // ENABLE(WEB_AUDIO)