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 #ifndef AudioParam_h_
8 #define AudioParam_h_
9 
10 #include "AudioParamTimeline.h"
11 #include "nsWrapperCache.h"
12 #include "nsCycleCollectionParticipant.h"
13 #include "AudioNode.h"
14 #include "mozilla/dom/TypedArray.h"
15 #include "WebAudioUtils.h"
16 #include "js/TypeDecls.h"
17 
18 namespace mozilla {
19 
20 namespace dom {
21 
22 class AudioParam final : public nsWrapperCache, public AudioParamTimeline {
23   virtual ~AudioParam();
24 
25  public:
26   AudioParam(AudioNode* aNode, uint32_t aIndex, const char16_t* aName,
27              float aDefaultValue,
28              float aMinValue = std::numeric_limits<float>::lowest(),
29              float aMaxValue = std::numeric_limits<float>::max());
30 
31   NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
32   NS_IMETHOD_(MozExternalRefCountType) Release(void);
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioParam)33   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioParam)
34 
35   AudioContext* GetParentObject() const { return mNode->Context(); }
36 
37   JSObject* WrapObject(JSContext* aCx,
38                        JS::Handle<JSObject*> aGivenProto) override;
39 
Value()40   float Value() {
41     return AudioParamTimeline::GetValueAtTime<double>(
42         GetParentObject()->CurrentTime());
43   }
44 
45   // We override SetValueCurveAtTime to convert the Float32Array to the wrapper
46   // object.
SetValueCurveAtTime(const nsTArray<float> & aValues,double aStartTime,double aDuration,ErrorResult & aRv)47   AudioParam* SetValueCurveAtTime(const nsTArray<float>& aValues,
48                                   double aStartTime, double aDuration,
49                                   ErrorResult& aRv) {
50     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
51       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
52       return this;
53     }
54     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
55     EventInsertionHelper(aRv, AudioTimelineEvent::SetValueCurve, aStartTime,
56                          0.0f, 0.0f, aDuration, aValues.Elements(),
57                          aValues.Length());
58 
59     return this;
60   }
61 
SetValue(float aValue)62   void SetValue(float aValue) {
63     AudioTimelineEvent event(AudioTimelineEvent::SetValue, 0.0f, aValue);
64 
65     ErrorResult rv;
66     if (!ValidateEvent(event, rv)) {
67       MOZ_ASSERT(false,
68                  "This should not happen, "
69                  "setting the value should always work");
70       return;
71     }
72 
73     AudioParamTimeline::SetValue(aValue);
74 
75     SendEventToEngine(event);
76   }
77 
SetValueAtTime(float aValue,double aStartTime,ErrorResult & aRv)78   AudioParam* SetValueAtTime(float aValue, double aStartTime,
79                              ErrorResult& aRv) {
80     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
81       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
82       return this;
83     }
84     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
85     EventInsertionHelper(aRv, AudioTimelineEvent::SetValueAtTime, aStartTime,
86                          aValue);
87 
88     return this;
89   }
90 
LinearRampToValueAtTime(float aValue,double aEndTime,ErrorResult & aRv)91   AudioParam* LinearRampToValueAtTime(float aValue, double aEndTime,
92                                       ErrorResult& aRv) {
93     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
94       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR>();
95       return this;
96     }
97     aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
98     EventInsertionHelper(aRv, AudioTimelineEvent::LinearRamp, aEndTime, aValue);
99     return this;
100   }
101 
ExponentialRampToValueAtTime(float aValue,double aEndTime,ErrorResult & aRv)102   AudioParam* ExponentialRampToValueAtTime(float aValue, double aEndTime,
103                                            ErrorResult& aRv) {
104     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
105       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR>();
106       return this;
107     }
108     aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
109     EventInsertionHelper(aRv, AudioTimelineEvent::ExponentialRamp, aEndTime,
110                          aValue);
111     return this;
112   }
113 
SetTargetAtTime(float aTarget,double aStartTime,double aTimeConstant,ErrorResult & aRv)114   AudioParam* SetTargetAtTime(float aTarget, double aStartTime,
115                               double aTimeConstant, ErrorResult& aRv) {
116     if (!WebAudioUtils::IsTimeValid(aStartTime) ||
117         !WebAudioUtils::IsTimeValid(aTimeConstant)) {
118       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
119       return this;
120     }
121     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
122     EventInsertionHelper(aRv, AudioTimelineEvent::SetTarget, aStartTime,
123                          aTarget, aTimeConstant);
124 
125     return this;
126   }
127 
CancelScheduledValues(double aStartTime,ErrorResult & aRv)128   AudioParam* CancelScheduledValues(double aStartTime, ErrorResult& aRv) {
129     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
130       aRv.ThrowRangeError<MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
131       return this;
132     }
133 
134     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
135 
136     // Remove some events on the main thread copy.
137     AudioEventTimeline::CancelScheduledValues(aStartTime);
138 
139     AudioTimelineEvent event(AudioTimelineEvent::Cancel, aStartTime, 0.0f);
140 
141     SendEventToEngine(event);
142 
143     return this;
144   }
145 
ParentNodeId()146   uint32_t ParentNodeId() { return mNode->Id(); }
147 
GetName(nsAString & aName)148   void GetName(nsAString& aName) { aName.Assign(mName); }
149 
DefaultValue()150   float DefaultValue() const { return mDefaultValue; }
151 
MinValue()152   float MinValue() const { return mMinValue; }
153 
MaxValue()154   float MaxValue() const { return mMaxValue; }
155 
IsTrackSuspended()156   bool IsTrackSuspended() const {
157     return mTrack ? mTrack->IsSuspended() : false;
158   }
159 
InputNodes()160   const nsTArray<AudioNode::InputNode>& InputNodes() const {
161     return mInputNodes;
162   }
163 
RemoveInputNode(uint32_t aIndex)164   void RemoveInputNode(uint32_t aIndex) { mInputNodes.RemoveElementAt(aIndex); }
165 
AppendInputNode()166   AudioNode::InputNode* AppendInputNode() {
167     return mInputNodes.AppendElement();
168   }
169 
170   // May create the track if it doesn't exist
171   mozilla::MediaTrack* Track();
172 
173   // Return nullptr if track doesn't exist.
174   mozilla::MediaTrack* GetTrack() const;
175 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)176   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
177     size_t amount = AudioParamTimeline::SizeOfExcludingThis(aMallocSizeOf);
178     // Not owned:
179     // - mNode
180 
181     // Just count the array, actual nodes are counted in mNode.
182     amount += mInputNodes.ShallowSizeOfExcludingThis(aMallocSizeOf);
183 
184     if (mNodeTrackPort) {
185       amount += mNodeTrackPort->SizeOfIncludingThis(aMallocSizeOf);
186     }
187 
188     return amount;
189   }
190 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)191   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
192     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
193   }
194 
195  private:
196   void EventInsertionHelper(ErrorResult& aRv, AudioTimelineEvent::Type aType,
197                             double aTime, float aValue,
198                             double aTimeConstant = 0.0, double aDuration = 0.0,
199                             const float* aCurve = nullptr,
200                             uint32_t aCurveLength = 0) {
201     AudioTimelineEvent event(aType, aTime, aValue, aTimeConstant, aDuration,
202                              aCurve, aCurveLength);
203 
204     if (!ValidateEvent(event, aRv)) {
205       return;
206     }
207 
208     AudioEventTimeline::InsertEvent<double>(event);
209 
210     SendEventToEngine(event);
211 
212     CleanupOldEvents();
213   }
214 
215   void CleanupOldEvents();
216 
217   void SendEventToEngine(const AudioTimelineEvent& aEvent);
218 
219   void DisconnectFromGraphAndDestroyTrack();
220 
221   nsCycleCollectingAutoRefCnt mRefCnt;
222   NS_DECL_OWNINGTHREAD
223   RefPtr<AudioNode> mNode;
224   // For every InputNode, there is a corresponding entry in mOutputParams of the
225   // InputNode's mInputNode.
226   nsTArray<AudioNode::InputNode> mInputNodes;
227   const char16_t* mName;
228   // The input port used to connect the AudioParam's track to its node's track
229   RefPtr<MediaInputPort> mNodeTrackPort;
230   const uint32_t mIndex;
231   const float mDefaultValue;
232   const float mMinValue;
233   const float mMaxValue;
234 };
235 
236 }  // namespace dom
237 }  // namespace mozilla
238 
239 #endif
240