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 "OscillatorNode.h"
8 #include "AudioNodeEngine.h"
9 #include "AudioNodeStream.h"
10 #include "AudioDestinationNode.h"
11 #include "nsContentUtils.h"
12 #include "WebAudioUtils.h"
13 #include "blink/PeriodicWave.h"
14 
15 namespace mozilla {
16 namespace dom {
17 
18 NS_IMPL_CYCLE_COLLECTION_INHERITED(OscillatorNode, AudioScheduledSourceNode,
19                                    mPeriodicWave, mFrequency, mDetune)
20 
21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OscillatorNode)
22 NS_INTERFACE_MAP_END_INHERITING(AudioScheduledSourceNode)
23 
24 NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioScheduledSourceNode)
25 NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioScheduledSourceNode)
26 
27 class OscillatorNodeEngine final : public AudioNodeEngine {
28  public:
OscillatorNodeEngine(AudioNode * aNode,AudioDestinationNode * aDestination)29   OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
30       : AudioNodeEngine(aNode),
31         mSource(nullptr),
32         mDestination(aDestination->Stream()),
33         mStart(-1),
34         mStop(STREAM_TIME_MAX)
35         // Keep the default values in sync with OscillatorNode::OscillatorNode.
36         ,
37         mFrequency(440.f),
38         mDetune(0.f),
39         mType(OscillatorType::Sine),
40         mPhase(0.),
41         mFinalFrequency(0.),
42         mPhaseIncrement(0.),
43         mRecomputeParameters(true),
44         mCustomDisableNormalization(false) {
45     MOZ_ASSERT(NS_IsMainThread());
46     mBasicWaveFormCache = aDestination->Context()->GetBasicWaveFormCache();
47   }
48 
SetSourceStream(AudioNodeStream * aSource)49   void SetSourceStream(AudioNodeStream* aSource) { mSource = aSource; }
50 
51   enum Parameters {
52     FREQUENCY,
53     DETUNE,
54     TYPE,
55     DISABLE_NORMALIZATION,
56     START,
57     STOP,
58   };
RecvTimelineEvent(uint32_t aIndex,AudioTimelineEvent & aEvent)59   void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
60     mRecomputeParameters = true;
61 
62     MOZ_ASSERT(mDestination);
63 
64     WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);
65 
66     switch (aIndex) {
67       case FREQUENCY:
68         mFrequency.InsertEvent<int64_t>(aEvent);
69         break;
70       case DETUNE:
71         mDetune.InsertEvent<int64_t>(aEvent);
72         break;
73       default:
74         NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
75     }
76   }
77 
SetStreamTimeParameter(uint32_t aIndex,StreamTime aParam)78   void SetStreamTimeParameter(uint32_t aIndex, StreamTime aParam) override {
79     switch (aIndex) {
80       case START:
81         mStart = aParam;
82         mSource->SetActive();
83         break;
84       case STOP:
85         mStop = aParam;
86         break;
87       default:
88         NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
89     }
90   }
91 
SetInt32Parameter(uint32_t aIndex,int32_t aParam)92   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override {
93     switch (aIndex) {
94       case TYPE:
95         // Set the new type.
96         mType = static_cast<OscillatorType>(aParam);
97         if (mType == OscillatorType::Sine) {
98           // Forget any previous custom data.
99           mCustomDisableNormalization = false;
100           mPeriodicWave = nullptr;
101           mRecomputeParameters = true;
102         }
103         switch (mType) {
104           case OscillatorType::Sine:
105             mPhase = 0.0;
106             break;
107           case OscillatorType::Square:
108           case OscillatorType::Triangle:
109           case OscillatorType::Sawtooth:
110             mPeriodicWave = mBasicWaveFormCache->GetBasicWaveForm(mType);
111             break;
112           case OscillatorType::Custom:
113             break;
114           default:
115             NS_ERROR("Bad OscillatorNodeEngine type parameter.");
116         }
117         // End type switch.
118         break;
119       case DISABLE_NORMALIZATION:
120         MOZ_ASSERT(aParam >= 0, "negative custom array length");
121         mCustomDisableNormalization = static_cast<uint32_t>(aParam);
122         break;
123       default:
124         NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
125     }
126     // End index switch.
127   }
128 
SetBuffer(AudioChunk && aBuffer)129   void SetBuffer(AudioChunk&& aBuffer) override {
130     MOZ_ASSERT(aBuffer.ChannelCount() == 2,
131                "PeriodicWave should have sent two channels");
132     MOZ_ASSERT(aBuffer.mVolume == 1.0f);
133     mPeriodicWave = WebCore::PeriodicWave::create(
134         mSource->SampleRate(), aBuffer.ChannelData<float>()[0],
135         aBuffer.ChannelData<float>()[1], aBuffer.mDuration,
136         mCustomDisableNormalization);
137   }
138 
IncrementPhase()139   void IncrementPhase() {
140     const float twoPiFloat = float(2 * M_PI);
141     mPhase += mPhaseIncrement;
142     if (mPhase > twoPiFloat) {
143       mPhase -= twoPiFloat;
144     } else if (mPhase < -twoPiFloat) {
145       mPhase += twoPiFloat;
146     }
147   }
148 
149   // Returns true if the final frequency (and thus the phase increment) changed,
150   // false otherwise. This allow some optimizations at callsite.
UpdateParametersIfNeeded(StreamTime ticks,size_t count)151   bool UpdateParametersIfNeeded(StreamTime ticks, size_t count) {
152     double frequency, detune;
153 
154     // Shortcut if frequency-related AudioParam are not automated, and we
155     // already have computed the frequency information and related parameters.
156     if (!ParametersMayNeedUpdate()) {
157       return false;
158     }
159 
160     bool simpleFrequency = mFrequency.HasSimpleValue();
161     bool simpleDetune = mDetune.HasSimpleValue();
162 
163     if (simpleFrequency) {
164       frequency = mFrequency.GetValue();
165     } else {
166       frequency = mFrequency.GetValueAtTime(ticks, count);
167     }
168     if (simpleDetune) {
169       detune = mDetune.GetValue();
170     } else {
171       detune = mDetune.GetValueAtTime(ticks, count);
172     }
173 
174     float finalFrequency = frequency * pow(2., detune / 1200.);
175     float signalPeriod = mSource->SampleRate() / finalFrequency;
176     mRecomputeParameters = false;
177 
178     mPhaseIncrement = 2 * M_PI / signalPeriod;
179 
180     if (finalFrequency != mFinalFrequency) {
181       mFinalFrequency = finalFrequency;
182       return true;
183     }
184     return false;
185   }
186 
FillBounds(float * output,StreamTime ticks,uint32_t & start,uint32_t & end)187   void FillBounds(float* output, StreamTime ticks, uint32_t& start,
188                   uint32_t& end) {
189     MOZ_ASSERT(output);
190     static_assert(StreamTime(WEBAUDIO_BLOCK_SIZE) < UINT_MAX,
191                   "WEBAUDIO_BLOCK_SIZE overflows interator bounds.");
192     start = 0;
193     if (ticks < mStart) {
194       start = mStart - ticks;
195       for (uint32_t i = 0; i < start; ++i) {
196         output[i] = 0.0;
197       }
198     }
199     end = WEBAUDIO_BLOCK_SIZE;
200     if (ticks + end > mStop) {
201       end = mStop - ticks;
202       for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) {
203         output[i] = 0.0;
204       }
205     }
206   }
207 
ComputeSine(float * aOutput,StreamTime ticks,uint32_t aStart,uint32_t aEnd)208   void ComputeSine(float* aOutput, StreamTime ticks, uint32_t aStart,
209                    uint32_t aEnd) {
210     for (uint32_t i = aStart; i < aEnd; ++i) {
211       // We ignore the return value, changing the frequency has no impact on
212       // performances here.
213       UpdateParametersIfNeeded(ticks, i);
214 
215       aOutput[i] = sin(mPhase);
216 
217       IncrementPhase();
218     }
219   }
220 
ParametersMayNeedUpdate()221   bool ParametersMayNeedUpdate() {
222     return !mDetune.HasSimpleValue() || !mFrequency.HasSimpleValue() ||
223            mRecomputeParameters;
224   }
225 
ComputeCustom(float * aOutput,StreamTime ticks,uint32_t aStart,uint32_t aEnd)226   void ComputeCustom(float* aOutput, StreamTime ticks, uint32_t aStart,
227                      uint32_t aEnd) {
228     MOZ_ASSERT(mPeriodicWave, "No custom waveform data");
229 
230     uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
231     // Mask to wrap wave data indices into the range [0,periodicWaveSize).
232     uint32_t indexMask = periodicWaveSize - 1;
233     MOZ_ASSERT(periodicWaveSize && (periodicWaveSize & indexMask) == 0,
234                "periodicWaveSize must be power of 2");
235     float* higherWaveData = nullptr;
236     float* lowerWaveData = nullptr;
237     float tableInterpolationFactor;
238     // Phase increment at frequency of 1 Hz.
239     // mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI).
240     float basePhaseIncrement = mPeriodicWave->rateScale();
241 
242     bool needToFetchWaveData = UpdateParametersIfNeeded(ticks, aStart);
243 
244     bool parametersMayNeedUpdate = ParametersMayNeedUpdate();
245     mPeriodicWave->waveDataForFundamentalFrequency(
246         mFinalFrequency, lowerWaveData, higherWaveData,
247         tableInterpolationFactor);
248 
249     for (uint32_t i = aStart; i < aEnd; ++i) {
250       if (parametersMayNeedUpdate) {
251         if (needToFetchWaveData) {
252           mPeriodicWave->waveDataForFundamentalFrequency(
253               mFinalFrequency, lowerWaveData, higherWaveData,
254               tableInterpolationFactor);
255         }
256         needToFetchWaveData = UpdateParametersIfNeeded(ticks, i);
257       }
258       // Bilinear interpolation between adjacent samples in each table.
259       float floorPhase = floorf(mPhase);
260       int j1Signed = static_cast<int>(floorPhase);
261       uint32_t j1 = j1Signed & indexMask;
262       uint32_t j2 = j1 + 1;
263       j2 &= indexMask;
264 
265       float sampleInterpolationFactor = mPhase - floorPhase;
266 
267       float lower = (1.0f - sampleInterpolationFactor) * lowerWaveData[j1] +
268                     sampleInterpolationFactor * lowerWaveData[j2];
269       float higher = (1.0f - sampleInterpolationFactor) * higherWaveData[j1] +
270                      sampleInterpolationFactor * higherWaveData[j2];
271       aOutput[i] = (1.0f - tableInterpolationFactor) * lower +
272                    tableInterpolationFactor * higher;
273 
274       // Calculate next phase position from wrapped value j1 to avoid loss of
275       // precision at large values.
276       mPhase =
277           j1 + sampleInterpolationFactor + basePhaseIncrement * mFinalFrequency;
278     }
279   }
280 
ComputeSilence(AudioBlock * aOutput)281   void ComputeSilence(AudioBlock* aOutput) {
282     aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
283   }
284 
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)285   void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom,
286                     const AudioBlock& aInput, AudioBlock* aOutput,
287                     bool* aFinished) override {
288     MOZ_ASSERT(mSource == aStream, "Invalid source stream");
289 
290     StreamTime ticks = mDestination->GraphTimeToStreamTime(aFrom);
291     if (mStart == -1) {
292       ComputeSilence(aOutput);
293       return;
294     }
295 
296     if (ticks + WEBAUDIO_BLOCK_SIZE <= mStart || ticks >= mStop) {
297       ComputeSilence(aOutput);
298 
299     } else {
300       aOutput->AllocateChannels(1);
301       float* output = aOutput->ChannelFloatsForWrite(0);
302 
303       uint32_t start, end;
304       FillBounds(output, ticks, start, end);
305 
306       // Synthesize the correct waveform.
307       switch (mType) {
308         case OscillatorType::Sine:
309           ComputeSine(output, ticks, start, end);
310           break;
311         case OscillatorType::Square:
312         case OscillatorType::Triangle:
313         case OscillatorType::Sawtooth:
314         case OscillatorType::Custom:
315           ComputeCustom(output, ticks, start, end);
316           break;
317         default:
318           ComputeSilence(aOutput);
319       };
320     }
321 
322     if (ticks + WEBAUDIO_BLOCK_SIZE >= mStop) {
323       // We've finished playing.
324       *aFinished = true;
325     }
326   }
327 
IsActive() const328   bool IsActive() const override {
329     // start() has been called.
330     return mStart != -1;
331   }
332 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const333   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
334     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
335 
336     // Not owned:
337     // - mSource
338     // - mDestination
339     // - mFrequency (internal ref owned by node)
340     // - mDetune (internal ref owned by node)
341 
342     if (mPeriodicWave) {
343       amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
344     }
345 
346     return amount;
347   }
348 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const349   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
350     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
351   }
352 
353   // mSource deletes this engine in its destructor
354   AudioNodeStream* MOZ_NON_OWNING_REF mSource;
355   RefPtr<AudioNodeStream> mDestination;
356   StreamTime mStart;
357   StreamTime mStop;
358   AudioParamTimeline mFrequency;
359   AudioParamTimeline mDetune;
360   OscillatorType mType;
361   float mPhase;
362   float mFinalFrequency;
363   float mPhaseIncrement;
364   bool mRecomputeParameters;
365   RefPtr<BasicWaveFormCache> mBasicWaveFormCache;
366   bool mCustomDisableNormalization;
367   RefPtr<WebCore::PeriodicWave> mPeriodicWave;
368 };
369 
OscillatorNode(AudioContext * aContext)370 OscillatorNode::OscillatorNode(AudioContext* aContext)
371     : AudioScheduledSourceNode(aContext, 2, ChannelCountMode::Max,
372                                ChannelInterpretation::Speakers),
373       mType(OscillatorType::Sine),
374       mFrequency(new AudioParam(
375           this, OscillatorNodeEngine::FREQUENCY, "frequency", 440.0f,
376           -(aContext->SampleRate() / 2), aContext->SampleRate() / 2)),
377       mDetune(
378           new AudioParam(this, OscillatorNodeEngine::DETUNE, "detune", 0.0f)),
379       mStartCalled(false) {
380   OscillatorNodeEngine* engine =
381       new OscillatorNodeEngine(this, aContext->Destination());
382   mStream = AudioNodeStream::Create(aContext, engine,
383                                     AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
384                                     aContext->Graph());
385   engine->SetSourceStream(mStream);
386   mStream->AddMainThreadListener(this);
387 }
388 
Create(AudioContext & aAudioContext,const OscillatorOptions & aOptions,ErrorResult & aRv)389 /* static */ already_AddRefed<OscillatorNode> OscillatorNode::Create(
390     AudioContext& aAudioContext, const OscillatorOptions& aOptions,
391     ErrorResult& aRv) {
392   if (aAudioContext.CheckClosed(aRv)) {
393     return nullptr;
394   }
395 
396   RefPtr<OscillatorNode> audioNode = new OscillatorNode(&aAudioContext);
397 
398   audioNode->Initialize(aOptions, aRv);
399   if (NS_WARN_IF(aRv.Failed())) {
400     return nullptr;
401   }
402 
403   audioNode->SetType(aOptions.mType, aRv);
404   if (NS_WARN_IF(aRv.Failed())) {
405     return nullptr;
406   }
407 
408   audioNode->Frequency()->SetValue(aOptions.mFrequency);
409   audioNode->Detune()->SetValue(aOptions.mDetune);
410 
411   if (aOptions.mPeriodicWave.WasPassed()) {
412     audioNode->SetPeriodicWave(aOptions.mPeriodicWave.Value());
413   }
414 
415   return audioNode.forget();
416 }
417 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const418 size_t OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
419   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
420 
421   // For now only report if we know for sure that it's not shared.
422   if (mPeriodicWave) {
423     amount += mPeriodicWave->SizeOfIncludingThisIfNotShared(aMallocSizeOf);
424   }
425   amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
426   amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
427   return amount;
428 }
429 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const430 size_t OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
431   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
432 }
433 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)434 JSObject* OscillatorNode::WrapObject(JSContext* aCx,
435                                      JS::Handle<JSObject*> aGivenProto) {
436   return OscillatorNodeBinding::Wrap(aCx, this, aGivenProto);
437 }
438 
DestroyMediaStream()439 void OscillatorNode::DestroyMediaStream() {
440   if (mStream) {
441     mStream->RemoveMainThreadListener(this);
442   }
443   AudioNode::DestroyMediaStream();
444 }
445 
SendTypeToStream()446 void OscillatorNode::SendTypeToStream() {
447   if (!mStream) {
448     return;
449   }
450   if (mType == OscillatorType::Custom) {
451     // The engine assumes we'll send the custom data before updating the type.
452     SendPeriodicWaveToStream();
453   }
454   SendInt32ParameterToStream(OscillatorNodeEngine::TYPE,
455                              static_cast<int32_t>(mType));
456 }
457 
SendPeriodicWaveToStream()458 void OscillatorNode::SendPeriodicWaveToStream() {
459   NS_ASSERTION(mType == OscillatorType::Custom,
460                "Sending custom waveform to engine thread with non-custom type");
461   MOZ_ASSERT(mStream, "Missing node stream.");
462   MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
463   SendInt32ParameterToStream(OscillatorNodeEngine::DISABLE_NORMALIZATION,
464                              mPeriodicWave->DisableNormalization());
465   AudioChunk data = mPeriodicWave->GetThreadSharedBuffer();
466   mStream->SetBuffer(Move(data));
467 }
468 
Start(double aWhen,ErrorResult & aRv)469 void OscillatorNode::Start(double aWhen, ErrorResult& aRv) {
470   if (!WebAudioUtils::IsTimeValid(aWhen)) {
471     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
472     return;
473   }
474 
475   if (mStartCalled) {
476     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
477     return;
478   }
479   mStartCalled = true;
480 
481   if (!mStream) {
482     // Nothing to play, or we're already dead for some reason
483     return;
484   }
485 
486   // TODO: Perhaps we need to do more here.
487   mStream->SetStreamTimeParameter(OscillatorNodeEngine::START, Context(),
488                                   aWhen);
489 
490   MarkActive();
491 }
492 
Stop(double aWhen,ErrorResult & aRv)493 void OscillatorNode::Stop(double aWhen, ErrorResult& aRv) {
494   if (!WebAudioUtils::IsTimeValid(aWhen)) {
495     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
496     return;
497   }
498 
499   if (!mStartCalled) {
500     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
501     return;
502   }
503 
504   if (!mStream || !Context()) {
505     // We've already stopped and had our stream shut down
506     return;
507   }
508 
509   // TODO: Perhaps we need to do more here.
510   mStream->SetStreamTimeParameter(OscillatorNodeEngine::STOP, Context(),
511                                   std::max(0.0, aWhen));
512 }
513 
NotifyMainThreadStreamFinished()514 void OscillatorNode::NotifyMainThreadStreamFinished() {
515   MOZ_ASSERT(mStream->IsFinished());
516 
517   class EndedEventDispatcher final : public Runnable {
518    public:
519     explicit EndedEventDispatcher(OscillatorNode* aNode)
520         : mozilla::Runnable("EndedEventDispatcher"), mNode(aNode) {}
521     NS_IMETHOD Run() override {
522       // If it's not safe to run scripts right now, schedule this to run later
523       if (!nsContentUtils::IsSafeToRunScript()) {
524         nsContentUtils::AddScriptRunner(this);
525         return NS_OK;
526       }
527 
528       mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
529       // Release stream resources.
530       mNode->DestroyMediaStream();
531       return NS_OK;
532     }
533 
534    private:
535     RefPtr<OscillatorNode> mNode;
536   };
537 
538   Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this)));
539 
540   // Drop the playing reference
541   // Warning: The below line might delete this.
542   MarkInactive();
543 }
544 
545 }  // namespace dom
546 }  // namespace mozilla
547