LCOV - code coverage report
Current view: top level - dom/media/webaudio - AudioBufferSourceNode.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 405 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 45 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "AudioBufferSourceNode.h"
       8             : #include "nsDebug.h"
       9             : #include "mozilla/dom/AudioBufferSourceNodeBinding.h"
      10             : #include "mozilla/dom/AudioParam.h"
      11             : #include "mozilla/FloatingPoint.h"
      12             : #include "nsContentUtils.h"
      13             : #include "nsMathUtils.h"
      14             : #include "AlignmentUtils.h"
      15             : #include "AudioNodeEngine.h"
      16             : #include "AudioNodeStream.h"
      17             : #include "AudioDestinationNode.h"
      18             : #include "AudioParamTimeline.h"
      19             : #include <limits>
      20             : #include <algorithm>
      21             : 
      22             : namespace mozilla {
      23             : namespace dom {
      24             : 
      25           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioBufferSourceNode,
      26             :                                    AudioScheduledSourceNode, mBuffer,
      27             :                                    mPlaybackRate, mDetune)
      28             : 
      29           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioBufferSourceNode)
      30           0 : NS_INTERFACE_MAP_END_INHERITING(AudioScheduledSourceNode)
      31             : 
      32           0 : NS_IMPL_ADDREF_INHERITED(AudioBufferSourceNode, AudioScheduledSourceNode)
      33           0 : NS_IMPL_RELEASE_INHERITED(AudioBufferSourceNode, AudioScheduledSourceNode)
      34             : 
      35             : /**
      36             :  * Media-thread playback engine for AudioBufferSourceNode.
      37             :  * Nothing is played until a non-null buffer has been set (via
      38             :  * AudioNodeStream::SetBuffer) and a non-zero mBufferEnd has been set (via
      39             :  * AudioNodeStream::SetInt32Parameter).
      40             :  */
      41             : class AudioBufferSourceNodeEngine final : public AudioNodeEngine
      42             : {
      43             : public:
      44           0 :   AudioBufferSourceNodeEngine(AudioNode* aNode,
      45           0 :                               AudioDestinationNode* aDestination) :
      46             :     AudioNodeEngine(aNode),
      47             :     mStart(0.0), mBeginProcessing(0),
      48             :     mStop(STREAM_TIME_MAX),
      49             :     mResampler(nullptr), mRemainingResamplerTail(0),
      50             :     mBufferEnd(0),
      51             :     mLoopStart(0), mLoopEnd(0),
      52             :     mBufferPosition(0), mBufferSampleRate(0),
      53             :     // mResamplerOutRate is initialized in UpdateResampler().
      54             :     mChannels(0),
      55             :     mDopplerShift(1.0f),
      56           0 :     mDestination(aDestination->Stream()),
      57             :     mPlaybackRateTimeline(1.0f),
      58             :     mDetuneTimeline(0.0f),
      59           0 :     mLoop(false)
      60           0 :   {}
      61             : 
      62           0 :   ~AudioBufferSourceNodeEngine()
      63           0 :   {
      64           0 :     if (mResampler) {
      65           0 :       speex_resampler_destroy(mResampler);
      66             :     }
      67           0 :   }
      68             : 
      69           0 :   void SetSourceStream(AudioNodeStream* aSource)
      70             :   {
      71           0 :     mSource = aSource;
      72           0 :   }
      73             : 
      74           0 :   void RecvTimelineEvent(uint32_t aIndex,
      75             :                          dom::AudioTimelineEvent& aEvent) override
      76             :   {
      77           0 :     MOZ_ASSERT(mDestination);
      78           0 :     WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
      79           0 :                                                     mDestination);
      80             : 
      81           0 :     switch (aIndex) {
      82             :     case AudioBufferSourceNode::PLAYBACKRATE:
      83           0 :       mPlaybackRateTimeline.InsertEvent<int64_t>(aEvent);
      84           0 :       break;
      85             :     case AudioBufferSourceNode::DETUNE:
      86           0 :       mDetuneTimeline.InsertEvent<int64_t>(aEvent);
      87           0 :       break;
      88             :     default:
      89           0 :       NS_ERROR("Bad AudioBufferSourceNodeEngine TimelineParameter");
      90             :     }
      91           0 :   }
      92           0 :   void SetStreamTimeParameter(uint32_t aIndex, StreamTime aParam) override
      93             :   {
      94           0 :     switch (aIndex) {
      95           0 :     case AudioBufferSourceNode::STOP: mStop = aParam; break;
      96             :     default:
      97           0 :       NS_ERROR("Bad AudioBufferSourceNodeEngine StreamTimeParameter");
      98             :     }
      99           0 :   }
     100           0 :   void SetDoubleParameter(uint32_t aIndex, double aParam) override
     101             :   {
     102           0 :     switch (aIndex) {
     103             :     case AudioBufferSourceNode::START:
     104           0 :       MOZ_ASSERT(!mStart, "Another START?");
     105           0 :       mStart = aParam * mDestination->SampleRate();
     106             :       // Round to nearest
     107           0 :       mBeginProcessing = mStart + 0.5;
     108           0 :       break;
     109             :     case AudioBufferSourceNode::DOPPLERSHIFT:
     110           0 :       mDopplerShift = (aParam <= 0 || mozilla::IsNaN(aParam)) ? 1.0 : aParam;
     111           0 :       break;
     112             :     default:
     113           0 :       NS_ERROR("Bad AudioBufferSourceNodeEngine double parameter.");
     114             :     };
     115           0 :   }
     116           0 :   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
     117             :   {
     118           0 :     switch (aIndex) {
     119             :     case AudioBufferSourceNode::SAMPLE_RATE:
     120           0 :       MOZ_ASSERT(aParam > 0);
     121           0 :       mBufferSampleRate = aParam;
     122           0 :       mSource->SetActive();
     123           0 :       break;
     124             :     case AudioBufferSourceNode::BUFFERSTART:
     125           0 :       MOZ_ASSERT(aParam >= 0);
     126           0 :       if (mBufferPosition == 0) {
     127           0 :         mBufferPosition = aParam;
     128             :       }
     129           0 :       break;
     130             :     case AudioBufferSourceNode::BUFFEREND:
     131           0 :       MOZ_ASSERT(aParam >= 0);
     132           0 :       mBufferEnd = aParam;
     133           0 :       break;
     134           0 :     case AudioBufferSourceNode::LOOP: mLoop = !!aParam; break;
     135             :     case AudioBufferSourceNode::LOOPSTART:
     136           0 :       MOZ_ASSERT(aParam >= 0);
     137           0 :       mLoopStart = aParam;
     138           0 :       break;
     139             :     case AudioBufferSourceNode::LOOPEND:
     140           0 :       MOZ_ASSERT(aParam >= 0);
     141           0 :       mLoopEnd = aParam;
     142           0 :       break;
     143             :     default:
     144           0 :       NS_ERROR("Bad AudioBufferSourceNodeEngine Int32Parameter");
     145             :     }
     146           0 :   }
     147           0 :   void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
     148             :   {
     149           0 :     mBuffer = aBuffer;
     150           0 :   }
     151             : 
     152           0 :   bool BegunResampling()
     153             :   {
     154           0 :     return mBeginProcessing == -STREAM_TIME_MAX;
     155             :   }
     156             : 
     157           0 :   void UpdateResampler(int32_t aOutRate, uint32_t aChannels)
     158             :   {
     159           0 :     if (mResampler &&
     160           0 :         (aChannels != mChannels ||
     161             :          // If the resampler has begun, then it will have moved
     162             :          // mBufferPosition to after the samples it has read, but it hasn't
     163             :          // output its buffered samples.  Keep using the resampler, even if
     164             :          // the rates now match, so that this latent segment is output.
     165           0 :          (aOutRate == mBufferSampleRate && !BegunResampling()))) {
     166           0 :       speex_resampler_destroy(mResampler);
     167           0 :       mResampler = nullptr;
     168           0 :       mRemainingResamplerTail = 0;
     169           0 :       mBeginProcessing = mStart + 0.5;
     170             :     }
     171             : 
     172           0 :     if (aChannels == 0 ||
     173           0 :         (aOutRate == mBufferSampleRate && !mResampler)) {
     174           0 :       mResamplerOutRate = aOutRate;
     175           0 :       return;
     176             :     }
     177             : 
     178           0 :     if (!mResampler) {
     179           0 :       mChannels = aChannels;
     180           0 :       mResampler = speex_resampler_init(mChannels, mBufferSampleRate, aOutRate,
     181             :                                         SPEEX_RESAMPLER_QUALITY_MIN,
     182             :                                         nullptr);
     183             :     } else {
     184           0 :       if (mResamplerOutRate == aOutRate) {
     185           0 :         return;
     186             :       }
     187           0 :       if (speex_resampler_set_rate(mResampler, mBufferSampleRate, aOutRate) != RESAMPLER_ERR_SUCCESS) {
     188           0 :         NS_ASSERTION(false, "speex_resampler_set_rate failed");
     189           0 :         return;
     190             :       }
     191             :     }
     192             : 
     193           0 :     mResamplerOutRate = aOutRate;
     194             : 
     195           0 :     if (!BegunResampling()) {
     196             :       // Low pass filter effects from the resampler mean that samples before
     197             :       // the start time are influenced by resampling the buffer.  The input
     198             :       // latency indicates half the filter width.
     199           0 :       int64_t inputLatency = speex_resampler_get_input_latency(mResampler);
     200             :       uint32_t ratioNum, ratioDen;
     201           0 :       speex_resampler_get_ratio(mResampler, &ratioNum, &ratioDen);
     202             :       // The output subsample resolution supported in aligning the resampler
     203             :       // is ratioNum.  First round the start time to the nearest subsample.
     204           0 :       int64_t subsample = mStart * ratioNum + 0.5;
     205             :       // Now include the leading effects of the filter, and round *up* to the
     206             :       // next whole tick, because there is no effect on samples outside the
     207             :       // filter width.
     208           0 :       mBeginProcessing =
     209           0 :         (subsample - inputLatency * ratioDen + ratioNum - 1) / ratioNum;
     210             :     }
     211             :   }
     212             : 
     213             :   // Borrow a full buffer of size WEBAUDIO_BLOCK_SIZE from the source buffer
     214             :   // at offset aSourceOffset.  This avoids copying memory.
     215           0 :   void BorrowFromInputBuffer(AudioBlock* aOutput,
     216             :                              uint32_t aChannels)
     217             :   {
     218           0 :     aOutput->SetBuffer(mBuffer);
     219           0 :     aOutput->mChannelData.SetLength(aChannels);
     220           0 :     for (uint32_t i = 0; i < aChannels; ++i) {
     221           0 :       aOutput->mChannelData[i] = mBuffer->GetData(i) + mBufferPosition;
     222             :     }
     223           0 :     aOutput->mVolume = 1.0f;
     224           0 :     aOutput->mBufferFormat = AUDIO_FORMAT_FLOAT32;
     225           0 :   }
     226             : 
     227             :   // Copy aNumberOfFrames frames from the source buffer at offset aSourceOffset
     228             :   // and put it at offset aBufferOffset in the destination buffer.
     229           0 :   void CopyFromInputBuffer(AudioBlock* aOutput,
     230             :                            uint32_t aChannels,
     231             :                            uintptr_t aOffsetWithinBlock,
     232             :                            uint32_t aNumberOfFrames) {
     233           0 :     for (uint32_t i = 0; i < aChannels; ++i) {
     234           0 :       float* baseChannelData = aOutput->ChannelFloatsForWrite(i);
     235           0 :       memcpy(baseChannelData + aOffsetWithinBlock,
     236           0 :              mBuffer->GetData(i) + mBufferPosition,
     237           0 :              aNumberOfFrames * sizeof(float));
     238             :     }
     239           0 :   }
     240             : 
     241             :   // Resamples input data to an output buffer, according to |mBufferSampleRate| and
     242             :   // the playbackRate/detune.
     243             :   // The number of frames consumed/produced depends on the amount of space
     244             :   // remaining in both the input and output buffer, and the playback rate (that
     245             :   // is, the ratio between the output samplerate and the input samplerate).
     246           0 :   void CopyFromInputBufferWithResampling(AudioBlock* aOutput,
     247             :                                          uint32_t aChannels,
     248             :                                          uint32_t* aOffsetWithinBlock,
     249             :                                          uint32_t aAvailableInOutput,
     250             :                                          StreamTime* aCurrentPosition,
     251             :                                          uint32_t aBufferMax)
     252             :   {
     253           0 :     if (*aOffsetWithinBlock == 0) {
     254           0 :       aOutput->AllocateChannels(aChannels);
     255             :     }
     256           0 :     SpeexResamplerState* resampler = mResampler;
     257           0 :     MOZ_ASSERT(aChannels > 0);
     258             : 
     259           0 :     if (mBufferPosition < aBufferMax) {
     260           0 :       uint32_t availableInInputBuffer = aBufferMax - mBufferPosition;
     261             :       uint32_t ratioNum, ratioDen;
     262           0 :       speex_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
     263             :       // Limit the number of input samples copied and possibly
     264             :       // format-converted for resampling by estimating how many will be used.
     265             :       // This may be a little small if still filling the resampler with
     266             :       // initial data, but we'll get called again and it will work out.
     267           0 :       uint32_t inputLimit = aAvailableInOutput * ratioNum / ratioDen + 10;
     268           0 :       if (!BegunResampling()) {
     269             :         // First time the resampler is used.
     270           0 :         uint32_t inputLatency = speex_resampler_get_input_latency(resampler);
     271           0 :         inputLimit += inputLatency;
     272             :         // If starting after mStart, then play from the beginning of the
     273             :         // buffer, but correct for input latency.  If starting before mStart,
     274             :         // then align the resampler so that the time corresponding to the
     275             :         // first input sample is mStart.
     276           0 :         int64_t skipFracNum = static_cast<int64_t>(inputLatency) * ratioDen;
     277           0 :         double leadTicks = mStart - *aCurrentPosition;
     278           0 :         if (leadTicks > 0.0) {
     279             :           // Round to nearest output subsample supported by the resampler at
     280             :           // these rates.
     281           0 :           int64_t leadSubsamples = leadTicks * ratioNum + 0.5;
     282           0 :           MOZ_ASSERT(leadSubsamples <= skipFracNum,
     283             :                      "mBeginProcessing is wrong?");
     284           0 :           skipFracNum -= leadSubsamples;
     285             :         }
     286           0 :         speex_resampler_set_skip_frac_num(resampler,
     287           0 :                                   std::min<int64_t>(skipFracNum, UINT32_MAX));
     288             : 
     289           0 :         mBeginProcessing = -STREAM_TIME_MAX;
     290             :       }
     291           0 :       inputLimit = std::min(inputLimit, availableInInputBuffer);
     292             : 
     293           0 :       for (uint32_t i = 0; true; ) {
     294           0 :         uint32_t inSamples = inputLimit;
     295           0 :         const float* inputData = mBuffer->GetData(i) + mBufferPosition;
     296             : 
     297           0 :         uint32_t outSamples = aAvailableInOutput;
     298             :         float* outputData =
     299           0 :           aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
     300             : 
     301             :         WebAudioUtils::SpeexResamplerProcess(resampler, i,
     302             :                                              inputData, &inSamples,
     303           0 :                                              outputData, &outSamples);
     304           0 :         if (++i == aChannels) {
     305           0 :           mBufferPosition += inSamples;
     306           0 :           MOZ_ASSERT(mBufferPosition <= mBufferEnd || mLoop);
     307           0 :           *aOffsetWithinBlock += outSamples;
     308           0 :           *aCurrentPosition += outSamples;
     309           0 :           if (inSamples == availableInInputBuffer && !mLoop) {
     310             :             // We'll feed in enough zeros to empty out the resampler's memory.
     311             :             // This handles the output latency as well as capturing the low
     312             :             // pass effects of the resample filter.
     313           0 :             mRemainingResamplerTail =
     314           0 :               2 * speex_resampler_get_input_latency(resampler) - 1;
     315             :           }
     316           0 :           return;
     317             :         }
     318           0 :       }
     319             :     } else {
     320           0 :       for (uint32_t i = 0; true; ) {
     321           0 :         uint32_t inSamples = mRemainingResamplerTail;
     322           0 :         uint32_t outSamples = aAvailableInOutput;
     323             :         float* outputData =
     324           0 :           aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
     325             : 
     326             :         // AudioDataValue* for aIn selects the function that does not try to
     327             :         // copy and format-convert input data.
     328             :         WebAudioUtils::SpeexResamplerProcess(resampler, i,
     329             :                          static_cast<AudioDataValue*>(nullptr), &inSamples,
     330           0 :                          outputData, &outSamples);
     331           0 :         if (++i == aChannels) {
     332           0 :           MOZ_ASSERT(inSamples <= mRemainingResamplerTail);
     333           0 :           mRemainingResamplerTail -= inSamples;
     334           0 :           *aOffsetWithinBlock += outSamples;
     335           0 :           *aCurrentPosition += outSamples;
     336           0 :           break;
     337             :         }
     338           0 :       }
     339             :     }
     340             :   }
     341             : 
     342             :   /**
     343             :    * Fill aOutput with as many zero frames as we can, and advance
     344             :    * aOffsetWithinBlock and aCurrentPosition based on how many frames we write.
     345             :    * This will never advance aOffsetWithinBlock past WEBAUDIO_BLOCK_SIZE or
     346             :    * aCurrentPosition past aMaxPos.  This function knows when it needs to
     347             :    * allocate the output buffer, and also optimizes the case where it can avoid
     348             :    * memory allocations.
     349             :    */
     350           0 :   void FillWithZeroes(AudioBlock* aOutput,
     351             :                       uint32_t aChannels,
     352             :                       uint32_t* aOffsetWithinBlock,
     353             :                       StreamTime* aCurrentPosition,
     354             :                       StreamTime aMaxPos)
     355             :   {
     356           0 :     MOZ_ASSERT(*aCurrentPosition < aMaxPos);
     357             :     uint32_t numFrames =
     358           0 :       std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
     359           0 :                            aMaxPos - *aCurrentPosition);
     360           0 :     if (numFrames == WEBAUDIO_BLOCK_SIZE || !aChannels) {
     361           0 :       aOutput->SetNull(numFrames);
     362             :     } else {
     363           0 :       if (*aOffsetWithinBlock == 0) {
     364           0 :         aOutput->AllocateChannels(aChannels);
     365             :       }
     366           0 :       WriteZeroesToAudioBlock(aOutput, *aOffsetWithinBlock, numFrames);
     367             :     }
     368           0 :     *aOffsetWithinBlock += numFrames;
     369           0 :     *aCurrentPosition += numFrames;
     370           0 :   }
     371             : 
     372             :   /**
     373             :    * Copy as many frames as possible from the source buffer to aOutput, and
     374             :    * advance aOffsetWithinBlock and aCurrentPosition based on how many frames
     375             :    * we write.  This will never advance aOffsetWithinBlock past
     376             :    * WEBAUDIO_BLOCK_SIZE, or aCurrentPosition past mStop.  It takes data from
     377             :    * the buffer at aBufferOffset, and never takes more data than aBufferMax.
     378             :    * This function knows when it needs to allocate the output buffer, and also
     379             :    * optimizes the case where it can avoid memory allocations.
     380             :    */
     381           0 :   void CopyFromBuffer(AudioBlock* aOutput,
     382             :                       uint32_t aChannels,
     383             :                       uint32_t* aOffsetWithinBlock,
     384             :                       StreamTime* aCurrentPosition,
     385             :                       uint32_t aBufferMax)
     386             :   {
     387           0 :     MOZ_ASSERT(*aCurrentPosition < mStop);
     388             :     uint32_t availableInOutput =
     389           0 :       std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
     390           0 :                            mStop - *aCurrentPosition);
     391           0 :     if (mResampler) {
     392             :       CopyFromInputBufferWithResampling(aOutput, aChannels,
     393             :                                         aOffsetWithinBlock, availableInOutput,
     394           0 :                                         aCurrentPosition, aBufferMax);
     395           0 :       return;
     396             :     }
     397             : 
     398           0 :     if (aChannels == 0) {
     399           0 :       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     400             :       // There is no attempt here to limit advance so that mBufferPosition is
     401             :       // limited to aBufferMax.  The only observable affect of skipping the
     402             :       // check would be in the precise timing of the ended event if the loop
     403             :       // attribute is reset after playback has looped.
     404           0 :       *aOffsetWithinBlock += availableInOutput;
     405           0 :       *aCurrentPosition += availableInOutput;
     406             :       // Rounding at the start and end of the period means that fractional
     407             :       // increments essentially accumulate if outRate remains constant.  If
     408             :       // outRate is varying, then accumulation happens on average but not
     409             :       // precisely.
     410           0 :       TrackTicks start = *aCurrentPosition *
     411           0 :         mBufferSampleRate / mResamplerOutRate;
     412           0 :       TrackTicks end = (*aCurrentPosition + availableInOutput) *
     413           0 :         mBufferSampleRate / mResamplerOutRate;
     414           0 :       mBufferPosition += end - start;
     415           0 :       return;
     416             :     }
     417             : 
     418           0 :     uint32_t numFrames = std::min(aBufferMax - mBufferPosition,
     419           0 :                                   availableInOutput);
     420             : 
     421           0 :     bool inputBufferAligned = true;
     422           0 :     for (uint32_t i = 0; i < aChannels; ++i) {
     423           0 :       if (!IS_ALIGNED16(mBuffer->GetData(i) + mBufferPosition)) {
     424           0 :         inputBufferAligned = false;
     425             :       }
     426             :     }
     427             : 
     428           0 :     if (numFrames == WEBAUDIO_BLOCK_SIZE && inputBufferAligned) {
     429           0 :       MOZ_ASSERT(mBufferPosition < aBufferMax);
     430           0 :       BorrowFromInputBuffer(aOutput, aChannels);
     431             :     } else {
     432           0 :       if (*aOffsetWithinBlock == 0) {
     433           0 :         aOutput->AllocateChannels(aChannels);
     434             :       }
     435           0 :       MOZ_ASSERT(mBufferPosition < aBufferMax);
     436           0 :       CopyFromInputBuffer(aOutput, aChannels, *aOffsetWithinBlock, numFrames);
     437             :     }
     438           0 :     *aOffsetWithinBlock += numFrames;
     439           0 :     *aCurrentPosition += numFrames;
     440           0 :     mBufferPosition += numFrames;
     441             :   }
     442             : 
     443           0 :   int32_t ComputeFinalOutSampleRate(float aPlaybackRate, float aDetune)
     444             :   {
     445           0 :     float computedPlaybackRate = aPlaybackRate * pow(2, aDetune / 1200.f);
     446             :     // Make sure the playback rate and the doppler shift are something
     447             :     // our resampler can work with.
     448             :     int32_t rate = WebAudioUtils::
     449           0 :       TruncateFloatToInt<int32_t>(mSource->SampleRate() /
     450           0 :                                   (computedPlaybackRate * mDopplerShift));
     451           0 :     return rate ? rate : mBufferSampleRate;
     452             :   }
     453             : 
     454           0 :   void UpdateSampleRateIfNeeded(uint32_t aChannels, StreamTime aStreamPosition)
     455             :   {
     456             :     float playbackRate;
     457             :     float detune;
     458             : 
     459           0 :     if (mPlaybackRateTimeline.HasSimpleValue()) {
     460           0 :       playbackRate = mPlaybackRateTimeline.GetValue();
     461             :     } else {
     462           0 :       playbackRate = mPlaybackRateTimeline.GetValueAtTime(aStreamPosition);
     463             :     }
     464           0 :     if (mDetuneTimeline.HasSimpleValue()) {
     465           0 :       detune = mDetuneTimeline.GetValue();
     466             :     } else {
     467           0 :       detune = mDetuneTimeline.GetValueAtTime(aStreamPosition);
     468             :     }
     469           0 :     if (playbackRate <= 0 || mozilla::IsNaN(playbackRate)) {
     470           0 :       playbackRate = 1.0f;
     471             :     }
     472             : 
     473           0 :     detune = std::min(std::max(-1200.f, detune), 1200.f);
     474             : 
     475           0 :     int32_t outRate = ComputeFinalOutSampleRate(playbackRate, detune);
     476           0 :     UpdateResampler(outRate, aChannels);
     477           0 :   }
     478             : 
     479           0 :   void ProcessBlock(AudioNodeStream* aStream,
     480             :                     GraphTime aFrom,
     481             :                     const AudioBlock& aInput,
     482             :                     AudioBlock* aOutput,
     483             :                     bool* aFinished) override
     484             :   {
     485           0 :     if (mBufferSampleRate == 0) {
     486             :       // start() has not yet been called or no buffer has yet been set
     487           0 :       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     488           0 :       return;
     489             :     }
     490             : 
     491           0 :     StreamTime streamPosition = mDestination->GraphTimeToStreamTime(aFrom);
     492           0 :     uint32_t channels = mBuffer ? mBuffer->GetChannels() : 0;
     493             : 
     494           0 :     UpdateSampleRateIfNeeded(channels, streamPosition);
     495             : 
     496           0 :     uint32_t written = 0;
     497           0 :     while (written < WEBAUDIO_BLOCK_SIZE) {
     498           0 :       if (mStop != STREAM_TIME_MAX &&
     499           0 :           streamPosition >= mStop) {
     500           0 :         FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
     501           0 :         continue;
     502             :       }
     503           0 :       if (streamPosition < mBeginProcessing) {
     504           0 :         FillWithZeroes(aOutput, channels, &written, &streamPosition,
     505           0 :                        mBeginProcessing);
     506           0 :         continue;
     507             :       }
     508           0 :       if (mLoop) {
     509             :         // mLoopEnd can become less than mBufferPosition when a LOOPEND engine
     510             :         // parameter is received after "loopend" is changed on the node or a
     511             :         // new buffer with lower samplerate is set.
     512           0 :         if (mBufferPosition >= mLoopEnd) {
     513           0 :           mBufferPosition = mLoopStart;
     514             :         }
     515           0 :         CopyFromBuffer(aOutput, channels, &written, &streamPosition, mLoopEnd);
     516             :       } else {
     517           0 :         if (mBufferPosition < mBufferEnd || mRemainingResamplerTail) {
     518           0 :           CopyFromBuffer(aOutput, channels, &written, &streamPosition, mBufferEnd);
     519             :         } else {
     520           0 :           FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
     521             :         }
     522             :       }
     523             :     }
     524             : 
     525             :     // We've finished if we've gone past mStop, or if we're past mDuration when
     526             :     // looping is disabled.
     527           0 :     if (streamPosition >= mStop ||
     528           0 :         (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
     529           0 :       *aFinished = true;
     530             :     }
     531             :   }
     532             : 
     533           0 :   bool IsActive() const override
     534             :   {
     535             :     // Whether buffer has been set and start() has been called.
     536           0 :     return mBufferSampleRate != 0;
     537             :   }
     538             : 
     539           0 :   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
     540             :   {
     541             :     // Not owned:
     542             :     // - mBuffer - shared w/ AudioNode
     543             :     // - mPlaybackRateTimeline - shared w/ AudioNode
     544             :     // - mDetuneTimeline - shared w/ AudioNode
     545             : 
     546           0 :     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     547             : 
     548             :     // NB: We need to modify speex if we want the full memory picture, internal
     549             :     //     fields that need measuring noted below.
     550             :     // - mResampler->mem
     551             :     // - mResampler->sinc_table
     552             :     // - mResampler->last_sample
     553             :     // - mResampler->magic_samples
     554             :     // - mResampler->samp_frac_num
     555           0 :     amount += aMallocSizeOf(mResampler);
     556             : 
     557           0 :     return amount;
     558             :   }
     559             : 
     560           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
     561             :   {
     562           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     563             :   }
     564             : 
     565             :   double mStart; // including the fractional position between ticks
     566             :   // Low pass filter effects from the resampler mean that samples before the
     567             :   // start time are influenced by resampling the buffer.  mBeginProcessing
     568             :   // includes the extent of this filter.  The special value of -STREAM_TIME_MAX
     569             :   // indicates that the resampler has begun processing.
     570             :   StreamTime mBeginProcessing;
     571             :   StreamTime mStop;
     572             :   RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
     573             :   SpeexResamplerState* mResampler;
     574             :   // mRemainingResamplerTail, like mBufferPosition, and
     575             :   // mBufferEnd, is measured in input buffer samples.
     576             :   uint32_t mRemainingResamplerTail;
     577             :   uint32_t mBufferEnd;
     578             :   uint32_t mLoopStart;
     579             :   uint32_t mLoopEnd;
     580             :   uint32_t mBufferPosition;
     581             :   int32_t mBufferSampleRate;
     582             :   int32_t mResamplerOutRate;
     583             :   uint32_t mChannels;
     584             :   float mDopplerShift;
     585             :   AudioNodeStream* mDestination;
     586             :   AudioNodeStream* mSource;
     587             :   AudioParamTimeline mPlaybackRateTimeline;
     588             :   AudioParamTimeline mDetuneTimeline;
     589             :   bool mLoop;
     590             : };
     591             : 
     592           0 : AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
     593             :   : AudioScheduledSourceNode(aContext,
     594             :                              2,
     595             :                              ChannelCountMode::Max,
     596             :                              ChannelInterpretation::Speakers)
     597             :   , mLoopStart(0.0)
     598             :   , mLoopEnd(0.0)
     599             :   // mOffset and mDuration are initialized in Start().
     600           0 :   , mPlaybackRate(new AudioParam(this, PLAYBACKRATE, "playbackRate", 1.0f))
     601           0 :   , mDetune(new AudioParam(this, DETUNE, "detune", 0.0f))
     602             :   , mLoop(false)
     603           0 :   , mStartCalled(false)
     604             : {
     605           0 :   AudioBufferSourceNodeEngine* engine = new AudioBufferSourceNodeEngine(this, aContext->Destination());
     606           0 :   mStream = AudioNodeStream::Create(aContext, engine,
     607             :                                     AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
     608           0 :                                     aContext->Graph());
     609           0 :   engine->SetSourceStream(mStream);
     610           0 :   mStream->AddMainThreadListener(this);
     611           0 : }
     612             : 
     613             : /* static */ already_AddRefed<AudioBufferSourceNode>
     614           0 : AudioBufferSourceNode::Create(JSContext* aCx, AudioContext& aAudioContext,
     615             :                               const AudioBufferSourceOptions& aOptions,
     616             :                               ErrorResult& aRv)
     617             : {
     618           0 :   if (aAudioContext.CheckClosed(aRv)) {
     619           0 :     return nullptr;
     620             :   }
     621             : 
     622           0 :   RefPtr<AudioBufferSourceNode> audioNode = new AudioBufferSourceNode(&aAudioContext);
     623             : 
     624           0 :   if (aOptions.mBuffer.WasPassed()) {
     625           0 :     MOZ_ASSERT(aCx);
     626           0 :     audioNode->SetBuffer(aCx, aOptions.mBuffer.Value());
     627             :   }
     628             : 
     629           0 :   audioNode->Detune()->SetValue(aOptions.mDetune);
     630           0 :   audioNode->SetLoop(aOptions.mLoop);
     631           0 :   audioNode->SetLoopEnd(aOptions.mLoopEnd);
     632           0 :   audioNode->SetLoopStart(aOptions.mLoopStart);
     633           0 :   audioNode->PlaybackRate()->SetValue(aOptions.mPlaybackRate);
     634             : 
     635           0 :   return audioNode.forget();
     636             : }
     637             : void
     638           0 : AudioBufferSourceNode::DestroyMediaStream()
     639             : {
     640           0 :   bool hadStream = mStream;
     641           0 :   if (hadStream) {
     642           0 :     mStream->RemoveMainThreadListener(this);
     643             :   }
     644           0 :   AudioNode::DestroyMediaStream();
     645           0 :   if (hadStream && Context()) {
     646           0 :     Context()->UnregisterAudioBufferSourceNode(this);
     647             :   }
     648           0 : }
     649             : 
     650             : size_t
     651           0 : AudioBufferSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     652             : {
     653           0 :   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
     654             : 
     655             :   /* mBuffer can be shared and is accounted for separately. */
     656             : 
     657           0 :   amount += mPlaybackRate->SizeOfIncludingThis(aMallocSizeOf);
     658           0 :   amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
     659           0 :   return amount;
     660             : }
     661             : 
     662             : size_t
     663           0 : AudioBufferSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     664             : {
     665           0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     666             : }
     667             : 
     668             : JSObject*
     669           0 : AudioBufferSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     670             : {
     671           0 :   return AudioBufferSourceNodeBinding::Wrap(aCx, this, aGivenProto);
     672             : }
     673             : 
     674             : void
     675           0 : AudioBufferSourceNode::Start(double aWhen, double aOffset,
     676             :                              const Optional<double>& aDuration, ErrorResult& aRv)
     677             : {
     678           0 :   if (!WebAudioUtils::IsTimeValid(aWhen) ||
     679           0 :       (aDuration.WasPassed() && !WebAudioUtils::IsTimeValid(aDuration.Value()))) {
     680           0 :     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     681           0 :     return;
     682             :   }
     683             : 
     684           0 :   if (mStartCalled) {
     685           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     686           0 :     return;
     687             :   }
     688           0 :   mStartCalled = true;
     689             : 
     690           0 :   AudioNodeStream* ns = mStream;
     691           0 :   if (!ns) {
     692             :     // Nothing to play, or we're already dead for some reason
     693           0 :     return;
     694             :   }
     695             : 
     696             :   // Remember our arguments so that we can use them when we get a new buffer.
     697           0 :   mOffset = aOffset;
     698           0 :   mDuration = aDuration.WasPassed() ? aDuration.Value()
     699             :                                     : std::numeric_limits<double>::min();
     700             : 
     701           0 :   WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(),
     702             :                     NodeType(), Id(), aWhen, aOffset, mDuration);
     703             : 
     704             :   // We can't send these parameters without a buffer because we don't know the
     705             :   // buffer's sample rate or length.
     706           0 :   if (mBuffer) {
     707           0 :     SendOffsetAndDurationParametersToStream(ns);
     708             :   }
     709             : 
     710             :   // Don't set parameter unnecessarily
     711           0 :   if (aWhen > 0.0) {
     712           0 :     ns->SetDoubleParameter(START, aWhen);
     713             :   }
     714             : }
     715             : 
     716             : void
     717           0 : AudioBufferSourceNode::Start(double aWhen, ErrorResult& aRv)
     718             : {
     719           0 :   Start(aWhen, 0 /* offset */, Optional<double>(), aRv);
     720           0 : }
     721             : 
     722             : void
     723           0 : AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
     724             : {
     725           0 :   AudioNodeStream* ns = mStream;
     726           0 :   if (!ns) {
     727           0 :     return;
     728             :   }
     729             : 
     730           0 :   if (mBuffer) {
     731             :     RefPtr<ThreadSharedFloatArrayBufferList> data =
     732           0 :       mBuffer->GetThreadSharedChannelsForRate(aCx);
     733           0 :     ns->SetBuffer(data.forget());
     734             : 
     735           0 :     if (mStartCalled) {
     736           0 :       SendOffsetAndDurationParametersToStream(ns);
     737             :     }
     738             :   } else {
     739           0 :     ns->SetInt32Parameter(BUFFEREND, 0);
     740           0 :     ns->SetBuffer(nullptr);
     741             : 
     742           0 :     MarkInactive();
     743             :   }
     744             : }
     745             : 
     746             : void
     747           0 : AudioBufferSourceNode::SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream)
     748             : {
     749           0 :   NS_ASSERTION(mBuffer && mStartCalled,
     750             :                "Only call this when we have a buffer and start() has been called");
     751             : 
     752           0 :   float rate = mBuffer->SampleRate();
     753           0 :   aStream->SetInt32Parameter(SAMPLE_RATE, rate);
     754             : 
     755           0 :   int32_t bufferEnd = mBuffer->Length();
     756           0 :   int32_t offsetSamples = std::max(0, NS_lround(mOffset * rate));
     757             : 
     758             :   // Don't set parameter unnecessarily
     759           0 :   if (offsetSamples > 0) {
     760           0 :     aStream->SetInt32Parameter(BUFFERSTART, offsetSamples);
     761             :   }
     762             : 
     763           0 :   if (mDuration != std::numeric_limits<double>::min()) {
     764           0 :     MOZ_ASSERT(mDuration >= 0.0); // provided by Start()
     765           0 :     MOZ_ASSERT(rate >= 0.0f); // provided by AudioBuffer::Create()
     766             :     static_assert(std::numeric_limits<double>::digits >=
     767             :                   std::numeric_limits<decltype(bufferEnd)>::digits,
     768             :                   "bufferEnd should be represented exactly by double");
     769             :     // + 0.5 rounds mDuration to nearest sample when assigned to bufferEnd.
     770           0 :     bufferEnd = std::min<double>(bufferEnd,
     771           0 :                                  offsetSamples + mDuration * rate + 0.5);
     772             :   }
     773           0 :   aStream->SetInt32Parameter(BUFFEREND, bufferEnd);
     774             : 
     775           0 :   MarkActive();
     776           0 : }
     777             : 
     778             : void
     779           0 : AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
     780             : {
     781           0 :   if (!WebAudioUtils::IsTimeValid(aWhen)) {
     782           0 :     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     783           0 :     return;
     784             :   }
     785             : 
     786           0 :   if (!mStartCalled) {
     787           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     788           0 :     return;
     789             :   }
     790             : 
     791           0 :   WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(),
     792             :                     NodeType(), Id(), aWhen);
     793             : 
     794           0 :   AudioNodeStream* ns = mStream;
     795           0 :   if (!ns || !Context()) {
     796             :     // We've already stopped and had our stream shut down
     797           0 :     return;
     798             :   }
     799             : 
     800           0 :   ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
     801             : }
     802             : 
     803             : void
     804           0 : AudioBufferSourceNode::NotifyMainThreadStreamFinished()
     805             : {
     806           0 :   MOZ_ASSERT(mStream->IsFinished());
     807             : 
     808           0 :   class EndedEventDispatcher final : public Runnable
     809             :   {
     810             :   public:
     811           0 :     explicit EndedEventDispatcher(AudioBufferSourceNode* aNode)
     812           0 :       : mozilla::Runnable("EndedEventDispatcher")
     813           0 :       , mNode(aNode)
     814             :     {
     815           0 :     }
     816           0 :     NS_IMETHOD Run() override
     817             :     {
     818             :       // If it's not safe to run scripts right now, schedule this to run later
     819           0 :       if (!nsContentUtils::IsSafeToRunScript()) {
     820           0 :         nsContentUtils::AddScriptRunner(this);
     821           0 :         return NS_OK;
     822             :       }
     823             : 
     824           0 :       mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
     825             :       // Release stream resources.
     826           0 :       mNode->DestroyMediaStream();
     827           0 :       return NS_OK;
     828             :     }
     829             :   private:
     830             :     RefPtr<AudioBufferSourceNode> mNode;
     831             :   };
     832             : 
     833           0 :   Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this)));
     834             : 
     835             :   // Drop the playing reference
     836             :   // Warning: The below line might delete this.
     837           0 :   MarkInactive();
     838           0 : }
     839             : 
     840             : void
     841           0 : AudioBufferSourceNode::SendDopplerShiftToStream(double aDopplerShift)
     842             : {
     843           0 :   MOZ_ASSERT(mStream, "Should have disconnected panner if no stream");
     844           0 :   SendDoubleParameterToStream(DOPPLERSHIFT, aDopplerShift);
     845           0 : }
     846             : 
     847             : void
     848           0 : AudioBufferSourceNode::SendLoopParametersToStream()
     849             : {
     850           0 :   if (!mStream) {
     851           0 :     return;
     852             :   }
     853             :   // Don't compute and set the loop parameters unnecessarily
     854           0 :   if (mLoop && mBuffer) {
     855           0 :     float rate = mBuffer->SampleRate();
     856           0 :     double length = (double(mBuffer->Length()) / mBuffer->SampleRate());
     857             :     double actualLoopStart, actualLoopEnd;
     858           0 :     if (mLoopStart >= 0.0 && mLoopEnd > 0.0 &&
     859           0 :         mLoopStart < mLoopEnd) {
     860           0 :       MOZ_ASSERT(mLoopStart != 0.0 || mLoopEnd != 0.0);
     861           0 :       actualLoopStart = (mLoopStart > length) ? 0.0 : mLoopStart;
     862           0 :       actualLoopEnd = std::min(mLoopEnd, length);
     863             :     } else {
     864           0 :       actualLoopStart = 0.0;
     865           0 :       actualLoopEnd = length;
     866             :     }
     867           0 :     int32_t loopStartTicks = NS_lround(actualLoopStart * rate);
     868           0 :     int32_t loopEndTicks = NS_lround(actualLoopEnd * rate);
     869           0 :     if (loopStartTicks < loopEndTicks) {
     870           0 :       SendInt32ParameterToStream(LOOPSTART, loopStartTicks);
     871           0 :       SendInt32ParameterToStream(LOOPEND, loopEndTicks);
     872           0 :       SendInt32ParameterToStream(LOOP, 1);
     873             :     } else {
     874             :       // Be explicit about looping not happening if the offsets make
     875             :       // looping impossible.
     876           0 :       SendInt32ParameterToStream(LOOP, 0);
     877             :     }
     878             :   } else {
     879           0 :     SendInt32ParameterToStream(LOOP, 0);
     880             :   }
     881             : }
     882             : 
     883             : } // namespace dom
     884             : } // namespace mozilla

Generated by: LCOV version 1.13