Ejemplo n.º 1
0
bool
AudioNode::DisconnectFromOutputIfConnected<AudioNode>(uint32_t aOutputNodeIndex,
                                                      uint32_t aInputIndex)
{
  WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
                    NodeType(), Id());

  AudioNode* destination = mOutputNodes[aOutputNodeIndex];

  MOZ_ASSERT(aOutputNodeIndex < mOutputNodes.Length());
  MOZ_ASSERT(aInputIndex < destination->InputNodes().Length());

  // An upstream node may be starting to play on the graph thread, and the
  // engine for a downstream node may be sending a PlayingRefChangeHandler
  // ADDREF message to this (main) thread.  Wait for a round trip before
  // releasing nodes, to give engines receiving sound now time to keep their
  // nodes alive.
  class RunnableRelease final : public Runnable
  {
  public:
    explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
      : mozilla::Runnable("RunnableRelease")
      , mNode(aNode)
    {
    }

    NS_IMETHOD Run() override
    {
      mNode = nullptr;
      return NS_OK;
    }
  private:
    RefPtr<AudioNode> mNode;
  };

  InputNode& input = destination->mInputNodes[aInputIndex];
  if (input.mInputNode != this) {
    return false;
  }

  // Remove one instance of 'dest' from mOutputNodes. There could be
  // others, and it's not correct to remove them all since some of them
  // could be for different output ports.
  RefPtr<AudioNode> output = mOutputNodes[aOutputNodeIndex].forget();
  mOutputNodes.RemoveElementAt(aOutputNodeIndex);
  // Destroying the InputNode here sends a message to the graph thread
  // to disconnect the streams, which should be sent before the
  // RunAfterPendingUpdates() call below.
  destination->mInputNodes.RemoveElementAt(aInputIndex);
  output->NotifyInputsChanged();
  if (mStream) {
    nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
    mStream->RunAfterPendingUpdates(runnable.forget());
  }
  return true;
}
Ejemplo n.º 2
0
AudioNode*
AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
                   uint32_t aInput, ErrorResult& aRv)
{
  if (aOutput >= NumberOfOutputs() ||
      aInput >= aDestination.NumberOfInputs()) {
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    return nullptr;
  }

  if (Context() != aDestination.Context()) {
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    return nullptr;
  }

  if (FindIndexOfNodeWithPorts(aDestination.mInputNodes,
                               this, aInput, aOutput) !=
      nsTArray<AudioNode::InputNode>::NoIndex) {
    // connection already exists.
    return &aDestination;
  }

  WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u",
                    Context()->CurrentTime(), NodeType(), Id(),
                    aDestination.NodeType(), aDestination.Id());

  // The MediaStreamGraph will handle cycle detection. We don't need to do it
  // here.

  mOutputNodes.AppendElement(&aDestination);
  InputNode* input = aDestination.mInputNodes.AppendElement();
  input->mInputNode = this;
  input->mInputPort = aInput;
  input->mOutputPort = aOutput;
  AudioNodeStream* destinationStream = aDestination.mStream;
  if (mStream && destinationStream) {
    // Connect streams in the MediaStreamGraph
    MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
    MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
    input->mStreamPort = destinationStream->
      AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY,
                        static_cast<uint16_t>(aInput),
                        static_cast<uint16_t>(aOutput));
  }
  aDestination.NotifyInputsChanged();

  // This connection may have connected a panner and a source.
  Context()->UpdatePannerSource();

  return &aDestination;
}
void
AudioBufferSourceNode::Start(double aWhen, double aOffset,
                             const Optional<double>& aDuration, ErrorResult& aRv)
{
    if (!WebAudioUtils::IsTimeValid(aWhen) ||
            (aDuration.WasPassed() && !WebAudioUtils::IsTimeValid(aDuration.Value()))) {
        aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
        return;
    }

    if (mStartCalled) {
        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
        return;
    }
    mStartCalled = true;

    AudioNodeStream* ns = mStream;
    if (!ns) {
        // Nothing to play, or we're already dead for some reason
        return;
    }

    // Remember our arguments so that we can use them when we get a new buffer.
    mOffset = aOffset;
    mDuration = aDuration.WasPassed() ? aDuration.Value()
                : std::numeric_limits<double>::min();

    WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(),
                      NodeType(), Id(), aWhen, aOffset, mDuration);

    // We can't send these parameters without a buffer because we don't know the
    // buffer's sample rate or length.
    if (mBuffer) {
        SendOffsetAndDurationParametersToStream(ns);
    }

    // Don't set parameter unnecessarily
    if (aWhen > 0.0) {
        ns->SetDoubleParameter(START, aWhen);
    }
}
void
AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
{
    if (!WebAudioUtils::IsTimeValid(aWhen)) {
        aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
        return;
    }

    if (!mStartCalled) {
        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
        return;
    }

    WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(),
                      NodeType(), Id(), aWhen);

    AudioNodeStream* ns = mStream;
    if (!ns || !Context()) {
        // We've already stopped and had our stream shut down
        return;
    }

    ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
}
Ejemplo n.º 5
0
void
AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
{
  if (aOutput >= NumberOfOutputs()) {
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    return;
  }

  WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
                    NodeType(), Id());

  // An upstream node may be starting to play on the graph thread, and the
  // engine for a downstream node may be sending a PlayingRefChangeHandler
  // ADDREF message to this (main) thread.  Wait for a round trip before
  // releasing nodes, to give engines receiving sound now time to keep their
  // nodes alive.
  class RunnableRelease final : public nsRunnable
  {
  public:
    explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
      : mNode(aNode) {}

    NS_IMETHODIMP Run() override
    {
      mNode = nullptr;
      return NS_OK;
    }
  private:
    RefPtr<AudioNode> mNode;
  };

  for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) {
    AudioNode* dest = mOutputNodes[i];
    for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) {
      InputNode& input = dest->mInputNodes[j];
      if (input.mInputNode == this && input.mOutputPort == aOutput) {
        // Destroying the InputNode here sends a message to the graph thread
        // to disconnect the streams, which should be sent before the
        // RunAfterPendingUpdates() call below.
        dest->mInputNodes.RemoveElementAt(j);
        // Remove one instance of 'dest' from mOutputNodes. There could be
        // others, and it's not correct to remove them all since some of them
        // could be for different output ports.
        RefPtr<AudioNode> output = mOutputNodes[i].forget();
        mOutputNodes.RemoveElementAt(i);
        output->NotifyInputsChanged();
        if (mStream) {
          RefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
          mStream->RunAfterPendingUpdates(runnable.forget());
        }
        break;
      }
    }
  }

  for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
    AudioParam* dest = mOutputParams[i];
    for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
      const InputNode& input = dest->InputNodes()[j];
      if (input.mInputNode == this && input.mOutputPort == aOutput) {
        dest->RemoveInputNode(j);
        // Remove one instance of 'dest' from mOutputParams. There could be
        // others, and it's not correct to remove them all since some of them
        // could be for different output ports.
        mOutputParams.RemoveElementAt(i);
        break;
      }
    }
  }

  // This disconnection may have disconnected a panner and a source.
  Context()->UpdatePannerSource();
}