예제 #1
0
void AudioSummingJunction::changedOutputs()
{
    ASSERT(deferredTaskHandler().isGraphOwner());
    if (!m_renderingStateNeedUpdating) {
        deferredTaskHandler().markSummingJunctionDirty(this);
        m_renderingStateNeedUpdating = true;
    }
}
예제 #2
0
void AudioNodeOutput::updateNumberOfChannels() {
  DCHECK(deferredTaskHandler().isAudioThread());
  ASSERT(deferredTaskHandler().isGraphOwner());

  if (m_numberOfChannels != m_desiredNumberOfChannels) {
    m_numberOfChannels = m_desiredNumberOfChannels;
    updateInternalBus();
    propagateChannelCount();
  }
}
예제 #3
0
void AudioNodeOutput::propagateChannelCount() {
  DCHECK(deferredTaskHandler().isAudioThread());
  ASSERT(deferredTaskHandler().isGraphOwner());

  if (isChannelCountKnown()) {
    // Announce to any nodes we're connected to that we changed our channel
    // count for its input.
    for (AudioNodeInput* i : m_inputs)
      i->handler().checkNumberOfChannelsForInput(i);
  }
}
예제 #4
0
void AudioNodeInput::updateInternalBus() {
  DCHECK(deferredTaskHandler().isAudioThread());
  ASSERT(deferredTaskHandler().isGraphOwner());

  unsigned numberOfInputChannels = numberOfChannels();

  if (numberOfInputChannels == m_internalSummingBus->numberOfChannels())
    return;

  m_internalSummingBus = AudioBus::create(numberOfInputChannels,
                                          AudioUtilities::kRenderQuantumFrames);
}
void OfflineAudioContext::handlePostOfflineRenderTasks()
{
    ASSERT(isAudioThread());

    // OfflineGraphAutoLocker here locks the audio graph for the same reason
    // above in |handlePreOfflineRenderTasks|.
    OfflineGraphAutoLocker locker(this);

    deferredTaskHandler().breakConnections();
    releaseFinishedSourceNodes();
    deferredTaskHandler().handleDeferredTasks();
    deferredTaskHandler().requestToDeleteHandlersOnMainThread();
}
예제 #6
0
void AudioNodeOutput::disconnectAllParams() {
  ASSERT(deferredTaskHandler().isGraphOwner());

  // AudioParam::disconnect() changes m_params by calling removeParam().
  while (!m_params.isEmpty())
    (*m_params.begin())->disconnect(*this);
}
예제 #7
0
AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess) {
  DCHECK(deferredTaskHandler().isAudioThread());

  // Handle single connection case.
  if (numberOfRenderingConnections() == 1 &&
      handler().internalChannelCountMode() == AudioHandler::Max) {
    // The output will optimize processing using inPlaceBus if it's able.
    AudioNodeOutput* output = this->renderingOutput(0);
    return output->pull(inPlaceBus, framesToProcess);
  }

  AudioBus* internalSummingBus = this->internalSummingBus();

  if (!numberOfRenderingConnections()) {
    // At least, generate silence if we're not connected to anything.
    // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here
    // to optimize the downstream graph processing.
    internalSummingBus->zero();
    return internalSummingBus;
  }

  // Handle multiple connections case.
  sumAllConnections(internalSummingBus, framesToProcess);

  return internalSummingBus;
}
예제 #8
0
void AudioNodeInput::sumAllConnections(AudioBus* summingBus,
                                       size_t framesToProcess) {
  DCHECK(deferredTaskHandler().isAudioThread());

  // We shouldn't be calling this method if there's only one connection, since
  // it's less efficient.
  //    DCHECK(numberOfRenderingConnections() > 1 ||
  //        handler().internalChannelCountMode() != AudioHandler::Max);

  DCHECK(summingBus);
  if (!summingBus)
    return;

  summingBus->zero();

  AudioBus::ChannelInterpretation interpretation =
      handler().internalChannelInterpretation();

  for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
    AudioNodeOutput* output = renderingOutput(i);
    DCHECK(output);

    // Render audio from this output.
    AudioBus* connectionBus = output->pull(0, framesToProcess);

    // Sum, with unity-gain.
    summingBus->sumFrom(*connectionBus, interpretation);
  }
}
예제 #9
0
AudioContext* AudioParamHandler::context() const
{
    // TODO(tkent): We can remove this dangerous function by removing
    // AudioContext dependency from AudioParamTimeline.
    ASSERT_WITH_SECURITY_IMPLICATION(deferredTaskHandler().isAudioThread());
    return &m_context;
}
void AbstractAudioContext::clear()
{
    m_destinationNode.clear();
    // The audio rendering thread is dead.  Nobody will schedule AudioHandler
    // deletion.  Let's do it ourselves.
    deferredTaskHandler().clearHandlersToBeDeleted();
    m_isCleared = true;
}
예제 #11
0
void AudioNodeOutput::dispose() {
  m_didCallDispose = true;

  deferredTaskHandler().removeMarkedAudioNodeOutput(this);
  disconnectAll();
  DCHECK(m_inputs.isEmpty());
  DCHECK(m_params.isEmpty());
}
예제 #12
0
void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels) {
  DCHECK_LE(numberOfChannels, BaseAudioContext::maxNumberOfChannels());
  ASSERT(deferredTaskHandler().isGraphOwner());

  m_desiredNumberOfChannels = numberOfChannels;

  if (deferredTaskHandler().isAudioThread()) {
    // If we're in the audio thread then we can take care of it right away (we
    // should be at the very start or end of a rendering quantum).
    updateNumberOfChannels();
  } else {
    DCHECK(!m_didCallDispose);
    // Let the context take care of it in the audio thread in the pre and post
    // render tasks.
    deferredTaskHandler().markAudioNodeOutputDirty(this);
  }
}
예제 #13
0
void AudioParamHandler::calculateSampleAccurateValues(float* values, unsigned numberOfValues)
{
    bool isSafe = deferredTaskHandler().isAudioThread() && values && numberOfValues;
    ASSERT(isSafe);
    if (!isSafe)
        return;

    calculateFinalValues(values, numberOfValues, true);
}
예제 #14
0
void AudioNodeOutput::enable() {
  ASSERT(deferredTaskHandler().isGraphOwner());

  if (!m_isEnabled) {
    m_isEnabled = true;
    for (AudioNodeInput* i : m_inputs)
      i->enable(*this);
  }
}
예제 #15
0
void AudioSummingJunction::updateRenderingState()
{
    ASSERT(deferredTaskHandler().isAudioThread());
    ASSERT(deferredTaskHandler().isGraphOwner());
    if (m_renderingStateNeedUpdating) {
        // Copy from m_outputs to m_renderingOutputs.
        m_renderingOutputs.resize(m_outputs.size());
        unsigned j = 0;
        for (AudioNodeOutput* output : m_outputs) {
            m_renderingOutputs[j++] = output;
            output->updateRenderingState();
        }

        didUpdate();

        m_renderingStateNeedUpdating = false;
    }
}
AbstractAudioContext::~AbstractAudioContext()
{
    deferredTaskHandler().contextWillBeDestroyed();
    // 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);
    ASSERT(!m_activeSourceNodes.size());
    ASSERT(!m_finishedSourceHandlers.size());
    ASSERT(!m_isResolvingResumePromises);
    ASSERT(!m_resumeResolvers.size());
}
예제 #17
0
void AudioParamHandler::disconnect(AudioNodeOutput& output)
{
    ASSERT(deferredTaskHandler().isGraphOwner());

    if (m_outputs.contains(&output)) {
        m_outputs.remove(&output);
        changedOutputs();
        output.removeParam(*this);
    }
}
void AbstractAudioContext::handlePostRenderTasks()
{
    ASSERT(isAudioThread());

    // Must use a tryLock() here too.  Don't worry, the lock will very rarely be contended and this method is called frequently.
    // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed
    // from the render graph (in which case they'll render silence).
    if (tryLock()) {
        // Take care of AudioNode tasks where the tryLock() failed previously.
        deferredTaskHandler().breakConnections();

        // Dynamically clean up nodes which are no longer needed.
        releaseFinishedSourceNodes();

        deferredTaskHandler().handleDeferredTasks();
        deferredTaskHandler().requestToDeleteHandlersOnMainThread();

        unlock();
    }
}
예제 #19
0
AudioBus* AudioNodeInput::bus() {
  DCHECK(deferredTaskHandler().isAudioThread());

  // Handle single connection specially to allow for in-place processing.
  if (numberOfRenderingConnections() == 1 &&
      handler().internalChannelCountMode() == AudioHandler::Max)
    return renderingOutput(0)->bus();

  // Multiple connections case or complex ChannelCountMode (or no connections).
  return internalSummingBus();
}
예제 #20
0
void AudioParamHandler::connect(AudioNodeOutput& output)
{
    ASSERT(deferredTaskHandler().isGraphOwner());

    if (m_outputs.contains(&output))
        return;

    output.addParam(*this);
    m_outputs.add(&output);
    changedOutputs();
}
예제 #21
0
void AudioContext::stopRendering()
{
    ASSERT(isMainThread());
    ASSERT(destination());

    if (contextState() == Running) {
        destination()->audioDestinationHandler().stopRendering();
        setContextState(Suspended);
        deferredTaskHandler().clearHandlersToBeDeleted();
    }
}
예제 #22
0
void AudioNodeInput::connect(AudioNodeOutput& output) {
  ASSERT(deferredTaskHandler().isGraphOwner());

  // Check if we're already connected to this output.
  if (m_outputs.contains(&output))
    return;

  output.addInput(*this);
  m_outputs.add(&output);
  changedOutputs();
}
예제 #23
0
void AudioNodeInput::disable(AudioNodeOutput& output) {
  ASSERT(deferredTaskHandler().isGraphOwner());
  DCHECK(m_outputs.contains(&output));

  m_disabledOutputs.add(&output);
  m_outputs.remove(&output);
  changedOutputs();

  // Propagate disabled state to outputs.
  handler().disableOutputsIfNecessary();
}
예제 #24
0
float AudioParamHandler::value()
{
    // Update value for timeline.
    if (deferredTaskHandler().isAudioThread()) {
        bool hasValue;
        float timelineValue = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), hasValue);

        if (hasValue)
            m_value = timelineValue;
    }

    return narrowPrecisionToFloat(m_value);
}
예제 #25
0
void AudioNodeInput::enable(AudioNodeOutput& output) {
  ASSERT(deferredTaskHandler().isGraphOwner());

  // Move output from disabled list to active list.
  m_outputs.add(&output);
  if (m_disabledOutputs.size() > 0) {
    DCHECK(m_disabledOutputs.contains(&output));
    m_disabledOutputs.remove(&output);
  }
  changedOutputs();

  // Propagate enabled state to outputs.
  handler().enableOutputsIfNecessary();
}
bool OfflineAudioContext::handlePreOfflineRenderTasks()
{
    ASSERT(isAudioThread());

    // OfflineGraphAutoLocker here locks the audio graph for this scope. Note
    // that this locker does not use tryLock() inside because the timing of
    // suspension MUST NOT be delayed.
    OfflineGraphAutoLocker locker(this);

    deferredTaskHandler().handleDeferredTasks();
    handleStoppableSourceNodes();

    return shouldSuspend();
}
void AbstractAudioContext::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()) {
        deferredTaskHandler().handleDeferredTasks();

        resolvePromisesForResume();

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

        unlock();
    }
}
예제 #28
0
AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess) {
  DCHECK(deferredTaskHandler().isAudioThread());
  DCHECK(m_renderingFanOutCount > 0 || m_renderingParamFanOutCount > 0);

  // Causes our AudioNode to process if it hasn't already for this render
  // quantum.  We try to do in-place processing (using inPlaceBus) if at all
  // possible, but we can't process in-place if we're connected to more than one
  // input (fan-out > 1).  In this case pull() is called multiple times per
  // rendering quantum, and the processIfNecessary() call below will cause our
  // node to process() only the first time, caching the output in
  // m_internalOutputBus for subsequent calls.

  m_isInPlace = inPlaceBus &&
                inPlaceBus->numberOfChannels() == numberOfChannels() &&
                (m_renderingFanOutCount + m_renderingParamFanOutCount) == 1;

  m_inPlaceBus = m_isInPlace ? inPlaceBus : 0;

  handler().processIfNecessary(framesToProcess);
  return bus();
}
예제 #29
0
void AudioParamHandler::calculateFinalValues(float* values, unsigned numberOfValues, bool sampleAccurate)
{
    bool isGood = deferredTaskHandler().isAudioThread() && values && numberOfValues;
    ASSERT(isGood);
    if (!isGood)
        return;

    // The calculated result will be the "intrinsic" value summed with all audio-rate connections.

    if (sampleAccurate) {
        // Calculate sample-accurate (a-rate) intrinsic values.
        calculateTimelineValues(values, numberOfValues);
    } else {
        // Calculate control-rate (k-rate) intrinsic value.
        bool hasValue;
        float timelineValue = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), hasValue);

        if (hasValue)
            m_value = timelineValue;

        values[0] = narrowPrecisionToFloat(m_value);
    }

    // Now sum all of the audio-rate connections together (unity-gain summing junction).
    // Note that connections would normally be mono, but we mix down to mono if necessary.
    RefPtr<AudioBus> summingBus = AudioBus::create(1, numberOfValues, false);
    summingBus->setChannelMemory(0, values, numberOfValues);

    for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
        AudioNodeOutput* output = renderingOutput(i);
        ASSERT(output);

        // Render audio from this output.
        AudioBus* connectionBus = output->pull(0, AudioHandler::ProcessingSizeInFrames);

        // Sum, with unity-gain.
        summingBus->sumFrom(*connectionBus);
    }
}
예제 #30
0
void AudioNodeInput::disconnect(AudioNodeOutput& output) {
  ASSERT(deferredTaskHandler().isGraphOwner());

  // First try to disconnect from "active" connections.
  if (m_outputs.contains(&output)) {
    m_outputs.remove(&output);
    changedOutputs();
    output.removeInput(*this);
    // Note: it's important to return immediately after removeInput() calls
    // since the node may be deleted.
    return;
  }

  // Otherwise, try to disconnect from disabled connections.
  if (m_disabledOutputs.contains(&output)) {
    m_disabledOutputs.remove(&output);
    output.removeInput(*this);
    // Note: it's important to return immediately after all removeInput() calls
    // since the node may be deleted.
    return;
  }

  ASSERT_NOT_REACHED();
}