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