void OfflineAudioContext::fireCompletionEvent()
{
    ASSERT(isMainThread());

    // We set the state to closed here so that the oncomplete event handler sees
    // that the context has been closed.
    setContextState(Closed);

    AudioBuffer* renderedBuffer = renderTarget();

    ASSERT(renderedBuffer);
    if (!renderedBuffer)
        return;

    // Avoid firing the event if the document has already gone away.
    if (executionContext()) {
        // Call the offline rendering completion event listener and resolve the
        // promise too.
        dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
        m_completeResolver->resolve(renderedBuffer);
    } else {
        // The resolver should be rejected when the execution context is gone.
        m_completeResolver->reject(DOMException::create(InvalidStateError,
            "the execution context does not exist"));
    }
}
void AudioContext::stopRendering()
{
    ASSERT(isMainThread());
    ASSERT(m_destinationNode);
    ASSERT(!isOfflineContext());

    if (m_contextState == Running) {
        destination()->audioDestinationHandler().stopRendering();
        setContextState(Suspended);
    }
}
void AudioContext::startRendering()
{
    // This is called for both online and offline contexts.
    ASSERT(isMainThread());
    ASSERT(m_destinationNode);

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

    if (contextState() == Running) {
        destination()->audioDestinationHandler().stopRendering();
        setContextState(Suspended);
        deferredTaskHandler().clearHandlersToBeDeleted();
    }
}
Beispiel #5
0
void AudioContext::didClose()
{
    // This is specific to AudioContexts. OfflineAudioContexts
    // are closed in their completion event.
    setContextState(Closed);

    ASSERT(s_hardwareContextCount);
    --s_hardwareContextCount;

    if (m_closeResolver)
        m_closeResolver->resolve();
}
Beispiel #6
0
void OfflineAudioContext::resolveSuspendOnMainThread(size_t frame)
{
    ASSERT(isMainThread());

    // Suspend the context first. This will fire onstatechange event.
    setContextState(Suspended);

    // Wait until the suspend map is available for the removal.
    AutoLocker locker(this);

    ASSERT(m_scheduledSuspends.contains(frame));

    SuspendMap::iterator it = m_scheduledSuspends.find(frame);
    it->value->resolve();

    m_scheduledSuspends.remove(it);
}
ScriptPromise OfflineAudioContext::startOfflineRendering(ScriptState* scriptState)
{
    ASSERT(isMainThread());

    // Calling close() on an OfflineAudioContext is not supported/allowed,
    // but it might well have been stopped by its execution context.
    //
    // See: crbug.com/435867
    if (isContextClosed()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot call startRendering on an OfflineAudioContext in a stopped state."));
    }

    // If the context is not in the suspended state (i.e. running), reject the promise.
    if (contextState() != AudioContextState::Suspended) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot startRendering when an OfflineAudioContext is " + state()));
    }

    // Can't call startRendering more than once.  Return a rejected promise now.
    if (m_isRenderingStarted) {
        return ScriptPromise::rejectWithDOMException(
            scriptState,
            DOMException::create(
                InvalidStateError,
                "cannot call startRendering more than once"));
    }

    ASSERT(!m_isRenderingStarted);

    m_completeResolver = ScriptPromiseResolver::create(scriptState);

    // Start rendering and return the promise.
    m_isRenderingStarted = true;
    setContextState(Running);
    destinationHandler().startRendering();

    return m_completeResolver->promise();
}
void OfflineAudioContext::resolveSuspendOnMainThread(size_t frame)
{
    ASSERT(isMainThread());

    // Suspend the context first. This will fire onstatechange event.
    setContextState(Suspended);

    // Wait until the suspend map is available for the removal.
    AutoLocker locker(this);

    // |frame| must exist in the map. However, it can be removed already in a
    // very rare case. See: crbug.com/568796
    RELEASE_ASSERT(m_scheduledSuspends.contains(frame));

    SuspendMap::iterator it = m_scheduledSuspends.find(frame);
    it->value->resolve();

    m_scheduledSuspends.remove(it);
}
ScriptPromise OfflineAudioContext::resumeContext(ScriptState* scriptState)
{
    ASSERT(isMainThread());

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

    // If the rendering has not started, reject the promise.
    if (!m_isRenderingStarted) {
        resolver->reject(DOMException::create(InvalidStateError,
            "cannot resume an offline context that has not started"));
        return promise;
    }

    // If the context is in a closed state, reject the promise.
    if (contextState() == AudioContextState::Closed) {
        resolver->reject(DOMException::create(InvalidStateError,
            "cannot resume a closed offline context"));
        return promise;
    }

    // If the context is already running, resolve the promise without altering
    // the current state or starting the rendering loop.
    if (contextState() == AudioContextState::Running) {
        resolver->resolve();
        return promise;
    }

    ASSERT(contextState() == AudioContextState::Suspended);

    // If the context is suspended, resume rendering by setting the state to
    // "Running". and calling startRendering(). Note that resuming is possible
    // only after the rendering started.
    setContextState(Running);
    destinationHandler().startRendering();

    // Resolve the promise immediately.
    resolver->resolve();

    return promise;
}
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();
}
void AudioContext::fireCompletionEvent()
{
    ASSERT(isMainThread());
    if (!isMainThread())
        return;

    AudioBuffer* renderedBuffer = m_renderTarget.get();

    // For an offline context, we set the state to closed here so that the oncomplete handler sees
    // that the context has been closed.
    setContextState(Closed);

    ASSERT(renderedBuffer);
    if (!renderedBuffer)
        return;

    // Avoid firing the event if the document has already gone away.
    if (executionContext()) {
        // Call the offline rendering completion event listener and resolve the promise too.
        dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
        m_offlineResolver->resolve(renderedBuffer);
    }
}