web_audio_api/node/
scheduled_source.rs1use super::AudioNode;
2use crate::events::{Event, EventHandler, EventType};
3
4pub trait AudioScheduledSourceNode: AudioNode {
7 fn start(&mut self);
13
14 fn start_at(&mut self, when: f64);
20
21 fn stop(&mut self);
27
28 fn stop_at(&mut self, when: f64);
34
35 fn set_onended<F: FnOnce(Event) + Send + 'static>(&self, callback: F) {
45 let callback = move |_| callback(Event { type_: "ended" });
46
47 self.context().set_event_handler(
48 EventType::Ended(self.registration().id()),
49 EventHandler::Once(Box::new(callback)),
50 );
51 }
52
53 fn clear_onended(&self) {
55 self.context()
56 .clear_event_handler(EventType::Ended(self.registration().id()));
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use crate::context::{AudioContextRegistration, BaseAudioContext, OfflineAudioContext};
63 use crate::node::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
64
65 use std::sync::atomic::{AtomicBool, Ordering};
66 use std::sync::Arc;
67
68 enum ConcreteAudioScheduledSourceNode {
69 Buffer(crate::node::AudioBufferSourceNode),
70 Constant(crate::node::ConstantSourceNode),
71 Oscillator(crate::node::OscillatorNode),
72 }
73 use ConcreteAudioScheduledSourceNode::*;
74
75 impl AudioNode for ConcreteAudioScheduledSourceNode {
76 fn registration(&self) -> &AudioContextRegistration {
77 match self {
78 Buffer(n) => n.registration(),
79 Constant(n) => n.registration(),
80 Oscillator(n) => n.registration(),
81 }
82 }
83
84 fn channel_config(&self) -> &ChannelConfig {
85 match self {
86 Buffer(n) => n.channel_config(),
87 Constant(n) => n.channel_config(),
88 Oscillator(n) => n.channel_config(),
89 }
90 }
91
92 fn number_of_inputs(&self) -> usize {
93 match self {
94 Buffer(n) => n.number_of_inputs(),
95 Constant(n) => n.number_of_inputs(),
96 Oscillator(n) => n.number_of_inputs(),
97 }
98 }
99
100 fn number_of_outputs(&self) -> usize {
101 match self {
102 Buffer(n) => n.number_of_outputs(),
103 Constant(n) => n.number_of_outputs(),
104 Oscillator(n) => n.number_of_outputs(),
105 }
106 }
107 }
108
109 impl AudioScheduledSourceNode for ConcreteAudioScheduledSourceNode {
110 fn start(&mut self) {
111 match self {
112 Buffer(n) => n.start(),
113 Constant(n) => n.start(),
114 Oscillator(n) => n.start(),
115 }
116 }
117
118 fn start_at(&mut self, when: f64) {
119 match self {
120 Buffer(n) => n.start_at(when),
121 Constant(n) => n.start_at(when),
122 Oscillator(n) => n.start_at(when),
123 }
124 }
125
126 fn stop(&mut self) {
127 match self {
128 Buffer(n) => n.stop(),
129 Constant(n) => n.stop(),
130 Oscillator(n) => n.stop(),
131 }
132 }
133
134 fn stop_at(&mut self, when: f64) {
135 match self {
136 Buffer(n) => n.stop_at(when),
137 Constant(n) => n.stop_at(when),
138 Oscillator(n) => n.stop_at(when),
139 }
140 }
141 }
142
143 fn run_ended_event(f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode) {
144 let mut context = OfflineAudioContext::new(2, 44_100, 44_100.);
145 let mut src = f(&context);
146 src.start_at(0.);
147 src.stop_at(0.5);
148
149 let ended = Arc::new(AtomicBool::new(false));
150 let ended_clone = Arc::clone(&ended);
151 src.set_onended(move |_event| {
152 ended_clone.store(true, Ordering::Relaxed);
153 });
154
155 let _ = context.start_rendering_sync();
156 assert!(ended.load(Ordering::Relaxed));
157 }
158
159 #[test]
160 fn test_ended_event_constant_source() {
161 run_ended_event(|c| Constant(c.create_constant_source()));
162 }
163 #[test]
164 fn test_ended_event_buffer_source() {
165 run_ended_event(|c| Buffer(c.create_buffer_source()));
166 }
167 #[test]
168 fn test_ended_event_oscillator() {
169 run_ended_event(|c| Oscillator(c.create_oscillator()));
170 }
171
172 fn run_no_ended_event(
173 f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode,
174 ) {
175 let mut context = OfflineAudioContext::new(2, 44_100, 44_100.);
176 let src = f(&context);
177
178 let ended = Arc::new(AtomicBool::new(false));
181 let ended_clone = Arc::clone(&ended);
182 src.set_onended(move |_event| {
183 ended_clone.store(true, Ordering::Relaxed);
184 });
185
186 let _ = context.start_rendering_sync();
187 assert!(!ended.load(Ordering::Relaxed)); }
189
190 #[test]
191 fn test_no_ended_event_constant_source() {
192 run_no_ended_event(|c| Constant(c.create_constant_source()));
193 }
194 #[test]
195 fn test_no_ended_event_buffer_source() {
196 run_no_ended_event(|c| Buffer(c.create_buffer_source()));
197 }
198 #[test]
199 fn test_no_ended_event_oscillator() {
200 run_no_ended_event(|c| Oscillator(c.create_oscillator()));
201 }
202
203 fn run_exact_ended_event(
204 f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode,
205 ) {
206 let mut context = OfflineAudioContext::new(2, 44_100, 44_100.);
207 let mut src = f(&context);
208 src.start_at(0.);
209 src.stop_at(1.); let ended = Arc::new(AtomicBool::new(false));
212 let ended_clone = Arc::clone(&ended);
213 src.set_onended(move |_event| {
214 ended_clone.store(true, Ordering::Relaxed);
215 });
216
217 let _ = context.start_rendering_sync();
218 assert!(ended.load(Ordering::Relaxed));
219 }
220
221 #[test]
222 fn test_exact_ended_event_constant_source() {
223 run_exact_ended_event(|c| Constant(c.create_constant_source()));
224 }
225 #[test]
226 fn test_exact_ended_event_buffer_source() {
227 run_exact_ended_event(|c| Buffer(c.create_buffer_source()));
228 }
229 #[test]
230 fn test_exact_ended_event_oscillator() {
231 run_exact_ended_event(|c| Oscillator(c.create_oscillator()));
232 }
233
234 fn run_implicit_ended_event(
235 f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode,
236 ) {
237 let mut context = OfflineAudioContext::new(2, 44_100, 44_100.);
238 let mut src = f(&context);
239 src.start_at(0.);
240 let ended = Arc::new(AtomicBool::new(false));
243 let ended_clone = Arc::clone(&ended);
244 src.set_onended(move |_event| {
245 ended_clone.store(true, Ordering::Relaxed);
246 });
247
248 let _ = context.start_rendering_sync();
249 assert!(ended.load(Ordering::Relaxed));
250 }
251
252 #[test]
253 fn test_implicit_ended_event_constant_source() {
254 run_implicit_ended_event(|c| Constant(c.create_constant_source()));
255 }
256
257 #[test]
258 fn test_implicit_ended_event_buffer_source() {
259 run_implicit_ended_event(|c| Buffer(c.create_buffer_source()));
260 }
261
262 #[test]
263 fn test_implicit_ended_event_oscillator() {
264 run_implicit_ended_event(|c| Oscillator(c.create_oscillator()));
265 }
266
267 fn run_start_twice(f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode) {
268 let context = OfflineAudioContext::new(2, 1, 44_100.);
269 let mut src = f(&context);
270 src.start();
271 src.start();
272 }
273
274 #[test]
275 #[should_panic]
276 fn test_start_twice_constant_source() {
277 run_start_twice(|c| Constant(c.create_constant_source()));
278 }
279
280 #[test]
281 #[should_panic]
282 fn test_start_twice_buffer_source() {
283 run_start_twice(|c| Buffer(c.create_buffer_source()));
284 }
285
286 #[test]
287 #[should_panic]
288 fn test_start_twice_oscillator() {
289 run_start_twice(|c| Oscillator(c.create_oscillator()));
290 }
291
292 fn run_stop_before_start(
293 f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode,
294 ) {
295 let context = OfflineAudioContext::new(2, 1, 44_100.);
296 let mut src = f(&context);
297 src.stop();
298 }
299
300 #[test]
301 #[should_panic]
302 fn test_stop_before_start_constant_source() {
303 run_stop_before_start(|c| Constant(c.create_constant_source()));
304 }
305
306 #[test]
307 #[should_panic]
308 fn test_stop_before_start_buffer_source() {
309 run_stop_before_start(|c| Buffer(c.create_buffer_source()));
310 }
311
312 #[test]
313 #[should_panic]
314 fn test_stop_before_start_oscillator() {
315 run_stop_before_start(|c| Oscillator(c.create_oscillator()));
316 }
317
318 fn run_stop_twice(f: impl FnOnce(&OfflineAudioContext) -> ConcreteAudioScheduledSourceNode) {
319 let context = OfflineAudioContext::new(2, 1, 44_100.);
320 let mut src = f(&context);
321 src.start();
322 src.stop();
323 src.stop();
324 }
325
326 #[test]
327 #[should_panic]
328 fn test_stop_twice_constant_source() {
329 run_stop_twice(|c| Constant(c.create_constant_source()));
330 }
331 #[test]
332 #[should_panic]
333 fn test_stop_twice_buffer_source() {
334 run_stop_twice(|c| Buffer(c.create_buffer_source()));
335 }
336 #[test]
337 #[should_panic]
338 fn test_stop_twice_oscillator() {
339 run_stop_twice(|c| Oscillator(c.create_oscillator()));
340 }
341}