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 "DelayNode.h"
8 : #include "mozilla/dom/DelayNodeBinding.h"
9 : #include "AudioNodeEngine.h"
10 : #include "AudioNodeStream.h"
11 : #include "AudioDestinationNode.h"
12 : #include "WebAudioUtils.h"
13 : #include "DelayBuffer.h"
14 : #include "PlayingRefChangeHandler.h"
15 :
16 : namespace mozilla {
17 : namespace dom {
18 :
19 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
20 : mDelay)
21 :
22 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
23 0 : NS_INTERFACE_MAP_END_INHERITING(AudioNode)
24 :
25 0 : NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
26 0 : NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
27 :
28 0 : class DelayNodeEngine final : public AudioNodeEngine
29 : {
30 : typedef PlayingRefChangeHandler PlayingRefChanged;
31 : public:
32 0 : DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
33 : double aMaxDelayTicks)
34 0 : : AudioNodeEngine(aNode)
35 0 : , mDestination(aDestination->Stream())
36 : // Keep the default value in sync with the default value in DelayNode::DelayNode.
37 : , mDelay(0.f)
38 : // Use a smoothing range of 20ms
39 : , mBuffer(std::max(aMaxDelayTicks,
40 0 : static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
41 : WebAudioUtils::ComputeSmoothingRate(0.02,
42 0 : mDestination->SampleRate()))
43 : , mMaxDelay(aMaxDelayTicks)
44 : , mHaveProducedBeforeInput(false)
45 0 : , mLeftOverData(INT32_MIN)
46 : {
47 0 : }
48 :
49 0 : DelayNodeEngine* AsDelayNodeEngine() override
50 : {
51 0 : return this;
52 : }
53 :
54 : enum Parameters {
55 : DELAY,
56 : };
57 0 : void RecvTimelineEvent(uint32_t aIndex,
58 : AudioTimelineEvent& aEvent) override
59 : {
60 0 : MOZ_ASSERT(mDestination);
61 0 : WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
62 0 : mDestination);
63 :
64 0 : switch (aIndex) {
65 : case DELAY:
66 0 : mDelay.InsertEvent<int64_t>(aEvent);
67 0 : break;
68 : default:
69 0 : NS_ERROR("Bad DelayNodeEngine TimelineParameter");
70 : }
71 0 : }
72 :
73 0 : void ProcessBlock(AudioNodeStream* aStream,
74 : GraphTime aFrom,
75 : const AudioBlock& aInput,
76 : AudioBlock* aOutput,
77 : bool* aFinished) override
78 : {
79 0 : MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
80 :
81 0 : if (!aInput.IsSilentOrSubnormal()) {
82 0 : if (mLeftOverData <= 0) {
83 : RefPtr<PlayingRefChanged> refchanged =
84 0 : new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
85 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
86 0 : refchanged.forget());
87 : }
88 0 : mLeftOverData = mBuffer.MaxDelayTicks();
89 0 : } else if (mLeftOverData > 0) {
90 0 : mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
91 : } else {
92 0 : if (mLeftOverData != INT32_MIN) {
93 0 : mLeftOverData = INT32_MIN;
94 0 : aStream->ScheduleCheckForInactive();
95 :
96 : // Delete our buffered data now we no longer need it
97 0 : mBuffer.Reset();
98 :
99 : RefPtr<PlayingRefChanged> refchanged =
100 0 : new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
101 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
102 0 : refchanged.forget());
103 : }
104 0 : aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
105 0 : return;
106 : }
107 :
108 0 : mBuffer.Write(aInput);
109 :
110 : // Skip output update if mLastChunks has already been set by
111 : // ProduceBlockBeforeInput() when in a cycle.
112 0 : if (!mHaveProducedBeforeInput) {
113 0 : UpdateOutputBlock(aStream, aFrom, aOutput, 0.0);
114 : }
115 0 : mHaveProducedBeforeInput = false;
116 0 : mBuffer.NextBlock();
117 : }
118 :
119 0 : void UpdateOutputBlock(AudioNodeStream* aStream, GraphTime aFrom,
120 : AudioBlock* aOutput, double minDelay)
121 : {
122 0 : double maxDelay = mMaxDelay;
123 0 : double sampleRate = aStream->SampleRate();
124 : ChannelInterpretation channelInterpretation =
125 0 : aStream->GetChannelInterpretation();
126 0 : if (mDelay.HasSimpleValue()) {
127 : // If this DelayNode is in a cycle, make sure the delay value is at least
128 : // one block, even if that is greater than maxDelay.
129 0 : double delayFrames = mDelay.GetValue() * sampleRate;
130 : double delayFramesClamped =
131 0 : std::max(minDelay, std::min(delayFrames, maxDelay));
132 0 : mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
133 : } else {
134 : // Compute the delay values for the duration of the input AudioChunk
135 : // If this DelayNode is in a cycle, make sure the delay value is at least
136 : // one block.
137 0 : StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
138 : float values[WEBAUDIO_BLOCK_SIZE];
139 0 : mDelay.GetValuesAtTime(tick, values,WEBAUDIO_BLOCK_SIZE);
140 :
141 : double computedDelay[WEBAUDIO_BLOCK_SIZE];
142 0 : for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
143 0 : double delayAtTick = values[counter] * sampleRate;
144 : double delayAtTickClamped =
145 0 : std::max(minDelay, std::min(delayAtTick, maxDelay));
146 0 : computedDelay[counter] = delayAtTickClamped;
147 : }
148 0 : mBuffer.Read(computedDelay, aOutput, channelInterpretation);
149 : }
150 0 : }
151 :
152 0 : void ProduceBlockBeforeInput(AudioNodeStream* aStream,
153 : GraphTime aFrom,
154 : AudioBlock* aOutput) override
155 : {
156 0 : if (mLeftOverData <= 0) {
157 0 : aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
158 : } else {
159 0 : UpdateOutputBlock(aStream, aFrom, aOutput, WEBAUDIO_BLOCK_SIZE);
160 : }
161 0 : mHaveProducedBeforeInput = true;
162 0 : }
163 :
164 0 : bool IsActive() const override
165 : {
166 0 : return mLeftOverData != INT32_MIN;
167 : }
168 :
169 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
170 : {
171 0 : size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
172 : // Not owned:
173 : // - mDestination - probably not owned
174 : // - mDelay - shares ref with AudioNode, don't count
175 0 : amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
176 0 : return amount;
177 : }
178 :
179 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
180 : {
181 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
182 : }
183 :
184 : AudioNodeStream* mDestination;
185 : AudioParamTimeline mDelay;
186 : DelayBuffer mBuffer;
187 : double mMaxDelay;
188 : bool mHaveProducedBeforeInput;
189 : // How much data we have in our buffer which needs to be flushed out when our inputs
190 : // finish.
191 : int32_t mLeftOverData;
192 : };
193 :
194 0 : DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
195 : : AudioNode(aContext,
196 : 2,
197 : ChannelCountMode::Max,
198 : ChannelInterpretation::Speakers)
199 : , mDelay(new AudioParam(this, DelayNodeEngine::DELAY, "delayTime", 0.0f,
200 0 : 0.f, aMaxDelay))
201 : {
202 : DelayNodeEngine* engine =
203 0 : new DelayNodeEngine(this, aContext->Destination(),
204 0 : aContext->SampleRate() * aMaxDelay);
205 0 : mStream = AudioNodeStream::Create(aContext, engine,
206 : AudioNodeStream::NO_STREAM_FLAGS,
207 0 : aContext->Graph());
208 0 : }
209 :
210 : /* static */ already_AddRefed<DelayNode>
211 0 : DelayNode::Create(AudioContext& aAudioContext,
212 : const DelayOptions& aOptions,
213 : ErrorResult& aRv)
214 : {
215 0 : if (aAudioContext.CheckClosed(aRv)) {
216 0 : return nullptr;
217 : }
218 :
219 0 : if (aOptions.mMaxDelayTime <= 0. || aOptions.mMaxDelayTime >= 180.) {
220 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
221 0 : return nullptr;
222 : }
223 :
224 : RefPtr<DelayNode> audioNode = new DelayNode(&aAudioContext,
225 0 : aOptions.mMaxDelayTime);
226 :
227 0 : audioNode->Initialize(aOptions, aRv);
228 0 : if (NS_WARN_IF(aRv.Failed())) {
229 0 : return nullptr;
230 : }
231 :
232 0 : audioNode->DelayTime()->SetValue(aOptions.mDelayTime);
233 0 : return audioNode.forget();
234 : }
235 :
236 : size_t
237 0 : DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
238 : {
239 0 : size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
240 0 : amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
241 0 : return amount;
242 : }
243 :
244 : size_t
245 0 : DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
246 : {
247 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
248 : }
249 :
250 : JSObject*
251 0 : DelayNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
252 : {
253 0 : return DelayNodeBinding::Wrap(aCx, this, aGivenProto);
254 : }
255 :
256 : } // namespace dom
257 : } // namespace mozilla
|