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 #ifndef AudioContext_h_
8 #define AudioContext_h_
9 
10 #include "mozilla/dom/OfflineAudioContextBinding.h"
11 #include "MediaBufferDecoder.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/DOMEventTargetHelper.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/dom/TypedArray.h"
16 #include "mozilla/RelativeTimeline.h"
17 #include "mozilla/UniquePtr.h"
18 #include "nsCOMPtr.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsHashKeys.h"
21 #include "nsTHashtable.h"
22 #include "js/TypeDecls.h"
23 #include "nsIMemoryReporter.h"
24 
25 // X11 has a #define for CurrentTime. Unbelievable :-(.
26 // See dom/media/DOMMediaStream.h for more fun!
27 #ifdef CurrentTime
28 #undef CurrentTime
29 #endif
30 
31 namespace WebCore {
32 class PeriodicWave;
33 }  // namespace WebCore
34 
35 class nsPIDOMWindowInner;
36 
37 namespace mozilla {
38 
39 class DOMMediaStream;
40 class ErrorResult;
41 class MediaStream;
42 class MediaStreamGraph;
43 class AudioNodeStream;
44 
45 namespace dom {
46 
47 enum class AudioContextState : uint8_t;
48 class AnalyserNode;
49 class AudioBuffer;
50 class AudioBufferSourceNode;
51 class AudioDestinationNode;
52 class AudioListener;
53 class AudioNode;
54 class BiquadFilterNode;
55 class ChannelMergerNode;
56 class ChannelSplitterNode;
57 class ConstantSourceNode;
58 class ConvolverNode;
59 class DelayNode;
60 class DynamicsCompressorNode;
61 class GainNode;
62 class GlobalObject;
63 class HTMLMediaElement;
64 class IIRFilterNode;
65 class MediaElementAudioSourceNode;
66 class MediaStreamAudioDestinationNode;
67 class MediaStreamAudioSourceNode;
68 class OscillatorNode;
69 class PannerNode;
70 class ScriptProcessorNode;
71 class StereoPannerNode;
72 class WaveShaperNode;
73 class PeriodicWave;
74 struct PeriodicWaveConstraints;
75 class Promise;
76 enum class OscillatorType : uint8_t;
77 
78 // This is addrefed by the OscillatorNodeEngine on the main thread
79 // and then used from the MSG thread.
80 // It can be released either from the graph thread or the main thread.
81 class BasicWaveFormCache {
82  public:
83   explicit BasicWaveFormCache(uint32_t aSampleRate);
84   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BasicWaveFormCache)
85   WebCore::PeriodicWave* GetBasicWaveForm(OscillatorType aType);
86 
87  private:
88   ~BasicWaveFormCache();
89   RefPtr<WebCore::PeriodicWave> mSawtooth;
90   RefPtr<WebCore::PeriodicWave> mSquare;
91   RefPtr<WebCore::PeriodicWave> mTriangle;
92   uint32_t mSampleRate;
93 };
94 
95 /* This runnable allows the MSG to notify the main thread when audio is actually
96  * flowing */
97 class StateChangeTask final : public Runnable {
98  public:
99   /* This constructor should be used when this event is sent from the main
100    * thread. */
101   StateChangeTask(AudioContext* aAudioContext, void* aPromise,
102                   AudioContextState aNewState);
103 
104   /* This constructor should be used when this event is sent from the audio
105    * thread. */
106   StateChangeTask(AudioNodeStream* aStream, void* aPromise,
107                   AudioContextState aNewState);
108 
109   NS_IMETHOD Run() override;
110 
111  private:
112   RefPtr<AudioContext> mAudioContext;
113   void* mPromise;
114   RefPtr<AudioNodeStream> mAudioNodeStream;
115   AudioContextState mNewState;
116 };
117 
118 enum class AudioContextOperation { Suspend, Resume, Close };
119 
120 class AudioContext final : public DOMEventTargetHelper,
121                            public nsIMemoryReporter,
122                            public RelativeTimeline {
123   AudioContext(nsPIDOMWindowInner* aParentWindow, bool aIsOffline,
124                uint32_t aNumberOfChannels = 0, uint32_t aLength = 0,
125                float aSampleRate = 0.0f);
126   ~AudioContext();
127 
128   nsresult Init();
129 
130  public:
131   typedef uint64_t AudioContextId;
132 
133   NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioContext,DOMEventTargetHelper)134   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioContext, DOMEventTargetHelper)
135   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
136 
137   nsPIDOMWindowInner* GetParentObject() const { return GetOwner(); }
138 
139   virtual void DisconnectFromOwner() override;
140 
141   void Shutdown();  // idempotent
142 
143   JSObject* WrapObject(JSContext* aCx,
144                        JS::Handle<JSObject*> aGivenProto) override;
145 
146   using DOMEventTargetHelper::DispatchTrustedEvent;
147 
148   // Constructor for regular AudioContext
149   static already_AddRefed<AudioContext> Constructor(const GlobalObject& aGlobal,
150                                                     ErrorResult& aRv);
151 
152   // Constructor for offline AudioContext with options object
153   static already_AddRefed<AudioContext> Constructor(
154       const GlobalObject& aGlobal, const OfflineAudioContextOptions& aOptions,
155       ErrorResult& aRv);
156 
157   // Constructor for offline AudioContext
158   static already_AddRefed<AudioContext> Constructor(const GlobalObject& aGlobal,
159                                                     uint32_t aNumberOfChannels,
160                                                     uint32_t aLength,
161                                                     float aSampleRate,
162                                                     ErrorResult& aRv);
163 
164   // AudioContext methods
165 
Destination()166   AudioDestinationNode* Destination() const { return mDestination; }
167 
SampleRate()168   float SampleRate() const { return mSampleRate; }
169 
ShouldSuspendNewStream()170   bool ShouldSuspendNewStream() const { return mSuspendCalled; }
171 
172   double CurrentTime();
173 
174   AudioListener* Listener();
175 
State()176   AudioContextState State() const { return mAudioContextState; }
177   bool IsRunning() const;
178 
179   // Those three methods return a promise to content, that is resolved when an
180   // (possibly long) operation is completed on the MSG (and possibly other)
181   // thread(s). To avoid having to match the calls and asychronous result when
182   // the operation is completed, we keep a reference to the promises on the main
183   // thread, and then send the promises pointers down the MSG thread, as a void*
184   // (to make it very clear that the pointer is to merely be treated as an ID).
185   // When back on the main thread, we can resolve or reject the promise, by
186   // casting it back to a `Promise*` while asserting we're back on the main
187   // thread and removing the reference we added.
188   already_AddRefed<Promise> Suspend(ErrorResult& aRv);
189   already_AddRefed<Promise> Resume(ErrorResult& aRv);
190   already_AddRefed<Promise> Close(ErrorResult& aRv);
191   IMPL_EVENT_HANDLER(statechange)
192 
193   already_AddRefed<AudioBufferSourceNode> CreateBufferSource(ErrorResult& aRv);
194 
195   already_AddRefed<ConstantSourceNode> CreateConstantSource(ErrorResult& aRv);
196 
197   already_AddRefed<AudioBuffer> CreateBuffer(uint32_t aNumberOfChannels,
198                                              uint32_t aLength,
199                                              float aSampleRate,
200                                              ErrorResult& aRv);
201 
202   already_AddRefed<MediaStreamAudioDestinationNode>
203   CreateMediaStreamDestination(ErrorResult& aRv);
204 
205   already_AddRefed<ScriptProcessorNode> CreateScriptProcessor(
206       uint32_t aBufferSize, uint32_t aNumberOfInputChannels,
207       uint32_t aNumberOfOutputChannels, ErrorResult& aRv);
208 
209   already_AddRefed<StereoPannerNode> CreateStereoPanner(ErrorResult& aRv);
210 
211   already_AddRefed<AnalyserNode> CreateAnalyser(ErrorResult& aRv);
212 
213   already_AddRefed<GainNode> CreateGain(ErrorResult& aRv);
214 
215   already_AddRefed<WaveShaperNode> CreateWaveShaper(ErrorResult& aRv);
216 
217   already_AddRefed<MediaElementAudioSourceNode> CreateMediaElementSource(
218       HTMLMediaElement& aMediaElement, ErrorResult& aRv);
219   already_AddRefed<MediaStreamAudioSourceNode> CreateMediaStreamSource(
220       DOMMediaStream& aMediaStream, ErrorResult& aRv);
221 
222   already_AddRefed<DelayNode> CreateDelay(double aMaxDelayTime,
223                                           ErrorResult& aRv);
224 
225   already_AddRefed<PannerNode> CreatePanner(ErrorResult& aRv);
226 
227   already_AddRefed<ConvolverNode> CreateConvolver(ErrorResult& aRv);
228 
229   already_AddRefed<ChannelSplitterNode> CreateChannelSplitter(
230       uint32_t aNumberOfOutputs, ErrorResult& aRv);
231 
232   already_AddRefed<ChannelMergerNode> CreateChannelMerger(
233       uint32_t aNumberOfInputs, ErrorResult& aRv);
234 
235   already_AddRefed<DynamicsCompressorNode> CreateDynamicsCompressor(
236       ErrorResult& aRv);
237 
238   already_AddRefed<BiquadFilterNode> CreateBiquadFilter(ErrorResult& aRv);
239 
240   already_AddRefed<IIRFilterNode> CreateIIRFilter(
241       const Sequence<double>& aFeedforward, const Sequence<double>& aFeedback,
242       mozilla::ErrorResult& aRv);
243 
244   already_AddRefed<OscillatorNode> CreateOscillator(ErrorResult& aRv);
245 
246   already_AddRefed<PeriodicWave> CreatePeriodicWave(
247       const Float32Array& aRealData, const Float32Array& aImagData,
248       const PeriodicWaveConstraints& aConstraints, ErrorResult& aRv);
249 
250   already_AddRefed<Promise> DecodeAudioData(
251       const ArrayBuffer& aBuffer,
252       const Optional<OwningNonNull<DecodeSuccessCallback>>& aSuccessCallback,
253       const Optional<OwningNonNull<DecodeErrorCallback>>& aFailureCallback,
254       ErrorResult& aRv);
255 
256   // OfflineAudioContext methods
257   already_AddRefed<Promise> StartRendering(ErrorResult& aRv);
258   IMPL_EVENT_HANDLER(complete)
259   unsigned long Length();
260 
IsOffline()261   bool IsOffline() const { return mIsOffline; }
262 
263   MediaStreamGraph* Graph() const;
264   MediaStream* DestinationStream() const;
265 
266   // Nodes register here if they will produce sound even if they have silent
267   // or no input connections.  The AudioContext will keep registered nodes
268   // alive until the context is collected.  This takes care of "playing"
269   // references and "tail-time" references.
270   void RegisterActiveNode(AudioNode* aNode);
271   // Nodes unregister when they have finished producing sound for the
272   // foreseeable future.
273   // Do NOT call UnregisterActiveNode from an AudioNode destructor.
274   // If the destructor is called, then the Node has already been unregistered.
275   // The destructor may be called during hashtable enumeration, during which
276   // unregistering would not be safe.
277   void UnregisterActiveNode(AudioNode* aNode);
278 
279   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
280   void UnregisterPannerNode(PannerNode* aNode);
281   void UpdatePannerSource();
282 
283   uint32_t MaxChannelCount() const;
284 
285   uint32_t ActiveNodeCount() const;
286 
287   void Mute() const;
288   void Unmute() const;
289 
290   JSObject* GetGlobalJSObject() const;
291 
292   void RegisterNode(AudioNode* aNode);
293   void UnregisterNode(AudioNode* aNode);
294 
295   void OnStateChanged(void* aPromise, AudioContextState aNewState);
296 
297   BasicWaveFormCache* GetBasicWaveFormCache();
298 
299   bool CheckClosed(ErrorResult& aRv);
300 
301   void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable);
302 
303  private:
304   void DisconnectFromWindow();
305   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
306   void ShutdownDecoder();
307 
308   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
309   NS_DECL_NSIMEMORYREPORTER
310 
311   friend struct ::mozilla::WebAudioDecodeJob;
312 
313   nsTArray<MediaStream*> GetAllStreams() const;
314 
315  private:
316   // Each AudioContext has an id, that is passed down the MediaStreams that
317   // back the AudioNodes, so we can easily compute the set of all the
318   // MediaStreams for a given context, on the MediasStreamGraph side.
319   const AudioContextId mId;
320   // Note that it's important for mSampleRate to be initialized before
321   // mDestination, as mDestination's constructor needs to access it!
322   const float mSampleRate;
323   AudioContextState mAudioContextState;
324   RefPtr<AudioDestinationNode> mDestination;
325   RefPtr<AudioListener> mListener;
326   nsTArray<UniquePtr<WebAudioDecodeJob>> mDecodeJobs;
327   // This array is used to keep the suspend/resume/close promises alive until
328   // they are resolved, so we can safely pass them accross threads.
329   nsTArray<RefPtr<Promise>> mPromiseGripArray;
330   // See RegisterActiveNode.  These will keep the AudioContext alive while it
331   // is rendering and the window remains alive.
332   nsTHashtable<nsRefPtrHashKey<AudioNode>> mActiveNodes;
333   // Raw (non-owning) references to all AudioNodes for this AudioContext.
334   nsTHashtable<nsPtrHashKey<AudioNode>> mAllNodes;
335   // Hashsets containing all the PannerNodes, to compute the doppler shift.
336   // These are weak pointers.
337   nsTHashtable<nsPtrHashKey<PannerNode>> mPannerNodes;
338   // Cache to avoid recomputing basic waveforms all the time.
339   RefPtr<BasicWaveFormCache> mBasicWaveFormCache;
340   // Number of channels passed in the OfflineAudioContext ctor.
341   uint32_t mNumberOfChannels;
342   bool mIsOffline;
343   bool mIsStarted;
344   bool mIsShutDown;
345   // Close has been called, reject suspend and resume call.
346   bool mCloseCalled;
347   // Suspend has been called with no following resume.
348   bool mSuspendCalled;
349   bool mIsDisconnecting;
350 };
351 
352 static const dom::AudioContext::AudioContextId NO_AUDIO_CONTEXT = 0;
353 
354 }  // namespace dom
355 }  // namespace mozilla
356 
357 #endif
358