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