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 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
20                                    mDelay)
21 
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
23 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
24 
25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
27 
28 class DelayNodeEngine final : public AudioNodeEngine
29 {
30   typedef PlayingRefChangeHandler PlayingRefChanged;
31 public:
DelayNodeEngine(AudioNode * aNode,AudioDestinationNode * aDestination,double aMaxDelayTicks)32   DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
33                   double aMaxDelayTicks)
34     : AudioNodeEngine(aNode)
35     , 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                        static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
41               WebAudioUtils::ComputeSmoothingRate(0.02,
42                                                   mDestination->SampleRate()))
43     , mMaxDelay(aMaxDelayTicks)
44     , mHaveProducedBeforeInput(false)
45     , mLeftOverData(INT32_MIN)
46   {
47   }
48 
AsDelayNodeEngine()49   DelayNodeEngine* AsDelayNodeEngine() override
50   {
51     return this;
52   }
53 
54   enum Parameters {
55     DELAY,
56   };
RecvTimelineEvent(uint32_t aIndex,AudioTimelineEvent & aEvent)57   void RecvTimelineEvent(uint32_t aIndex,
58                          AudioTimelineEvent& aEvent) override
59   {
60     MOZ_ASSERT(mDestination);
61     WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
62                                                     mDestination);
63 
64     switch (aIndex) {
65     case DELAY:
66       mDelay.InsertEvent<int64_t>(aEvent);
67       break;
68     default:
69       NS_ERROR("Bad DelayNodeEngine TimelineParameter");
70     }
71   }
72 
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)73   void ProcessBlock(AudioNodeStream* aStream,
74                     GraphTime aFrom,
75                     const AudioBlock& aInput,
76                     AudioBlock* aOutput,
77                     bool* aFinished) override
78   {
79     MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
80 
81     if (!aInput.IsSilentOrSubnormal()) {
82       if (mLeftOverData <= 0) {
83         RefPtr<PlayingRefChanged> refchanged =
84           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
85         aStream->Graph()->
86           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
87       }
88       mLeftOverData = mBuffer.MaxDelayTicks();
89     } else if (mLeftOverData > 0) {
90       mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
91     } else {
92       if (mLeftOverData != INT32_MIN) {
93         mLeftOverData = INT32_MIN;
94         aStream->ScheduleCheckForInactive();
95 
96         // Delete our buffered data now we no longer need it
97         mBuffer.Reset();
98 
99         RefPtr<PlayingRefChanged> refchanged =
100           new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
101         aStream->Graph()->
102           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
103       }
104       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
105       return;
106     }
107 
108     mBuffer.Write(aInput);
109 
110     // Skip output update if mLastChunks has already been set by
111     // ProduceBlockBeforeInput() when in a cycle.
112     if (!mHaveProducedBeforeInput) {
113       UpdateOutputBlock(aStream, aFrom, aOutput, 0.0);
114     }
115     mHaveProducedBeforeInput = false;
116     mBuffer.NextBlock();
117   }
118 
UpdateOutputBlock(AudioNodeStream * aStream,GraphTime aFrom,AudioBlock * aOutput,double minDelay)119   void UpdateOutputBlock(AudioNodeStream* aStream, GraphTime aFrom,
120                          AudioBlock* aOutput, double minDelay)
121   {
122     double maxDelay = mMaxDelay;
123     double sampleRate = aStream->SampleRate();
124     ChannelInterpretation channelInterpretation =
125       aStream->GetChannelInterpretation();
126     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       double delayFrames = mDelay.GetValue() * sampleRate;
130       double delayFramesClamped =
131         std::max(minDelay, std::min(delayFrames, maxDelay));
132       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       StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
138       float values[WEBAUDIO_BLOCK_SIZE];
139       mDelay.GetValuesAtTime(tick, values,WEBAUDIO_BLOCK_SIZE);
140 
141       double computedDelay[WEBAUDIO_BLOCK_SIZE];
142       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
143         double delayAtTick = values[counter] * sampleRate;
144         double delayAtTickClamped =
145           std::max(minDelay, std::min(delayAtTick, maxDelay));
146         computedDelay[counter] = delayAtTickClamped;
147       }
148       mBuffer.Read(computedDelay, aOutput, channelInterpretation);
149     }
150   }
151 
ProduceBlockBeforeInput(AudioNodeStream * aStream,GraphTime aFrom,AudioBlock * aOutput)152   void ProduceBlockBeforeInput(AudioNodeStream* aStream,
153                                GraphTime aFrom,
154                                AudioBlock* aOutput) override
155   {
156     if (mLeftOverData <= 0) {
157       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
158     } else {
159       UpdateOutputBlock(aStream, aFrom, aOutput, WEBAUDIO_BLOCK_SIZE);
160     }
161     mHaveProducedBeforeInput = true;
162   }
163 
IsActive() const164   bool IsActive() const override
165   {
166     return mLeftOverData != INT32_MIN;
167   }
168 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const169   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
170   {
171     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
172     // Not owned:
173     // - mDestination - probably not owned
174     // - mDelay - shares ref with AudioNode, don't count
175     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
176     return amount;
177   }
178 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const179   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
180   {
181     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 
DelayNode(AudioContext * aContext,double aMaxDelay)194 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
195   : AudioNode(aContext,
196               2,
197               ChannelCountMode::Max,
198               ChannelInterpretation::Speakers)
199   , mDelay(new AudioParam(this, DelayNodeEngine::DELAY, 0.0f, "delayTime"))
200 {
201   DelayNodeEngine* engine =
202     new DelayNodeEngine(this, aContext->Destination(),
203                         aContext->SampleRate() * aMaxDelay);
204   mStream = AudioNodeStream::Create(aContext, engine,
205                                     AudioNodeStream::NO_STREAM_FLAGS,
206                                     aContext->Graph());
207 }
208 
~DelayNode()209 DelayNode::~DelayNode()
210 {
211 }
212 
213 size_t
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const214 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
215 {
216   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
217   amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
218   return amount;
219 }
220 
221 size_t
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const222 DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
223 {
224   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
225 }
226 
227 JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)228 DelayNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
229 {
230   return DelayNodeBinding::Wrap(aCx, this, aGivenProto);
231 }
232 
233 } // namespace dom
234 } // namespace mozilla
235