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