ConstantSourceNode.cpp (8344B)
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 "ConstantSourceNode.h" 8 9 #include "AudioDestinationNode.h" 10 #include "AudioNodeEngine.h" 11 #include "AudioNodeTrack.h" 12 #include "Tracing.h" 13 #include "nsContentUtils.h" 14 15 namespace mozilla::dom { 16 17 NS_IMPL_CYCLE_COLLECTION_INHERITED(ConstantSourceNode, AudioScheduledSourceNode, 18 mOffset) 19 20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConstantSourceNode) 21 NS_INTERFACE_MAP_END_INHERITING(AudioScheduledSourceNode) 22 23 NS_IMPL_ADDREF_INHERITED(ConstantSourceNode, AudioScheduledSourceNode) 24 NS_IMPL_RELEASE_INHERITED(ConstantSourceNode, AudioScheduledSourceNode) 25 26 class ConstantSourceNodeEngine final : public AudioNodeEngine { 27 public: 28 ConstantSourceNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) 29 : AudioNodeEngine(aNode), 30 mSource(nullptr), 31 mDestination(aDestination->Track()), 32 mStart(-1), 33 mStop(TRACK_TIME_MAX) 34 // Keep the default values in sync with 35 // ConstantSourceNode::ConstantSourceNode. 36 , 37 mOffset(1.0f) { 38 MOZ_ASSERT(NS_IsMainThread()); 39 } 40 41 void SetSourceTrack(AudioNodeTrack* aSource) { mSource = aSource; } 42 43 enum Parameters { 44 OFFSET, 45 START, 46 STOP, 47 }; 48 void RecvTimelineEvent(uint32_t aIndex, AudioParamEvent& aEvent) override { 49 MOZ_ASSERT(mDestination); 50 51 aEvent.ConvertToTicks(mDestination); 52 53 switch (aIndex) { 54 case OFFSET: 55 mOffset.InsertEvent<int64_t>(aEvent); 56 break; 57 default: 58 NS_ERROR("Bad ConstantSourceNodeEngine TimelineParameter"); 59 } 60 } 61 62 void SetTrackTimeParameter(uint32_t aIndex, TrackTime aParam) override { 63 switch (aIndex) { 64 case START: 65 mStart = aParam; 66 mSource->SetActive(); 67 break; 68 case STOP: 69 mStop = aParam; 70 break; 71 default: 72 NS_ERROR("Bad ConstantSourceNodeEngine TrackTimeParameter"); 73 } 74 } 75 76 void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom, 77 const AudioBlock& aInput, AudioBlock* aOutput, 78 bool* aFinished) override { 79 MOZ_ASSERT(mSource == aTrack, "Invalid source track"); 80 TRACE("ConstantSourceNodeEngine::ProcessBlock"); 81 82 TrackTime ticks = mDestination->GraphTimeToTrackTime(aFrom); 83 if (mStart == -1) { 84 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); 85 return; 86 } 87 88 if (ticks + WEBAUDIO_BLOCK_SIZE <= mStart || ticks >= mStop || 89 mStop <= mStart) { 90 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); 91 } else { 92 aOutput->AllocateChannels(1); 93 float* output = aOutput->ChannelFloatsForWrite(0); 94 uint32_t writeOffset = 0; 95 96 if (ticks < mStart) { 97 MOZ_ASSERT(mStart - ticks <= WEBAUDIO_BLOCK_SIZE); 98 uint32_t count = mStart - ticks; 99 std::fill_n(output, count, 0.0f); 100 writeOffset += count; 101 } 102 103 MOZ_ASSERT(ticks + writeOffset >= mStart); 104 MOZ_ASSERT(mStop - ticks >= writeOffset); 105 uint32_t count = 106 std::min<TrackTime>(WEBAUDIO_BLOCK_SIZE, mStop - ticks) - writeOffset; 107 108 if (mOffset.HasSimpleValue()) { 109 float value = mOffset.GetValue(); 110 std::fill_n(output + writeOffset, count, value); 111 } else { 112 mOffset.GetValuesAtTime(ticks + writeOffset, output + writeOffset, 113 count); 114 } 115 116 writeOffset += count; 117 118 std::fill_n(output + writeOffset, WEBAUDIO_BLOCK_SIZE - writeOffset, 119 0.0f); 120 } 121 122 if (ticks + WEBAUDIO_BLOCK_SIZE >= mStop) { 123 // We've finished playing. 124 *aFinished = true; 125 } 126 } 127 128 bool IsActive() const override { 129 // start() has been called. 130 return mStart != -1; 131 } 132 133 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { 134 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 135 136 // Not owned: 137 // - mSource 138 // - mDestination 139 // - mOffset (internal ref owned by node) 140 141 return amount; 142 } 143 144 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { 145 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 146 } 147 148 // mSource deletes the engine in its destructor. 149 AudioNodeTrack* MOZ_NON_OWNING_REF mSource; 150 RefPtr<AudioNodeTrack> mDestination; 151 TrackTime mStart; 152 TrackTime mStop; 153 AudioParamTimeline mOffset; 154 }; 155 156 ConstantSourceNode::ConstantSourceNode(AudioContext* aContext) 157 : AudioScheduledSourceNode(aContext, 2, ChannelCountMode::Max, 158 ChannelInterpretation::Speakers), 159 mStartCalled(false) { 160 mOffset = 161 CreateAudioParam(ConstantSourceNodeEngine::OFFSET, u"offset"_ns, 1.0f); 162 ConstantSourceNodeEngine* engine = 163 new ConstantSourceNodeEngine(this, aContext->Destination()); 164 mTrack = AudioNodeTrack::Create(aContext, engine, 165 AudioNodeTrack::NEED_MAIN_THREAD_ENDED, 166 aContext->Graph()); 167 engine->SetSourceTrack(mTrack); 168 mTrack->AddMainThreadListener(this); 169 } 170 171 ConstantSourceNode::~ConstantSourceNode() = default; 172 173 size_t ConstantSourceNode::SizeOfExcludingThis( 174 MallocSizeOf aMallocSizeOf) const { 175 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 176 177 amount += mOffset->SizeOfIncludingThis(aMallocSizeOf); 178 return amount; 179 } 180 181 size_t ConstantSourceNode::SizeOfIncludingThis( 182 MallocSizeOf aMallocSizeOf) const { 183 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 184 } 185 186 JSObject* ConstantSourceNode::WrapObject(JSContext* aCx, 187 JS::Handle<JSObject*> aGivenProto) { 188 return ConstantSourceNode_Binding::Wrap(aCx, this, aGivenProto); 189 } 190 191 already_AddRefed<ConstantSourceNode> ConstantSourceNode::Constructor( 192 const GlobalObject& aGlobal, AudioContext& aContext, 193 const ConstantSourceOptions& aOptions) { 194 RefPtr<ConstantSourceNode> object = new ConstantSourceNode(&aContext); 195 object->mOffset->SetInitialValue(aOptions.mOffset); 196 return object.forget(); 197 } 198 199 void ConstantSourceNode::DestroyMediaTrack() { 200 if (mTrack) { 201 mTrack->RemoveMainThreadListener(this); 202 } 203 AudioNode::DestroyMediaTrack(); 204 } 205 206 void ConstantSourceNode::Start(double aWhen, ErrorResult& aRv) { 207 if (!WebAudioUtils::IsTimeValid(aWhen)) { 208 aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>("start time"); 209 return; 210 } 211 212 if (mStartCalled) { 213 aRv.ThrowInvalidStateError("Can't call start() more than once"); 214 return; 215 } 216 mStartCalled = true; 217 218 if (!mTrack) { 219 return; 220 } 221 222 mTrack->SetTrackTimeParameter(ConstantSourceNodeEngine::START, Context(), 223 aWhen); 224 225 MarkActive(); 226 Context()->StartBlockedAudioContextIfAllowed(); 227 } 228 229 void ConstantSourceNode::Stop(double aWhen, ErrorResult& aRv) { 230 if (!WebAudioUtils::IsTimeValid(aWhen)) { 231 aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>("stop time"); 232 return; 233 } 234 235 if (!mStartCalled) { 236 aRv.ThrowInvalidStateError("Can't call stop() without calling start()"); 237 return; 238 } 239 240 if (!mTrack || !Context()) { 241 return; 242 } 243 244 mTrack->SetTrackTimeParameter(ConstantSourceNodeEngine::STOP, Context(), 245 std::max(0.0, aWhen)); 246 } 247 248 void ConstantSourceNode::NotifyMainThreadTrackEnded() { 249 MOZ_ASSERT(mTrack->IsEnded()); 250 251 class EndedEventDispatcher final : public Runnable { 252 public: 253 explicit EndedEventDispatcher(ConstantSourceNode* aNode) 254 : mozilla::Runnable("EndedEventDispatcher"), mNode(aNode) {} 255 NS_IMETHOD Run() override { 256 // If it's not safe to run scripts right now, schedule this to run later 257 if (!nsContentUtils::IsSafeToRunScript()) { 258 nsContentUtils::AddScriptRunner(this); 259 return NS_OK; 260 } 261 262 mNode->DispatchTrustedEvent(u"ended"_ns); 263 // Release track resources. 264 mNode->DestroyMediaTrack(); 265 return NS_OK; 266 } 267 268 private: 269 RefPtr<ConstantSourceNode> mNode; 270 }; 271 272 Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this))); 273 274 // Drop the playing reference 275 // Warning: The below line might delete this. 276 MarkInactive(); 277 } 278 279 } // namespace mozilla::dom