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