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