Example #1
0
void AudioContext::initialize()
{
    if (isInitialized())
        return;

    FFTFrame::initialize();
    m_listener = AudioListener::create();

    if (m_destinationNode.get()) {
        m_destinationNode->handler().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.
            startRendering();
            ++s_hardwareContextCount;
        }

        m_contextId = s_contextId++;
        m_isInitialized = true;
#if DEBUG_AUDIONODE_REFERENCES
        fprintf(stderr, "%p: AudioContext::AudioContext(): %u #%u\n",
            this, m_contextId, AudioContext::s_hardwareContextCount);
#endif
    }
}
Example #2
0
ScriptPromise AudioContext::resumeContext(ScriptState* scriptState)
{
    ASSERT(isMainThread());
    AutoLocker locker(this);

    if (isOfflineContext()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot resume an OfflineAudioContext"));
    }

    if (isContextClosed()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot resume a closed AudioContext"));
    }

    RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
    ScriptPromise promise = resolver->promise();

    // Restart the destination node to pull on the audio graph.
    if (m_destinationNode)
        startRendering();

    // Save the resolver which will get resolved when the destination node starts pulling on the
    // graph again.
    m_resumeResolvers.append(resolver);

    return promise;
}
Example #3
0
void AudioContext::lazyInitialize()
{
    if (m_isInitialized)
        return;

    // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
    ASSERT(!m_isAudioThreadFinished);
    if (m_isAudioThreadFinished)
        return;

    if (m_destinationNode.get()) {
        m_destinationNode->initialize();

        if (!isOfflineContext()) {
            document()->addAudioProducer(this);

            // 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.
            startRendering();
            ++s_hardwareContextCount;
        }
    }
    m_isInitialized = true;
}
Example #4
0
void AudioContext::uninitialize()
{
    ASSERT(isMainThread());

    if (!m_isInitialized)
        return;

    // 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;

    if (!isOfflineContext()) {
        document()->removeAudioProducer(this);

        ASSERT(s_hardwareContextCount);
        --s_hardwareContextCount;

        // Offline contexts move to 'Closed' state when dispatching the completion event.
        setState(State::Closed);
    }

    // Get rid of the sources which may still be playing.
    derefUnfinishedSourceNodes();

    m_isInitialized = false;
}
Example #5
0
void AudioContext::resume(Promise&& promise)
{
    if (isOfflineContext()) {
        promise.reject(INVALID_STATE_ERR);
        return;
    }

    if (m_state == State::Running) {
        promise.resolve(nullptr);
        return;
    }

    if (m_state == State::Closed || !m_destinationNode) {
        promise.reject(0);
        return;
    }

    addReaction(State::Running, WTFMove(promise));

    if (!willBeginPlayback())
        return;

    lazyInitialize();

    RefPtr<AudioContext> strongThis(this);
    m_destinationNode->resume([strongThis] {
        strongThis->setState(State::Running);
    });
}
Example #6
0
void AudioContext::suspend(Promise&& promise)
{
    if (isOfflineContext()) {
        promise.reject(INVALID_STATE_ERR);
        return;
    }

    if (m_state == State::Suspended) {
        promise.resolve(nullptr);
        return;
    }

    if (m_state == State::Closed || m_state == State::Interrupted || !m_destinationNode) {
        promise.reject(0);
        return;
    }

    addReaction(State::Suspended, WTFMove(promise));

    if (!willPausePlayback())
        return;

    lazyInitialize();

    RefPtr<AudioContext> strongThis(this);
    m_destinationNode->suspend([strongThis] {
        strongThis->setState(State::Suspended);
    });
}
Example #7
0
void AudioContext::uninitialize()
{
    ASSERT(isMainThread());

    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();

        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;
    }
}
Example #8
0
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();

        m_isInitialized = false;
    }
}
Example #9
0
void AudioContext::stopRendering()
{
    ASSERT(isMainThread());
    ASSERT(m_destinationNode);
    ASSERT(!isOfflineContext());

    if (m_contextState == Running) {
        destination()->audioDestinationHandler().stopRendering();
        setContextState(Suspended);
    }
}
Example #10
0
void AudioContext::uninitialize()
{
    ASSERT(isMainThread());

    if (!isInitialized())
        return;

    m_isInitialized = false;

    // This stops the audio thread and all audio rendering.
    if (m_destinationNode)
        m_destinationNode->handler().uninitialize();

    if (!isOfflineContext()) {
        ASSERT(s_hardwareContextCount);
        --s_hardwareContextCount;
    }

    // Get rid of the sources which may still be playing.
    derefUnfinishedSourceNodes();

    // Reject any pending resolvers before we go away.
    rejectPendingResolvers();

    // For an offline audio context, the completion event will set the state to closed.  For an
    // online context, we need to do it here.  We only want to set the closed state once.
    if (!isOfflineContext())
        setContextState(Closed);

    // Resolve the promise now, if any
    if (m_closeResolver)
        m_closeResolver->resolve();

    ASSERT(m_listener);
    m_listener->waitForHRTFDatabaseLoaderThreadCompletion();

    clear();
}
Example #11
0
ScriptPromise AudioContext::suspendContext(ScriptState* scriptState)
{
    ASSERT(isMainThread());
    AutoLocker locker(this);

    if (isOfflineContext()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot suspend an OfflineAudioContext"));
    }

    RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
    ScriptPromise promise = resolver->promise();

    // Save the resolver which will get resolved at the end of the rendering quantum.
    m_suspendResolvers.append(resolver);

    return promise;
}
Example #12
0
void AudioContext::close(Promise&& promise)
{
    if (isOfflineContext()) {
        promise.reject(INVALID_STATE_ERR);
        return;
    }

    if (m_state == State::Closed || !m_destinationNode) {
        promise.resolve(nullptr);
        return;
    }

    addReaction(State::Closed, WTFMove(promise));

    lazyInitialize();

    RefPtr<AudioContext> strongThis(this);
    m_destinationNode->close([strongThis] {
        strongThis->setState(State::Closed);
        strongThis->uninitialize();
    });
}
Example #13
0
ScriptPromise AudioContext::closeContext(ScriptState* scriptState)
{
    if (isOfflineContext()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(InvalidStateError, "Cannot call close() on an OfflineAudioContext."));
    }

    if (isContextClosed()) {
        // We've already closed the context previously, but it hasn't yet been resolved, so just
        // create a new promise and reject it.
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(InvalidStateError,
                "Cannot close a context that is being closed or has already been closed."));
    }

    m_closeResolver = ScriptPromiseResolver::create(scriptState);
    ScriptPromise promise = m_closeResolver->promise();

    // Before closing the context go and disconnect all nodes, allowing them to be collected. This
    // will also break any connections to the destination node. Any unfinished sourced nodes will
    // get stopped when the context is unitialized.
    for (auto& node : m_liveNodes) {
        if (node) {
            for (unsigned k = 0; k < node->numberOfOutputs(); ++k)
                node->handler().disconnectWithoutException(k);
        }
    }

    // Stop the audio context. This will stop the destination node from pulling audio anymore. And
    // since we have disconnected the destination from the audio graph, and thus has no references,
    // the destination node can GCed if JS has no references. stop() will also resolve the Promise
    // created here.
    stop();

    return promise;
}
Example #14
0
void AudioContext::uninitialize()
{
    ASSERT(isMainThread());

    if (!m_isInitialized)
        return;

    // 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;

    if (!isOfflineContext()) {
        ASSERT(s_hardwareContextCount);
        --s_hardwareContextCount;
    }

    // Get rid of the sources which may still be playing.
    derefUnfinishedSourceNodes();

    m_isInitialized = false;
}