DocumentTimeline / CSSTransition objects are leaking on CNN.com
[WebKit-https.git] / Source / WebCore / Modules / webaudio / AudioScheduledSourceNode.cpp
1 /*
2  * Copyright (C) 2012, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(WEB_AUDIO)
28
29 #include "AudioScheduledSourceNode.h"
30
31 #include "AudioContext.h"
32 #include "AudioUtilities.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "ScriptController.h"
36 #include "ScriptExecutionContext.h"
37 #include <algorithm>
38 #include <wtf/IsoMallocInlines.h>
39 #include <wtf/MathExtras.h>
40 #include <wtf/Scope.h>
41
42 #if PLATFORM(IOS_FAMILY)
43 #include "ScriptController.h"
44 #endif
45
46 namespace WebCore {
47
48 WTF_MAKE_ISO_ALLOCATED_IMPL(AudioScheduledSourceNode);
49
50 const double AudioScheduledSourceNode::UnknownTime = -1;
51
52 AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext& context, float sampleRate)
53     : AudioNode(context, sampleRate)
54     , ActiveDOMObject(context.scriptExecutionContext())
55     , m_endTime(UnknownTime)
56 {
57     suspendIfNeeded();
58     m_pendingActivity = makePendingActivity(*this);
59 }
60
61 void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize, AudioBus& outputBus, size_t& quantumFrameOffset, size_t& nonSilentFramesToProcess)
62 {
63     nonSilentFramesToProcess = 0;
64     quantumFrameOffset = 0;
65
66     ASSERT(quantumFrameSize == AudioNode::ProcessingSizeInFrames);
67     if (quantumFrameSize != AudioNode::ProcessingSizeInFrames)
68         return;
69
70     double sampleRate = this->sampleRate();
71
72     // quantumStartFrame     : Start frame of the current time quantum.
73     // quantumEndFrame       : End frame of the current time quantum.
74     // startFrame            : Start frame for this source.
75     // endFrame              : End frame for this source.
76     size_t quantumStartFrame = context().currentSampleFrame();
77     size_t quantumEndFrame = quantumStartFrame + quantumFrameSize;
78     size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate);
79     size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate);
80
81     // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle.
82     if (m_endTime != UnknownTime && endFrame <= quantumStartFrame)
83         finish();
84
85     if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || startFrame >= quantumEndFrame) {
86         // Output silence.
87         outputBus.zero();
88         return;
89     }
90
91     // Check if it's time to start playing.
92     if (m_playbackState == SCHEDULED_STATE) {
93         // Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE.
94         m_playbackState = PLAYING_STATE;
95         context().incrementActiveSourceCount();
96     }
97
98     quantumFrameOffset = startFrame > quantumStartFrame ? startFrame - quantumStartFrame : 0;
99     quantumFrameOffset = std::min(quantumFrameOffset, quantumFrameSize); // clamp to valid range
100     nonSilentFramesToProcess = quantumFrameSize - quantumFrameOffset;
101
102     if (!nonSilentFramesToProcess) {
103         // Output silence.
104         outputBus.zero();
105         return;
106     }
107
108     // Handle silence before we start playing.
109     // Zero any initial frames representing silence leading up to a rendering start time in the middle of the quantum.
110     if (quantumFrameOffset) {
111         for (unsigned i = 0; i < outputBus.numberOfChannels(); ++i)
112             memset(outputBus.channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset);
113     }
114
115     // Handle silence after we're done playing.
116     // If the end time is somewhere in the middle of this time quantum, then zero out the
117     // frames from the end time to the very end of the quantum.
118     if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) {
119         size_t zeroStartFrame = endFrame - quantumStartFrame;
120         size_t framesToZero = quantumFrameSize - zeroStartFrame;
121
122         bool isSafe = zeroStartFrame < quantumFrameSize && framesToZero <= quantumFrameSize && zeroStartFrame + framesToZero <= quantumFrameSize;
123         ASSERT(isSafe);
124
125         if (isSafe) {
126             if (framesToZero > nonSilentFramesToProcess)
127                 nonSilentFramesToProcess = 0;
128             else
129                 nonSilentFramesToProcess -= framesToZero;
130
131             for (unsigned i = 0; i < outputBus.numberOfChannels(); ++i)
132                 memset(outputBus.channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero);
133         }
134
135         finish();
136     }
137 }
138
139 ExceptionOr<void> AudioScheduledSourceNode::startLater(double when)
140 {
141     ASSERT(isMainThread());
142     ALWAYS_LOG(LOGIDENTIFIER, when);
143
144     context().nodeWillBeginPlayback();
145
146     if (m_playbackState != UNSCHEDULED_STATE)
147         return Exception { InvalidStateError };
148     if (!std::isfinite(when) || when < 0)
149         return Exception { InvalidStateError };
150
151     m_startTime = when;
152     m_playbackState = SCHEDULED_STATE;
153
154     return { };
155 }
156
157 ExceptionOr<void> AudioScheduledSourceNode::stopLater(double when)
158 {
159     ASSERT(isMainThread());
160     ALWAYS_LOG(LOGIDENTIFIER, when);
161
162     if (m_playbackState == UNSCHEDULED_STATE || m_endTime != UnknownTime)
163         return Exception { InvalidStateError };
164     if (!std::isfinite(when) || when < 0)
165         return Exception { InvalidStateError };
166
167     m_endTime = when;
168
169     return { };
170 }
171
172 void AudioScheduledSourceNode::didBecomeMarkedForDeletion()
173 {
174     ASSERT(context().isGraphOwner());
175     m_pendingActivity = nullptr;
176     ASSERT(!hasPendingActivity());
177 }
178
179 void AudioScheduledSourceNode::finish()
180 {
181     ASSERT(!hasFinished());
182     // Let the context dereference this AudioNode.
183     context().notifyNodeFinishedProcessing(this);
184     m_playbackState = FINISHED_STATE;
185     context().decrementActiveSourceCount();
186
187     context().postTask([this, protectedThis = makeRef(*this)] {
188         auto release = makeScopeExit([&] () {
189             AudioContext::AutoLocker locker(context());
190             m_pendingActivity = nullptr;
191             ASSERT(!hasPendingActivity());
192         });
193         if (context().isStopped())
194             return;
195         this->dispatchEvent(Event::create(eventNames().endedEvent, Event::CanBubble::No, Event::IsCancelable::No));
196     });
197 }
198
199 } // namespace WebCore
200
201 #endif // ENABLE(WEB_AUDIO)