Ejemplo n.º 1
0
void AudioNode::processIfNecessary(ContextRenderLock& r, size_t framesToProcess)
{
    if (!isInitialized())
        return;
    
    auto ac = r.context();
    if (!ac)
        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 = ac->currentTime();
    if (m_lastProcessingTime != currentTime) {
        m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph

        pullInputs(r, framesToProcess);

        bool silentInputs = inputsAreSilent(r);
        if (!silentInputs)
            m_lastNonSilentTime = (ac->currentSampleFrame() + framesToProcess) / static_cast<double>(m_sampleRate);

        bool ps = propagatesSilence(r.context()->currentTime());
        if (silentInputs && ps)
            silenceOutputs(r);
        else {
            process(r, framesToProcess);
            unsilenceOutputs(r);
        }
    }
}
bool OfflineAudioContext::shouldSuspend()
{
    ASSERT(isAudioThread());

    // Note that the GraphLock is required before this check. Since this needs
    // to run on the audio thread, OfflineGraphAutoLocker must be used.
    if (m_scheduledSuspends.contains(currentSampleFrame()))
        return true;

    return false;
}
Ejemplo n.º 3
0
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.
    if (tryLock()) {
        handler().handleDeferredTasks();

        resolvePromisesForResume();

        // Check to see if source nodes can be stopped because the end time has passed.
        handleStoppableSourceNodes();

        // Update the cached sample frame value.
        m_cachedSampleFrame = currentSampleFrame();

        unlock();
    }
}
ScriptPromise OfflineAudioContext::suspendContext(ScriptState* scriptState, double when)
{
    ASSERT(isMainThread());

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

    // The render thread does not exist; reject the promise.
    if (!destinationHandler().offlineRenderThread()) {
        resolver->reject(DOMException::create(InvalidStateError,
            "the rendering is already finished"));
        return promise;
    }

    // The specified suspend time is negative; reject the promise.
    if (when < 0) {
        resolver->reject(DOMException::create(InvalidStateError,
            "negative suspend time (" + String::number(when) + ") is not allowed"));
        return promise;
    }

    // Quantize (to the lower boundary) the suspend time by the render quantum.
    size_t frame = when * sampleRate();
    frame -= frame % destinationHandler().renderQuantumFrames();

    // The suspend time should be earlier than the total render frame. If the
    // requested suspension time is equal to the total render frame, the promise
    // will be rejected.
    if (m_totalRenderFrames <= frame) {
        resolver->reject(DOMException::create(InvalidStateError,
            "cannot schedule a suspend at frame " + String::number(frame) +
            " (" + String::number(when) + " seconds) " +
            "because it is greater than or equal to the total render duration of " +
            String::number(m_totalRenderFrames) + " frames"));
        return promise;
    }

    // The specified suspend time is in the past; reject the promise.
    if (frame < currentSampleFrame()) {
        resolver->reject(DOMException::create(InvalidStateError,
            "cannot schedule a suspend at frame " +
            String::number(frame) + " (" + String::number(when) +
            " seconds) because it is earlier than the current frame of " +
            String::number(currentSampleFrame()) + " (" +
            String::number(currentTime()) + " seconds)"));
        return promise;
    }

    // Wait until the suspend map is available for the insertion. Here we should
    // use AutoLocker because it locks the graph from the main thread.
    AutoLocker locker(this);

    // If there is a duplicate suspension at the same quantized frame,
    // reject the promise.
    if (m_scheduledSuspends.contains(frame)) {
        resolver->reject(DOMException::create(InvalidStateError,
            "cannot schedule more than one suspend at frame " +
            String::number(frame) + " (" +
            String::number(when) + " seconds)"));
        return promise;
    }

    m_scheduledSuspends.add(frame, resolver);

    return promise;
}