content/media/webaudio/OscillatorNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 #include "OscillatorNode.h"
     8 #include "AudioNodeEngine.h"
     9 #include "AudioNodeStream.h"
    10 #include "AudioDestinationNode.h"
    11 #include "WebAudioUtils.h"
    12 #include "blink/PeriodicWave.h"
    14 namespace mozilla {
    15 namespace dom {
    17 NS_IMPL_CYCLE_COLLECTION_INHERITED(OscillatorNode, AudioNode,
    18                                    mPeriodicWave, mFrequency, mDetune)
    20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
    21 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
    23 NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
    24 NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
    26 static const float sLeakTriangle = 0.995f;
    27 static const float sLeak = 0.999f;
    29 class DCBlocker
    30 {
    31 public:
    32   // These are sane defauts when the initial mPhase is zero
    33   DCBlocker(float aLastInput = 0.0f,
    34             float aLastOutput = 0.0f,
    35             float aPole = 0.995)
    36     :mLastInput(aLastInput),
    37      mLastOutput(aLastOutput),
    38      mPole(aPole)
    39   {
    40     MOZ_ASSERT(aPole > 0);
    41   }
    43   inline float Process(float aInput)
    44   {
    45     float out;
    47     out = mLastOutput * mPole + aInput - mLastInput;
    48     mLastOutput = out;
    49     mLastInput = aInput;
    51     return out;
    52   }
    53 private:
    54   float mLastInput;
    55   float mLastOutput;
    56   float mPole;
    57 };
    60 class OscillatorNodeEngine : public AudioNodeEngine
    61 {
    62 public:
    63   OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
    64     : AudioNodeEngine(aNode)
    65     , mSource(nullptr)
    66     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
    67     , mStart(-1)
    68     , mStop(TRACK_TICKS_MAX)
    69     // Keep the default values in sync with OscillatorNode::OscillatorNode.
    70     , mFrequency(440.f)
    71     , mDetune(0.f)
    72     , mType(OscillatorType::Sine)
    73     , mPhase(0.)
    74     // mSquare, mTriangle, and mSaw are not used for default type "sine".
    75     // They are initialized if and when switching to the OscillatorTypes that
    76     // use them.
    77     // mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
    78     // mPhaseIncrement, and mPhaseWrap are initialized in
    79     // UpdateParametersIfNeeded() when mRecomputeParameters is set.
    80     , mRecomputeParameters(true)
    81     , mCustomLength(0)
    82   {
    83   }
    85   void SetSourceStream(AudioNodeStream* aSource)
    86   {
    87     mSource = aSource;
    88   }
    90   enum Parameters {
    91     FREQUENCY,
    92     DETUNE,
    93     TYPE,
    94     PERIODICWAVE,
    95     START,
    96     STOP,
    97   };
    98   void SetTimelineParameter(uint32_t aIndex,
    99                             const AudioParamTimeline& aValue,
   100                             TrackRate aSampleRate) MOZ_OVERRIDE
   101   {
   102     mRecomputeParameters = true;
   103     switch (aIndex) {
   104     case FREQUENCY:
   105       MOZ_ASSERT(mSource && mDestination);
   106       mFrequency = aValue;
   107       WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
   108       break;
   109     case DETUNE:
   110       MOZ_ASSERT(mSource && mDestination);
   111       mDetune = aValue;
   112       WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
   113       break;
   114     default:
   115       NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
   116     }
   117   }
   119   virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
   120   {
   121     switch (aIndex) {
   122     case START: mStart = aParam; break;
   123     case STOP: mStop = aParam; break;
   124     default:
   125       NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
   126     }
   127   }
   129   virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
   130   {
   131     switch (aIndex) {
   132       case TYPE:
   133         // Set the new type.
   134         mType = static_cast<OscillatorType>(aParam);
   135         if (mType != OscillatorType::Custom) {
   136           // Forget any previous custom data.
   137           mCustomLength = 0;
   138           mCustom = nullptr;
   139           mPeriodicWave = nullptr;
   140           mRecomputeParameters = true;
   141         }
   142         // Update BLIT integrators with the new initial conditions.
   143         switch (mType) {
   144           case OscillatorType::Sine:
   145             mPhase = 0.0;
   146             break;
   147           case OscillatorType::Square:
   148             mPhase = 0.0;
   149             // Initial integration condition is -0.5, because our
   150             // square has 50% duty cycle.
   151             mSquare = -0.5;
   152             break;
   153           case OscillatorType::Triangle:
   154             // Initial mPhase and related integration condition so the
   155             // triangle is in the middle of the first upward slope.
   156             // XXX actually do the maths and put the right number here.
   157             mPhase = (float)(M_PI / 2);
   158             mSquare = 0.5;
   159             mTriangle = 0.0;
   160             break;
   161           case OscillatorType::Sawtooth:
   162             // Initial mPhase so the oscillator starts at the
   163             // middle of the ramp, per spec.
   164             mPhase = (float)(M_PI / 2);
   165             // mSaw = 0 when mPhase = pi/2.
   166             mSaw = 0.0;
   167             break;
   168           case OscillatorType::Custom:
   169             // Custom waveforms don't use BLIT.
   170             break;
   171           default:
   172             NS_ERROR("Bad OscillatorNodeEngine type parameter.");
   173         }
   174         // End type switch.
   175         break;
   176       case PERIODICWAVE:
   177         MOZ_ASSERT(aParam >= 0, "negative custom array length");
   178         mCustomLength = static_cast<uint32_t>(aParam);
   179         break;
   180       default:
   181         NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
   182     }
   183     // End index switch.
   184   }
   186   virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
   187   {
   188     MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
   189     mCustom = aBuffer;
   190     MOZ_ASSERT(mCustom->GetChannels() == 2,
   191                "PeriodicWave should have sent two channels");
   192     mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
   193     mCustom->GetData(0), mCustom->GetData(1), mCustomLength);
   194   }
   196   void IncrementPhase()
   197   {
   198     mPhase += mPhaseIncrement;
   199     if (mPhase > mPhaseWrap) {
   200       mPhase -= mPhaseWrap;
   201     }
   202   }
   204   // Square and triangle are using a bipolar band-limited impulse train, saw is
   205   // using a normal band-limited impulse train.
   206   bool UsesBipolarBLIT() {
   207     return mType == OscillatorType::Square || mType == OscillatorType::Triangle;
   208   }
   210   void UpdateParametersIfNeeded(TrackTicks ticks, size_t count)
   211   {
   212     double frequency, detune;
   214     bool simpleFrequency = mFrequency.HasSimpleValue();
   215     bool simpleDetune = mDetune.HasSimpleValue();
   217     // Shortcut if frequency-related AudioParam are not automated, and we
   218     // already have computed the frequency information and related parameters.
   219     if (simpleFrequency && simpleDetune && !mRecomputeParameters) {
   220       return;
   221     }
   223     if (simpleFrequency) {
   224       frequency = mFrequency.GetValue();
   225     } else {
   226       frequency = mFrequency.GetValueAtTime(ticks, count);
   227     }
   228     if (simpleDetune) {
   229       detune = mDetune.GetValue();
   230     } else {
   231       detune = mDetune.GetValueAtTime(ticks, count);
   232     }
   234     mFinalFrequency = frequency * pow(2., detune / 1200.);
   235     mRecomputeParameters = false;
   237     // When using bipolar BLIT, we divide the signal period by two, because we
   238     // are using two BLIT out of phase.
   239     mSignalPeriod = UsesBipolarBLIT() ? 0.5 * mSource->SampleRate() / mFinalFrequency
   240                                       : mSource->SampleRate() / mFinalFrequency;
   241     // Wrap the phase accordingly:
   242     mPhaseWrap = UsesBipolarBLIT() || mType == OscillatorType::Sine ? 2 * M_PI
   243                                    : M_PI;
   244     // Even number of harmonics for bipolar blit, odd otherwise.
   245     mNumberOfHarmonics = UsesBipolarBLIT() ? 2 * floor(0.5 * mSignalPeriod)
   246                                            : 2 * floor(0.5 * mSignalPeriod) + 1;
   247     mPhaseIncrement = mType == OscillatorType::Sine ? 2 * M_PI / mSignalPeriod
   248                                                     : M_PI / mSignalPeriod;
   249     mAmplitudeAtZero = mNumberOfHarmonics / mSignalPeriod;
   250   }
   252   void FillBounds(float* output, TrackTicks ticks,
   253                   uint32_t& start, uint32_t& end)
   254   {
   255     MOZ_ASSERT(output);
   256     static_assert(TrackTicks(WEBAUDIO_BLOCK_SIZE) < UINT_MAX,
   257         "WEBAUDIO_BLOCK_SIZE overflows interator bounds.");
   258     start = 0;
   259     if (ticks < mStart) {
   260       start = mStart - ticks;
   261       for (uint32_t i = 0; i < start; ++i) {
   262         output[i] = 0.0;
   263       }
   264     }
   265     end = WEBAUDIO_BLOCK_SIZE;
   266     if (ticks + end > mStop) {
   267       end = mStop - ticks;
   268       for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) {
   269         output[i] = 0.0;
   270       }
   271     }
   272   }
   274   float BipolarBLIT()
   275   {
   276     float blit;
   277     float denom = sin(mPhase);
   279     if (fabs(denom) < std::numeric_limits<float>::epsilon()) {
   280       if (mPhase < 0.1f || mPhase > 2 * M_PI - 0.1f) {
   281         blit = mAmplitudeAtZero;
   282       } else {
   283         blit = -mAmplitudeAtZero;
   284       }
   285     } else {
   286       blit = sin(mNumberOfHarmonics * mPhase);
   287       blit /= mSignalPeriod * denom;
   288     }
   289     return blit;
   290   }
   292   float UnipolarBLIT()
   293   {
   294     float blit;
   295     float denom = sin(mPhase);
   297     if (fabs(denom) <= std::numeric_limits<float>::epsilon()) {
   298       blit = mAmplitudeAtZero;
   299     } else {
   300       blit = sin(mNumberOfHarmonics * mPhase);
   301       blit /= mSignalPeriod * denom;
   302     }
   304     return blit;
   305   }
   307   void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
   308   {
   309     for (uint32_t i = aStart; i < aEnd; ++i) {
   310       UpdateParametersIfNeeded(ticks, i);
   312       aOutput[i] = sin(mPhase);
   314       IncrementPhase();
   315     }
   316   }
   318   void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
   319   {
   320     for (uint32_t i = aStart; i < aEnd; ++i) {
   321       UpdateParametersIfNeeded(ticks, i);
   322       // Integration to get us a square. It turns out we can have a
   323       // pure integrator here.
   324       mSquare = mSquare * sLeak + BipolarBLIT();
   325       aOutput[i] = mSquare;
   326       // maybe we want to apply a gain, the wg has not decided yet
   327       aOutput[i] *= 1.5;
   328       IncrementPhase();
   329     }
   330   }
   332   void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
   333   {
   334     float dcoffset;
   335     for (uint32_t i = aStart; i < aEnd; ++i) {
   336       UpdateParametersIfNeeded(ticks, i);
   337       // DC offset so the Saw does not ramp up to infinity when integrating.
   338       dcoffset = mFinalFrequency / mSource->SampleRate();
   339       // Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a
   340       // very low frequency component somewhere here, but I'm not sure where.
   341       mSaw = mSaw * sLeak + (UnipolarBLIT() - dcoffset);
   342       // reverse the saw so we are spec compliant
   343       aOutput[i] = -mSaw * 1.5;
   345       IncrementPhase();
   346     }
   347   }
   349   void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
   350   {
   351     for (uint32_t i = aStart; i < aEnd; ++i) {
   352       UpdateParametersIfNeeded(ticks, i);
   353       // Integrate to get a square
   354       mSquare += BipolarBLIT();
   355       // Leaky integrate to get a triangle. We get too much dc offset if we don't
   356       // leaky integrate here.
   357       // C6 = k0 / period
   358       // (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25
   359       float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency);
   360       mTriangle = mTriangle * sLeakTriangle + mSquare + C6;
   361       // DC Block, and scale back to [-1.0; 1.0]
   362       aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5;
   364       IncrementPhase();
   365     }
   366   }
   368   void ComputeCustom(float* aOutput,
   369                      TrackTicks ticks,
   370                      uint32_t aStart,
   371                      uint32_t aEnd)
   372   {
   373     MOZ_ASSERT(mPeriodicWave, "No custom waveform data");
   375     uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
   376     // Mask to wrap wave data indices into the range [0,periodicWaveSize).
   377     uint32_t indexMask = periodicWaveSize - 1;
   378     MOZ_ASSERT(periodicWaveSize && (periodicWaveSize & indexMask) == 0,
   379                "periodicWaveSize must be power of 2");
   380     float* higherWaveData = nullptr;
   381     float* lowerWaveData = nullptr;
   382     float tableInterpolationFactor;
   383     // Phase increment at frequency of 1 Hz.
   384     // mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI).
   385     float basePhaseIncrement =
   386       static_cast<float>(periodicWaveSize) / mSource->SampleRate();
   388     for (uint32_t i = aStart; i < aEnd; ++i) {
   389       UpdateParametersIfNeeded(ticks, i);
   390       mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
   391                                                      lowerWaveData,
   392                                                      higherWaveData,
   393                                                      tableInterpolationFactor);
   394       // Bilinear interpolation between adjacent samples in each table.
   395       float floorPhase = floorf(mPhase);
   396       uint32_t j1 = floorPhase;
   397       j1 &= indexMask;
   398       uint32_t j2 = j1 + 1;
   399       j2 &= indexMask;
   401       float sampleInterpolationFactor = mPhase - floorPhase;
   403       float lower = (1.0f - sampleInterpolationFactor) * lowerWaveData[j1] +
   404                     sampleInterpolationFactor * lowerWaveData[j2];
   405       float higher = (1.0f - sampleInterpolationFactor) * higherWaveData[j1] +
   406                     sampleInterpolationFactor * higherWaveData[j2];
   407       aOutput[i] = (1.0f - tableInterpolationFactor) * lower +
   408                    tableInterpolationFactor * higher;
   410       // Calculate next phase position from wrapped value j1 to avoid loss of
   411       // precision at large values.
   412       mPhase =
   413         j1 + sampleInterpolationFactor + basePhaseIncrement * mFinalFrequency;
   414     }
   415   }
   417   void ComputeSilence(AudioChunk *aOutput)
   418   {
   419     aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
   420   }
   422   virtual void ProcessBlock(AudioNodeStream* aStream,
   423                             const AudioChunk& aInput,
   424                             AudioChunk* aOutput,
   425                             bool* aFinished) MOZ_OVERRIDE
   426   {
   427     MOZ_ASSERT(mSource == aStream, "Invalid source stream");
   429     TrackTicks ticks = aStream->GetCurrentPosition();
   430     if (mStart == -1) {
   431       ComputeSilence(aOutput);
   432       return;
   433     }
   435     if (ticks >= mStop) {
   436       // We've finished playing.
   437       ComputeSilence(aOutput);
   438       *aFinished = true;
   439       return;
   440     }
   441     if (ticks + WEBAUDIO_BLOCK_SIZE < mStart) {
   442       // We're not playing yet.
   443       ComputeSilence(aOutput);
   444       return;
   445     }
   447     AllocateAudioBlock(1, aOutput);
   448     float* output = static_cast<float*>(
   449         const_cast<void*>(aOutput->mChannelData[0]));
   451     uint32_t start, end;
   452     FillBounds(output, ticks, start, end);
   454     // Synthesize the correct waveform.
   455     switch(mType) {
   456       case OscillatorType::Sine:
   457         ComputeSine(output, ticks, start, end);
   458         break;
   459       case OscillatorType::Square:
   460         ComputeSquare(output, ticks, start, end);
   461         break;
   462       case OscillatorType::Triangle:
   463         ComputeTriangle(output, ticks, start, end);
   464         break;
   465       case OscillatorType::Sawtooth:
   466         ComputeSawtooth(output, ticks, start, end);
   467         break;
   468       case OscillatorType::Custom:
   469         ComputeCustom(output, ticks, start, end);
   470         break;
   471       default:
   472         ComputeSilence(aOutput);
   473     };
   475   }
   477   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   478   {
   479     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
   481     // Not owned:
   482     // - mSource
   483     // - mDestination
   484     // - mFrequency (internal ref owned by node)
   485     // - mDetune (internal ref owned by node)
   487     if (mCustom) {
   488       amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
   489     }
   491     if (mPeriodicWave) {
   492       amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
   493     }
   495     return amount;
   496   }
   498   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   499   {
   500     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   501   }
   503   DCBlocker mDCBlocker;
   504   AudioNodeStream* mSource;
   505   AudioNodeStream* mDestination;
   506   TrackTicks mStart;
   507   TrackTicks mStop;
   508   AudioParamTimeline mFrequency;
   509   AudioParamTimeline mDetune;
   510   OscillatorType mType;
   511   float mPhase;
   512   float mFinalFrequency;
   513   uint32_t mNumberOfHarmonics;
   514   float mSignalPeriod;
   515   float mAmplitudeAtZero;
   516   float mPhaseIncrement;
   517   float mSquare;
   518   float mTriangle;
   519   float mSaw;
   520   float mPhaseWrap;
   521   bool mRecomputeParameters;
   522   nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom;
   523   uint32_t mCustomLength;
   524   nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave;
   525 };
   527 OscillatorNode::OscillatorNode(AudioContext* aContext)
   528   : AudioNode(aContext,
   529               2,
   530               ChannelCountMode::Max,
   531               ChannelInterpretation::Speakers)
   532   , mType(OscillatorType::Sine)
   533   , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
   534                SendFrequencyToStream, 440.0f))
   535   , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
   536             SendDetuneToStream, 0.0f))
   537   , mStartCalled(false)
   538   , mStopped(false)
   539 {
   540   OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
   541   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
   542   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
   543   mStream->AddMainThreadListener(this);
   544 }
   546 OscillatorNode::~OscillatorNode()
   547 {
   548 }
   550 size_t
   551 OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   552 {
   553   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   555   // For now only report if we know for sure that it's not shared.
   556   amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf);
   557   amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
   558   amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
   559   return amount;
   560 }
   562 size_t
   563 OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   564 {
   565   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   566 }
   568 JSObject*
   569 OscillatorNode::WrapObject(JSContext* aCx)
   570 {
   571   return OscillatorNodeBinding::Wrap(aCx, this);
   572 }
   574 void
   575 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
   576 {
   577   OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
   578   SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
   579 }
   581 void
   582 OscillatorNode::SendDetuneToStream(AudioNode* aNode)
   583 {
   584   OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
   585   SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
   586 }
   588 void
   589 OscillatorNode::SendTypeToStream()
   590 {
   591   if (mType == OscillatorType::Custom) {
   592     // The engine assumes we'll send the custom data before updating the type.
   593     SendPeriodicWaveToStream();
   594   }
   595   SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
   596 }
   598 void OscillatorNode::SendPeriodicWaveToStream()
   599 {
   600   NS_ASSERTION(mType == OscillatorType::Custom,
   601                "Sending custom waveform to engine thread with non-custom type");
   602   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   603   MOZ_ASSERT(ns, "Missing node stream.");
   604   MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
   605   SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
   606                              mPeriodicWave->DataLength());
   607   nsRefPtr<ThreadSharedFloatArrayBufferList> data =
   608     mPeriodicWave->GetThreadSharedBuffer();
   609   ns->SetBuffer(data.forget());
   610 }
   612 void
   613 OscillatorNode::Start(double aWhen, ErrorResult& aRv)
   614 {
   615   if (!WebAudioUtils::IsTimeValid(aWhen)) {
   616     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   617     return;
   618   }
   620   if (mStartCalled) {
   621     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   622     return;
   623   }
   624   mStartCalled = true;
   626   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   627   if (!ns) {
   628     // Nothing to play, or we're already dead for some reason
   629     return;
   630   }
   632   // TODO: Perhaps we need to do more here.
   633   ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
   634                              Context(), aWhen);
   636   MarkActive();
   637 }
   639 void
   640 OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
   641 {
   642   if (!WebAudioUtils::IsTimeValid(aWhen)) {
   643     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   644     return;
   645   }
   647   if (!mStartCalled) {
   648     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   649     return;
   650   }
   652   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   653   if (!ns || !Context()) {
   654     // We've already stopped and had our stream shut down
   655     return;
   656   }
   658   // TODO: Perhaps we need to do more here.
   659   ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
   660                              Context(), std::max(0.0, aWhen));
   661 }
   663 void
   664 OscillatorNode::NotifyMainThreadStateChanged()
   665 {
   666   if (mStream->IsFinished()) {
   667     class EndedEventDispatcher : public nsRunnable
   668     {
   669     public:
   670       explicit EndedEventDispatcher(OscillatorNode* aNode)
   671         : mNode(aNode) {}
   672       NS_IMETHODIMP Run()
   673       {
   674         // If it's not safe to run scripts right now, schedule this to run later
   675         if (!nsContentUtils::IsSafeToRunScript()) {
   676           nsContentUtils::AddScriptRunner(this);
   677           return NS_OK;
   678         }
   680         mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
   681         return NS_OK;
   682       }
   683     private:
   684       nsRefPtr<OscillatorNode> mNode;
   685     };
   686     if (!mStopped) {
   687       // Only dispatch the ended event once
   688       NS_DispatchToMainThread(new EndedEventDispatcher(this));
   689       mStopped = true;
   690     }
   692     // Drop the playing reference
   693     // Warning: The below line might delete this.
   694     MarkInactive();
   695   }
   696 }
   698 }
   699 }

mercurial