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(None));
138 }
139
140 self.automation_rate.set(automation_rate);
141 self.message_node(AudioNodeMessage::SetParamRate(
142 self.param,
143 automation_rate.convert(),
144 ));
145
146 Ok(())
147 }
148
149 fn Value(&self) -> Finite<f32> {
151 if self.node.is_none() {
152 return Finite::wrap(self.default_value);
153 }
154 let (tx, rx) = mpsc::channel();
155 self.message_node(AudioNodeMessage::GetParamValue(self.param, tx));
156 Finite::wrap(rx.recv().unwrap())
157 }
158
159 fn SetValue(&self, value: Finite<f32>) {
161 self.message_node(AudioNodeMessage::SetParam(
162 self.param,
163 UserAutomationEvent::SetValue(*value),
164 ));
165 }
166
167 fn DefaultValue(&self) -> Finite<f32> {
169 Finite::wrap(self.default_value)
170 }
171
172 fn MinValue(&self) -> Finite<f32> {
174 Finite::wrap(self.min_value)
175 }
176
177 fn MaxValue(&self) -> Finite<f32> {
179 Finite::wrap(self.max_value)
180 }
181
182 fn SetValueAtTime(
184 &self,
185 value: Finite<f32>,
186 start_time: Finite<f64>,
187 ) -> Fallible<DomRoot<AudioParam>> {
188 if *start_time < 0. {
189 return Err(Error::Range(cformat!(
190 "start time {} should not be negative",
191 *start_time
192 )));
193 }
194 self.message_node(AudioNodeMessage::SetParam(
195 self.param,
196 UserAutomationEvent::SetValueAtTime(*value, *start_time),
197 ));
198 Ok(DomRoot::from_ref(self))
199 }
200
201 fn LinearRampToValueAtTime(
203 &self,
204 value: Finite<f32>,
205 end_time: Finite<f64>,
206 ) -> Fallible<DomRoot<AudioParam>> {
207 if *end_time < 0. {
208 return Err(Error::Range(cformat!(
209 "end time {} should not be negative",
210 *end_time
211 )));
212 }
213 self.message_node(AudioNodeMessage::SetParam(
214 self.param,
215 UserAutomationEvent::RampToValueAtTime(RampKind::Linear, *value, *end_time),
216 ));
217 Ok(DomRoot::from_ref(self))
218 }
219
220 fn ExponentialRampToValueAtTime(
222 &self,
223 value: Finite<f32>,
224 end_time: Finite<f64>,
225 ) -> Fallible<DomRoot<AudioParam>> {
226 if *end_time < 0. {
227 return Err(Error::Range(cformat!(
228 "end time {} should not be negative",
229 *end_time
230 )));
231 }
232 if *value == 0. {
233 return Err(Error::Range(cformat!(
234 "target value {} should not be 0",
235 *value
236 )));
237 }
238 self.message_node(AudioNodeMessage::SetParam(
239 self.param,
240 UserAutomationEvent::RampToValueAtTime(RampKind::Exponential, *value, *end_time),
241 ));
242 Ok(DomRoot::from_ref(self))
243 }
244
245 fn SetTargetAtTime(
247 &self,
248 target: Finite<f32>,
249 start_time: Finite<f64>,
250 time_constant: Finite<f32>,
251 ) -> Fallible<DomRoot<AudioParam>> {
252 if *start_time < 0. {
253 return Err(Error::Range(cformat!(
254 "start time {} should not be negative",
255 *start_time
256 )));
257 }
258 if *time_constant < 0. {
259 return Err(Error::Range(cformat!(
260 "time constant {} should not be negative",
261 *time_constant
262 )));
263 }
264 self.message_node(AudioNodeMessage::SetParam(
265 self.param,
266 UserAutomationEvent::SetTargetAtTime(*target, *start_time, (*time_constant).into()),
267 ));
268 Ok(DomRoot::from_ref(self))
269 }
270
271 fn SetValueCurveAtTime(
273 &self,
274 values: Vec<Finite<f32>>,
275 start_time: Finite<f64>,
276 end_time: Finite<f64>,
277 ) -> Fallible<DomRoot<AudioParam>> {
278 if *start_time < 0. {
279 return Err(Error::Range(cformat!(
280 "start time {} should not be negative",
281 *start_time
282 )));
283 }
284 if values.len() < 2. as usize {
285 return Err(Error::InvalidState(None));
286 }
287
288 if *end_time < 0. {
289 return Err(Error::Range(cformat!(
290 "end time {} should not be negative",
291 *end_time
292 )));
293 }
294 self.message_node(AudioNodeMessage::SetParam(
295 self.param,
296 UserAutomationEvent::SetValueCurveAtTime(
297 values.into_iter().map(|v| *v).collect(),
298 *start_time,
299 *end_time,
300 ),
301 ));
302 Ok(DomRoot::from_ref(self))
303 }
304
305 fn CancelScheduledValues(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
307 if *cancel_time < 0. {
308 return Err(Error::Range(cformat!(
309 "cancel time {} should not be negative",
310 *cancel_time
311 )));
312 }
313 self.message_node(AudioNodeMessage::SetParam(
314 self.param,
315 UserAutomationEvent::CancelScheduledValues(*cancel_time),
316 ));
317 Ok(DomRoot::from_ref(self))
318 }
319
320 fn CancelAndHoldAtTime(&self, cancel_time: Finite<f64>) -> Fallible<DomRoot<AudioParam>> {
322 if *cancel_time < 0. {
323 return Err(Error::Range(cformat!(
324 "cancel time {} should not be negative",
325 *cancel_time
326 )));
327 }
328 self.message_node(AudioNodeMessage::SetParam(
329 self.param,
330 UserAutomationEvent::CancelAndHoldAtTime(*cancel_time),
331 ));
332 Ok(DomRoot::from_ref(self))
333 }
334}
335
336impl Convert<ParamRate> for AutomationRate {
338 fn convert(self) -> ParamRate {
339 match self {
340 AutomationRate::A_rate => ParamRate::ARate,
341 AutomationRate::K_rate => ParamRate::KRate,
342 }
343 }
344}