/**
  * Copy as many frames as possible from the source buffer to aOutput, and
  * advance aOffsetWithinBlock and aCurrentPosition based on how many frames
  * we write.  This will never advance aOffsetWithinBlock past
  * WEBAUDIO_BLOCK_SIZE, or aCurrentPosition past mStop.  It takes data from
  * the buffer at aBufferOffset, and never takes more data than aBufferMax.
  * This function knows when it needs to allocate the output buffer, and also
  * optimizes the case where it can avoid memory allocations.
  */
 void CopyFromBuffer(AudioNodeStream* aStream,
                     AudioChunk* aOutput,
                     uint32_t aChannels,
                     uint32_t* aOffsetWithinBlock,
                     TrackTicks* aCurrentPosition,
                     int32_t aBufferMax)
 {
   MOZ_ASSERT(*aCurrentPosition < mStop);
   uint32_t numFrames =
     std::min(std::min<TrackTicks>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
                                   aBufferMax - mBufferPosition),
              mStop - *aCurrentPosition);
   if (numFrames == WEBAUDIO_BLOCK_SIZE && !mResampler) {
     MOZ_ASSERT(mBufferPosition < aBufferMax);
     BorrowFromInputBuffer(aOutput, aChannels);
     *aOffsetWithinBlock += numFrames;
     *aCurrentPosition += numFrames;
     mBufferPosition += numFrames;
   } else {
     if (*aOffsetWithinBlock == 0) {
       AllocateAudioBlock(aChannels, aOutput);
     }
     if (!mResampler) {
       MOZ_ASSERT(mBufferPosition < aBufferMax);
       CopyFromInputBuffer(aOutput, aChannels, *aOffsetWithinBlock, numFrames);
       *aOffsetWithinBlock += numFrames;
       *aCurrentPosition += numFrames;
       mBufferPosition += numFrames;
     } else {
       CopyFromInputBufferWithResampling(aStream, aOutput, aChannels, aOffsetWithinBlock, aCurrentPosition, aBufferMax);
     }
   }
 }
  /**
   * Copy as many frames as possible from the source buffer to aOutput, and
   * advance aOffsetWithinBlock and aCurrentPosition based on how many frames
   * we copy.  This will never advance aOffsetWithinBlock past
   * WEBAUDIO_BLOCK_SIZE, or aCurrentPosition past mStop.  It takes data from
   * the buffer at aBufferOffset, and never takes more data than aBufferMax.
   * This function knows when it needs to allocate the output buffer, and also
   * optimizes the case where it can avoid memory allocations.
   */
  void CopyFromBuffer(AudioNodeStream* aStream,
                      AudioChunk* aOutput,
                      uint32_t aChannels,
                      uint32_t* aOffsetWithinBlock,
                      TrackTicks* aCurrentPosition,
                      uint32_t aBufferOffset,
                      uint32_t aBufferMax)
  {
    MOZ_ASSERT(*aCurrentPosition < mStop);
    uint32_t numFrames =
      std::min<TrackTicks>(std::min(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
                                    aBufferMax - aBufferOffset),
                           mStop - *aCurrentPosition);
    if (numFrames == WEBAUDIO_BLOCK_SIZE && !ShouldResample(aStream->SampleRate())) {
      BorrowFromInputBuffer(aOutput, aChannels, aBufferOffset);
      *aOffsetWithinBlock += numFrames;
      *aCurrentPosition += numFrames;
      mPosition += numFrames;
    } else {
      if (aOutput->IsNull()) {
        MOZ_ASSERT(*aOffsetWithinBlock == 0);
        AllocateAudioBlock(aChannels, aOutput);
      }
      if (!ShouldResample(aStream->SampleRate())) {
        CopyFromInputBuffer(aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, numFrames);
        *aOffsetWithinBlock += numFrames;
        *aCurrentPosition += numFrames;
        mPosition += numFrames;
      } else {
        uint32_t framesRead, framesWritten, availableInInputBuffer;

        availableInInputBuffer = aBufferMax - aBufferOffset;

        CopyFromInputBufferWithResampling(aStream, aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, availableInInputBuffer, framesRead, framesWritten);
        *aOffsetWithinBlock += framesWritten;
        *aCurrentPosition += framesRead;
        mPosition += framesRead;
      }
    }
  }
  /**
   * Copy as many frames as possible from the source buffer to aOutput, and
   * advance aOffsetWithinBlock and aCurrentPosition based on how many frames
   * we write.  This will never advance aOffsetWithinBlock past
   * WEBAUDIO_BLOCK_SIZE, or aCurrentPosition past mStop.  It takes data from
   * the buffer at aBufferOffset, and never takes more data than aBufferMax.
   * This function knows when it needs to allocate the output buffer, and also
   * optimizes the case where it can avoid memory allocations.
   */
  void CopyFromBuffer(AudioBlock* aOutput,
                      uint32_t aChannels,
                      uint32_t* aOffsetWithinBlock,
                      StreamTime* aCurrentPosition,
                      uint32_t aBufferMax)
  {
    MOZ_ASSERT(*aCurrentPosition < mStop);
    uint32_t availableInOutput =
      std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
                           mStop - *aCurrentPosition);
    if (mResampler) {
      CopyFromInputBufferWithResampling(aOutput, aChannels,
                                        aOffsetWithinBlock, availableInOutput,
                                        aCurrentPosition, aBufferMax);
      return;
    }

    if (aChannels == 0) {
      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
      // There is no attempt here to limit advance so that mBufferPosition is
      // limited to aBufferMax.  The only observable affect of skipping the
      // check would be in the precise timing of the ended event if the loop
      // attribute is reset after playback has looped.
      *aOffsetWithinBlock += availableInOutput;
      *aCurrentPosition += availableInOutput;
      // Rounding at the start and end of the period means that fractional
      // increments essentially accumulate if outRate remains constant.  If
      // outRate is varying, then accumulation happens on average but not
      // precisely.
      TrackTicks start = *aCurrentPosition *
        mBufferSampleRate / mResamplerOutRate;
      TrackTicks end = (*aCurrentPosition + availableInOutput) *
        mBufferSampleRate / mResamplerOutRate;
      mBufferPosition += end - start;
      return;
    }

    uint32_t numFrames = std::min(aBufferMax - mBufferPosition,
                                  availableInOutput);

    bool shouldBorrow = false;
    if (numFrames == WEBAUDIO_BLOCK_SIZE &&
        mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
      shouldBorrow = true;
      for (uint32_t i = 0; i < aChannels; ++i) {
        if (!IS_ALIGNED16(mBuffer.ChannelData<float>()[i] + mBufferPosition)) {
          shouldBorrow = false;
          break;
        }
      }
    }
    MOZ_ASSERT(mBufferPosition < aBufferMax);
    if (shouldBorrow) {
      BorrowFromInputBuffer(aOutput, aChannels);
    } else {
      if (*aOffsetWithinBlock == 0) {
        aOutput->AllocateChannels(aChannels);
      }
      if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
        CopyFromInputBuffer<float>(aOutput, aChannels,
                                   *aOffsetWithinBlock, numFrames);
      } else {
        MOZ_ASSERT(mBuffer.mBufferFormat == AUDIO_FORMAT_S16);
        CopyFromInputBuffer<int16_t>(aOutput, aChannels,
                                     *aOffsetWithinBlock, numFrames);
      }
    }
    *aOffsetWithinBlock += numFrames;
    *aCurrentPosition += numFrames;
    mBufferPosition += numFrames;
  }