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