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};
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 #[expect(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 #[expect(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 audio_param.message_node(AudioNodeMessage::SetParamRange(
93 audio_param.param,
94 (min_value, max_value),
95 ));
96 reflect_dom_object(cx, Box::new(audio_param), window)
97 }
98
99 fn message_node(&self, message: AudioNodeMessage) {
100 if let Some(node_id) = self.node {
101 self.context
102 .audio_context_impl()
103 .lock()
104 .unwrap()
105 .message_node(node_id, message);
106 }
107 }
108
109 pub(crate) fn context(&self) -> &BaseAudioContext {
110 &self.context
111 }
112
113 pub(crate) fn node_id(&self) -> Option<NodeId> {
114 self.node
115 }
116
117 pub(crate) fn param_type(&self) -> ParamType {
118 self.param
119 }
120}
121
122impl AudioParamMethods<crate::DomTypeHolder> for AudioParam {
123 fn AutomationRate(&self) -> AutomationRate {
125 self.automation_rate.get()
126 }
127
128 fn SetAutomationRate(&self, automation_rate: AutomationRate) -> Fallible<()> {
130 if automation_rate == AutomationRate::A_rate &&
134 self.node_type == AudioNodeType::AudioBufferSourceNode &&
135 (self.param == ParamType::Detune || self.param == ParamType::PlaybackRate)
136 {
137 return Err(Error::InvalidState(Some(
138 "This AudioParam must be \"k-rate\"".into(),
139 )));
140 }
141
142 self.automation_rate.set(automation_rate);
143 self.message_node(AudioNodeMessage::SetParamRate(
144 self.param,
145 automation_rate.convert(),
146 ));
147
148 Ok(())
149 }
150
151 fn Value(&self) -> Finite<f32> {
153 if self.node.is_none() {
154 return Finite::wrap(self.default_value);
155 }
156 let (tx, rx) = mpsc::channel();
157 self.message_node(AudioNodeMessage::GetParamValue(self.param, tx));
158 Finite::wrap(rx.recv().unwrap())
159 }
160
161 fn SetValue(&self, value: Finite<f32>) {
163 self.message_node(AudioNodeMessage::SetParam(
164 self.param,
165 UserAutomationEvent::SetValue(*value),
166 ));
167 }
168
169 fn DefaultValue(&self) -> Finite<f32> {
171 Finite::wrap(self.default_value)
172 }
173
174 fn MinValue(&self) -> Finite<f32> {
176 Finite::wrap(self.min_value)
177 }
178
179 fn MaxValue(&self) -> Finite<f32> {
181 Finite::wrap(self.max_value)
182 }
183
184 fn SetValueAtTime(
186 &self,
187 value: Finite<f32>,
188 start_time: Finite<f64>,
189 ) -> Fallible<DomRoot<AudioParam>> {
190 if *start_time < 0. {
191 return Err(Error::Range(cformat!(
192 "start time {} should not be negative",
193 *start_time
194 )));
195 }
196 self.message_node(AudioNodeMessage::SetParam(
197 self.param,
198 UserAutomationEvent::SetValueAtTime(*value, *start_time),
199 ));
200 Ok(DomRoot::from_ref(self))
201 }
202
203 fn LinearRampToValueAtTime(
205 &self,
206 value: Finite<f32>,
207 end_time: Finite<f64>,
208 ) -> Fallible<DomRoot<AudioParam>> {
209 if *end_time < 0. {
210 return Err(Error::Range(cformat!(
211 "end time {} should not be negative",
212 *end_time
213 )));
214 }
215 self.message_node(AudioNodeMessage::SetParam(
216 self.param,
217 UserAutomationEvent::RampToValueAtTime(RampKind::Linear, *value, *end_time),
218 ));
219 Ok(DomRoot::from_ref(self))
220 }
221
222 fn ExponentialRampToValueAtTime(
224 &self,
225 value: Finite<f32>,
226 end_time: Finite<f64>,
227 ) -> Fallible<DomRoot<AudioParam>> {
228 if *end_time < 0. {
229 return Err(Error::Range(cformat!(
230 "end time {} should not be negative",
231 *end_time
232 )));
233 }
234 if *value == 0. {
235 return Err(Error::Range(cformat!(
236 "target value {} should not be 0",
237 *value
238 )));
239 }
240 self.message_node(AudioNodeMessage::SetParam(
241 self.param,
242 UserAutomationEvent::RampToValueAtTime(RampKind::Exponential, *value, *end_time),
243 ));
244 Ok(DomRoot::from_ref(self))
245 }
246
247 fn SetTargetAtTime(
249 &self,
250 target: Finite<f32>,
251 start_time: Finite<f64>,
252 time_constant: Finite<f32>,
253 ) -> Fallible<DomRoot<AudioParam>> {
254 if *start_time < 0. {
255 return Err(Error::Range(cformat!(
256 "start time {} should not be negative",
257 *start_time
258 )));
259 }
260 if *time_constant < 0. {
261 return Err(Error::Range(cformat!(
262 "time constant {} should not be negative",
263 *time_constant
264 )));
265 }
266 self.message_node(AudioNodeMessage::SetParam(
267 self.param,
268 UserAutomationEvent::SetTargetAtTime(*target, *start_time, (*time_constant).into()),
269 ));
270 Ok(DomRoot::from_ref(self))
271 }
272
273 fn SetValueCurveAtTime(
275 &self,
276 values: Vec<Finite<f32>>,
277 start_time: Finite<f64>,
278 end_time: Finite<f64>,
279 ) -> Fallible<DomRoot<AudioParam>> {
280 if *start_time < 0. {
281 return Err(Error::Range(cformat!(
282 "start time {} should not be negative",
283 *start_time
284 )));
285 }
286 if values.len() < 2. as usize {
287 return Err(Error::InvalidState(Some(
288 "Values must contain at least 2 elements".into(),
289 )));
290 }
291
292 if *end_time < 0. {
293 return Err(Error::Range(cformat!(
294 "end time {} should not be negative",
295 *end_time
296 )));
297 }
298 self.message_node(AudioNodeMessage::SetParam(
299 self.param,
300 UserAutomationEvent::SetValueCurveAtTime(
301 values.into_iter().map(|v| *v).collect(),
302 *start_time,
303 *end_time,
304 ),
305 ));
306 Ok(DomRoot::from_ref(self))
307 }
308
309 fn CancelScheduledValues(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
311 if *cancel_time < 0. {
312 return Err(Error::Range(cformat!(
313 "cancel time {} should not be negative",
314 *cancel_time
315 )));
316 }
317 self.message_node(AudioNodeMessage::SetParam(
318 self.param,
319 UserAutomationEvent::CancelScheduledValues(*cancel_time),
320 ));
321 Ok(DomRoot::from_ref(self))
322 }
323
324 fn CancelAndHoldAtTime(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
326 if *cancel_time < 0. {
327 return Err(Error::Range(cformat!(
328 "cancel time {} should not be negative",
329 *cancel_time
330 )));
331 }
332 self.message_node(AudioNodeMessage::SetParam(
333 self.param,
334 UserAutomationEvent::CancelAndHoldAtTime(*cancel_time),
335 ));
336 Ok(DomRoot::from_ref(self))
337 }
338}
339
340impl Convert<ParamRate> for AutomationRate {
342 fn convert(self) -> ParamRate {
343 match self {
344 AutomationRate::A_rate => ParamRate::ARate,
345 AutomationRate::K_rate => ParamRate::KRate,
346 }
347 }
348}