1use std::cell::Cell;
6use std::sync::mpsc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::cformat;
11use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
12use servo_media::audio::audio_node::{AudioNodeMessage, AudioNodeType};
13use servo_media::audio::graph::NodeId;
14use servo_media::audio::param::{ParamRate, ParamType, RampKind, UserAutomationEvent};
15
16use crate::conversions::Convert;
17use crate::dom::audio::baseaudiocontext::BaseAudioContext;
18use crate::dom::bindings::codegen::Bindings::AudioParamBinding::{
19 AudioParamMethods, AutomationRate,
20};
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::num::Finite;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::window::Window;
25
26#[dom_struct]
27pub(crate) struct AudioParam {
28 reflector_: Reflector,
29 context: Dom<BaseAudioContext>,
30 #[no_trace]
31 node: Option<NodeId>,
32 #[no_trace]
33 node_type: AudioNodeType,
34 #[no_trace]
35 param: ParamType,
36 automation_rate: Cell<AutomationRate>,
37 default_value: f32,
38 min_value: f32,
39 max_value: f32,
40}
41
42impl AudioParam {
43 #[allow(clippy::too_many_arguments)]
44 pub(crate) fn new_inherited(
45 context: &BaseAudioContext,
46 node: Option<NodeId>,
47 node_type: AudioNodeType,
48 param: ParamType,
49 automation_rate: AutomationRate,
50 default_value: f32,
51 min_value: f32,
52 max_value: f32,
53 ) -> AudioParam {
54 AudioParam {
55 reflector_: Reflector::new(),
56 context: Dom::from_ref(context),
57 node,
58 node_type,
59 param,
60 automation_rate: Cell::new(automation_rate),
61 default_value,
62 min_value,
63 max_value,
64 }
65 }
66
67 #[allow(clippy::too_many_arguments)]
68 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
69 pub(crate) fn new(
70 cx: &mut JSContext,
71 window: &Window,
72 context: &BaseAudioContext,
73 node: Option<NodeId>,
74 node_type: AudioNodeType,
75 param: ParamType,
76 automation_rate: AutomationRate,
77 default_value: f32,
78 min_value: f32,
79 max_value: f32,
80 ) -> DomRoot<AudioParam> {
81 let audio_param = AudioParam::new_inherited(
82 context,
83 node,
84 node_type,
85 param,
86 automation_rate,
87 default_value,
88 min_value,
89 max_value,
90 );
91 reflect_dom_object_with_cx(Box::new(audio_param), window, cx)
92 }
93
94 fn message_node(&self, message: AudioNodeMessage) {
95 if let Some(node_id) = self.node {
96 self.context
97 .audio_context_impl()
98 .lock()
99 .unwrap()
100 .message_node(node_id, message);
101 }
102 }
103
104 pub(crate) fn context(&self) -> &BaseAudioContext {
105 &self.context
106 }
107
108 pub(crate) fn node_id(&self) -> Option<NodeId> {
109 self.node
110 }
111
112 pub(crate) fn param_type(&self) -> ParamType {
113 self.param
114 }
115}
116
117impl AudioParamMethods<crate::DomTypeHolder> for AudioParam {
118 fn AutomationRate(&self) -> AutomationRate {
120 self.automation_rate.get()
121 }
122
123 fn SetAutomationRate(&self, automation_rate: AutomationRate) -> Fallible<()> {
125 if automation_rate == AutomationRate::A_rate &&
129 self.node_type == AudioNodeType::AudioBufferSourceNode &&
130 (self.param == ParamType::Detune || self.param == ParamType::PlaybackRate)
131 {
132 return Err(Error::InvalidState(None));
133 }
134
135 self.automation_rate.set(automation_rate);
136 self.message_node(AudioNodeMessage::SetParamRate(
137 self.param,
138 automation_rate.convert(),
139 ));
140
141 Ok(())
142 }
143
144 fn Value(&self) -> Finite<f32> {
146 if self.node.is_none() {
147 return Finite::wrap(self.default_value);
148 }
149 let (tx, rx) = mpsc::channel();
150 self.message_node(AudioNodeMessage::GetParamValue(self.param, tx));
151 Finite::wrap(rx.recv().unwrap())
152 }
153
154 fn SetValue(&self, value: Finite<f32>) {
156 self.message_node(AudioNodeMessage::SetParam(
157 self.param,
158 UserAutomationEvent::SetValue(*value),
159 ));
160 }
161
162 fn DefaultValue(&self) -> Finite<f32> {
164 Finite::wrap(self.default_value)
165 }
166
167 fn MinValue(&self) -> Finite<f32> {
169 Finite::wrap(self.min_value)
170 }
171
172 fn MaxValue(&self) -> Finite<f32> {
174 Finite::wrap(self.max_value)
175 }
176
177 fn SetValueAtTime(
179 &self,
180 value: Finite<f32>,
181 start_time: Finite<f64>,
182 ) -> Fallible<DomRoot<AudioParam>> {
183 if *start_time < 0. {
184 return Err(Error::Range(cformat!(
185 "start time {} should not be negative",
186 *start_time
187 )));
188 }
189 self.message_node(AudioNodeMessage::SetParam(
190 self.param,
191 UserAutomationEvent::SetValueAtTime(*value, *start_time),
192 ));
193 Ok(DomRoot::from_ref(self))
194 }
195
196 fn LinearRampToValueAtTime(
198 &self,
199 value: Finite<f32>,
200 end_time: Finite<f64>,
201 ) -> Fallible<DomRoot<AudioParam>> {
202 if *end_time < 0. {
203 return Err(Error::Range(cformat!(
204 "end time {} should not be negative",
205 *end_time
206 )));
207 }
208 self.message_node(AudioNodeMessage::SetParam(
209 self.param,
210 UserAutomationEvent::RampToValueAtTime(RampKind::Linear, *value, *end_time),
211 ));
212 Ok(DomRoot::from_ref(self))
213 }
214
215 fn ExponentialRampToValueAtTime(
217 &self,
218 value: Finite<f32>,
219 end_time: Finite<f64>,
220 ) -> Fallible<DomRoot<AudioParam>> {
221 if *end_time < 0. {
222 return Err(Error::Range(cformat!(
223 "end time {} should not be negative",
224 *end_time
225 )));
226 }
227 if *value == 0. {
228 return Err(Error::Range(cformat!(
229 "target value {} should not be 0",
230 *value
231 )));
232 }
233 self.message_node(AudioNodeMessage::SetParam(
234 self.param,
235 UserAutomationEvent::RampToValueAtTime(RampKind::Exponential, *value, *end_time),
236 ));
237 Ok(DomRoot::from_ref(self))
238 }
239
240 fn SetTargetAtTime(
242 &self,
243 target: Finite<f32>,
244 start_time: Finite<f64>,
245 time_constant: Finite<f32>,
246 ) -> Fallible<DomRoot<AudioParam>> {
247 if *start_time < 0. {
248 return Err(Error::Range(cformat!(
249 "start time {} should not be negative",
250 *start_time
251 )));
252 }
253 if *time_constant < 0. {
254 return Err(Error::Range(cformat!(
255 "time constant {} should not be negative",
256 *time_constant
257 )));
258 }
259 self.message_node(AudioNodeMessage::SetParam(
260 self.param,
261 UserAutomationEvent::SetTargetAtTime(*target, *start_time, (*time_constant).into()),
262 ));
263 Ok(DomRoot::from_ref(self))
264 }
265
266 fn SetValueCurveAtTime(
268 &self,
269 values: Vec<Finite<f32>>,
270 start_time: Finite<f64>,
271 end_time: Finite<f64>,
272 ) -> Fallible<DomRoot<AudioParam>> {
273 if *start_time < 0. {
274 return Err(Error::Range(cformat!(
275 "start time {} should not be negative",
276 *start_time
277 )));
278 }
279 if values.len() < 2. as usize {
280 return Err(Error::InvalidState(None));
281 }
282
283 if *end_time < 0. {
284 return Err(Error::Range(cformat!(
285 "end time {} should not be negative",
286 *end_time
287 )));
288 }
289 self.message_node(AudioNodeMessage::SetParam(
290 self.param,
291 UserAutomationEvent::SetValueCurveAtTime(
292 values.into_iter().map(|v| *v).collect(),
293 *start_time,
294 *end_time,
295 ),
296 ));
297 Ok(DomRoot::from_ref(self))
298 }
299
300 fn CancelScheduledValues(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
302 if *cancel_time < 0. {
303 return Err(Error::Range(cformat!(
304 "cancel time {} should not be negative",
305 *cancel_time
306 )));
307 }
308 self.message_node(AudioNodeMessage::SetParam(
309 self.param,
310 UserAutomationEvent::CancelScheduledValues(*cancel_time),
311 ));
312 Ok(DomRoot::from_ref(self))
313 }
314
315 fn CancelAndHoldAtTime(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
317 if *cancel_time < 0. {
318 return Err(Error::Range(cformat!(
319 "cancel time {} should not be negative",
320 *cancel_time
321 )));
322 }
323 self.message_node(AudioNodeMessage::SetParam(
324 self.param,
325 UserAutomationEvent::CancelAndHoldAtTime(*cancel_time),
326 ));
327 Ok(DomRoot::from_ref(self))
328 }
329}
330
331impl Convert<ParamRate> for AutomationRate {
333 fn convert(self) -> ParamRate {
334 match self {
335 AutomationRate::A_rate => ParamRate::ARate,
336 AutomationRate::K_rate => ParamRate::KRate,
337 }
338 }
339}