bug 1201855 keep track of buffer position even when there are no channels r=padenot
authorKarl Tomlinson <karlt+@karlt.net>
Sat, 05 Sep 2015 00:50:35 +1200
changeset 294152 626165ab9f6c8c0a7415505a7f7181ffa85a3195
parent 294151 c0d655ae3cb5c7c15c6ea45174e6882c0bd36195
child 294153 358256d1f999d6472a94c6bf5e863646e224736d
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1201855
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1201855 keep track of buffer position even when there are no channels r=padenot
dom/media/webaudio/AudioBufferSourceNode.cpp
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -39,17 +39,19 @@ public:
   AudioBufferSourceNodeEngine(AudioNode* aNode,
                               AudioDestinationNode* aDestination) :
     AudioNodeEngine(aNode),
     mStart(0.0), mBeginProcessing(0),
     mStop(STREAM_TIME_MAX),
     mResampler(nullptr), mRemainingResamplerTail(0),
     mBufferEnd(0),
     mLoopStart(0), mLoopEnd(0),
-    mBufferSampleRate(0), mBufferPosition(0), mChannels(0),
+    mBufferPosition(0), mBufferSampleRate(0),
+    // mResamplerOutRate is initialized in UpdateResampler().
+    mChannels(0),
     mDopplerShift(1.0f),
     mDestination(aDestination->Stream()),
     mPlaybackRateTimeline(1.0f),
     mDetuneTimeline(0.0f),
     mLoop(false)
   {}
 
   ~AudioBufferSourceNodeEngine()
@@ -143,35 +145,36 @@ public:
          // the rates now match, so that this latent segment is output.
          (aOutRate == mBufferSampleRate && !BegunResampling()))) {
       speex_resampler_destroy(mResampler);
       mResampler = nullptr;
       mRemainingResamplerTail = 0;
       mBeginProcessing = mStart + 0.5;
     }
 
-    if (aOutRate == mBufferSampleRate && !mResampler) {
+    if (aChannels == 0 ||
+        (aOutRate == mBufferSampleRate && !mResampler)) {
+      mResamplerOutRate = aOutRate;
       return;
     }
 
     if (!mResampler) {
       mChannels = aChannels;
       mResampler = speex_resampler_init(mChannels, mBufferSampleRate, aOutRate,
                                         SPEEX_RESAMPLER_QUALITY_MIN,
                                         nullptr);
     } else {
-      uint32_t currentOutSampleRate, currentInSampleRate;
-      speex_resampler_get_rate(mResampler, &currentInSampleRate,
-                               &currentOutSampleRate);
-      if (currentOutSampleRate == static_cast<uint32_t>(aOutRate)) {
+      if (mResamplerOutRate == aOutRate) {
         return;
       }
-      speex_resampler_set_rate(mResampler, currentInSampleRate, aOutRate);
+      speex_resampler_set_rate(mResampler, mBufferSampleRate, aOutRate);
     }
 
+    mResamplerOutRate = aOutRate;
+
     if (!BegunResampling()) {
       // Low pass filter effects from the resampler mean that samples before
       // the start time are influenced by resampling the buffer.  The input
       // latency indicates half the filter width.
       int64_t inputLatency = speex_resampler_get_input_latency(mResampler);
       uint32_t ratioNum, ratioDen;
       speex_resampler_get_ratio(mResampler, &ratioNum, &ratioDen);
       // The output subsample resolution supported in aligning the resampler
@@ -325,17 +328,17 @@ public:
                       uint32_t* aOffsetWithinBlock,
                       StreamTime* aCurrentPosition,
                       StreamTime aMaxPos)
   {
     MOZ_ASSERT(*aCurrentPosition < aMaxPos);
     uint32_t numFrames =
       std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
                            aMaxPos - *aCurrentPosition);
-    if (numFrames == WEBAUDIO_BLOCK_SIZE) {
+    if (numFrames == WEBAUDIO_BLOCK_SIZE || !aChannels) {
       aOutput->SetNull(numFrames);
     } else {
       if (*aOffsetWithinBlock == 0) {
         aOutput->AllocateChannels(aChannels);
       }
       WriteZeroesToAudioBlock(aOutput, *aOffsetWithinBlock, numFrames);
     }
     *aOffsetWithinBlock += numFrames;
@@ -363,16 +366,37 @@ public:
       std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
                            mStop - *aCurrentPosition);
     if (mResampler) {
       CopyFromInputBufferWithResampling(aStream, 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<uint32_t>(aBufferMax - mBufferPosition,
                                             availableInOutput);
     if (numFrames == WEBAUDIO_BLOCK_SIZE) {
       MOZ_ASSERT(mBufferPosition < aBufferMax);
       BorrowFromInputBuffer(aOutput, aChannels);
     } else {
       if (*aOffsetWithinBlock == 0) {
         aOutput->AllocateChannels(aChannels);
@@ -427,20 +451,16 @@ public:
                             bool* aFinished) override
   {
     if (!mBuffer || !mBufferEnd) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
     uint32_t channels = mBuffer->GetChannels();
-    if (!channels) {
-      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
-      return;
-    }
 
     UpdateSampleRateIfNeeded(channels);
 
     uint32_t written = 0;
     StreamTime streamPosition = aStream->GetCurrentPosition();
     while (written < WEBAUDIO_BLOCK_SIZE) {
       if (mStop != STREAM_TIME_MAX &&
           streamPosition >= mStop) {
@@ -513,18 +533,19 @@ public:
   nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
   SpeexResamplerState* mResampler;
   // mRemainingResamplerTail, like mBufferPosition, and
   // mBufferEnd, is measured in input buffer samples.
   int mRemainingResamplerTail;
   int32_t mBufferEnd;
   int32_t mLoopStart;
   int32_t mLoopEnd;
+  int32_t mBufferPosition;
   int32_t mBufferSampleRate;
-  int32_t mBufferPosition;
+  int32_t mResamplerOutRate;
   uint32_t mChannels;
   float mDopplerShift;
   AudioNodeStream* mDestination;
   AudioNodeStream* mSource;
   AudioParamTimeline mPlaybackRateTimeline;
   AudioParamTimeline mDetuneTimeline;
   bool mLoop;
 };