1pub use crate::render::AudioWorkletGlobalScope;
9
10use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
11use crate::node::{AudioNode, AudioNodeOptions, ChannelConfig};
12use crate::param::{AudioParam, AudioParamDescriptor};
13use crate::render::{AudioProcessor, AudioRenderQuantum};
14use crate::{MessagePort, MAX_CHANNELS};
15
16use std::any::Any;
17use std::collections::HashMap;
18use std::ops::{Deref, DerefMut};
19
20pub struct AudioParamValues<'a> {
22 values: crate::render::AudioParamValues<'a>,
23 map: &'a HashMap<String, AudioParamId>,
24}
25
26impl std::fmt::Debug for AudioParamValues<'_> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("AudioParamValues").finish_non_exhaustive()
29 }
30}
31
32impl<'a> AudioParamValues<'a> {
33 #[allow(clippy::missing_panics_doc)]
39 pub fn get(&'a self, name: &str) -> impl Deref<Target = [f32]> + 'a {
40 let id = self.map.get(name).unwrap();
41 self.values.get(id)
42 }
43
44 pub fn keys(&self) -> impl Iterator<Item = &str> {
45 self.map.keys().map(|s| s.as_ref())
46 }
47}
48
49pub trait AudioWorkletProcessor {
51 type ProcessorOptions: Send;
57
58 fn constructor(opts: Self::ProcessorOptions) -> Self
60 where
61 Self: Sized;
62
63 fn parameter_descriptors() -> Vec<AudioParamDescriptor>
67 where
68 Self: Sized,
69 {
70 vec![] }
72
73 fn process<'a, 'b>(
92 &mut self,
93 inputs: &'b [&'a [&'a [f32]]],
94 outputs: &'b mut [&'a mut [&'a mut [f32]]],
95 params: AudioParamValues<'b>,
96 scope: &'b AudioWorkletGlobalScope,
97 ) -> bool;
98
99 fn onmessage(&mut self, _msg: &mut dyn Any) {
112 log::warn!("AudioWorkletProcessor: Ignoring incoming message");
113 }
114}
115
116#[derive(Clone, Debug)]
125pub struct AudioWorkletNodeOptions<C> {
126 pub number_of_inputs: usize,
128 pub number_of_outputs: usize,
130 pub output_channel_count: Vec<usize>,
132 pub parameter_data: HashMap<String, f64>,
135 pub processor_options: C,
138 pub audio_node_options: AudioNodeOptions,
140}
141
142impl<C: Default> Default for AudioWorkletNodeOptions<C> {
143 fn default() -> Self {
144 Self {
145 number_of_inputs: 1,
146 number_of_outputs: 1,
147 output_channel_count: Vec::new(),
148 parameter_data: HashMap::new(),
149 processor_options: C::default(),
150 audio_node_options: AudioNodeOptions::default(),
151 }
152 }
153}
154
155#[derive(Debug)]
167pub struct AudioWorkletNode {
168 registration: AudioContextRegistration,
169 channel_config: ChannelConfig,
170 number_of_inputs: usize,
171 number_of_outputs: usize,
172 audio_param_map: HashMap<String, AudioParam>,
173}
174
175impl AudioNode for AudioWorkletNode {
176 fn registration(&self) -> &AudioContextRegistration {
177 &self.registration
178 }
179
180 fn channel_config(&self) -> &ChannelConfig {
181 &self.channel_config
182 }
183
184 fn number_of_inputs(&self) -> usize {
185 self.number_of_inputs
186 }
187
188 fn number_of_outputs(&self) -> usize {
189 self.number_of_outputs
190 }
191}
192
193impl AudioWorkletNode {
194 pub fn new<P: AudioWorkletProcessor + 'static>(
203 context: &impl BaseAudioContext,
204 options: AudioWorkletNodeOptions<P::ProcessorOptions>,
205 ) -> Self {
206 let AudioWorkletNodeOptions {
207 number_of_inputs,
208 number_of_outputs,
209 output_channel_count,
210 parameter_data,
211 processor_options,
212 audio_node_options: channel_config,
213 } = options;
214
215 assert!(
216 number_of_inputs != 0 || number_of_outputs != 0,
217 "NotSupportedError: number of inputs and outputs cannot both be zero"
218 );
219
220 let output_channel_count = if output_channel_count.is_empty() {
221 if number_of_inputs == 1 && number_of_outputs == 1 {
222 vec![] } else {
224 vec![1; number_of_outputs]
225 }
226 } else {
227 output_channel_count
228 .iter()
229 .copied()
230 .for_each(crate::assert_valid_number_of_channels);
231 assert_eq!(
232 output_channel_count.len(),
233 number_of_outputs,
234 "IndexSizeError: outputChannelCount.length should equal numberOfOutputs"
235 );
236 output_channel_count
237 };
238
239 let number_of_output_channels = if output_channel_count.is_empty() {
240 MAX_CHANNELS
241 } else {
242 output_channel_count.iter().sum::<usize>()
243 };
244
245 let node = context.base().register(move |registration| {
246 let mut node_param_map = HashMap::new();
248 let mut processor_param_map = HashMap::new();
249 for mut param_descriptor in P::parameter_descriptors() {
250 let name = std::mem::take(&mut param_descriptor.name);
251 let (param, proc) = context.create_audio_param(param_descriptor, ®istration);
252 if let Some(value) = parameter_data.get(&name) {
253 param.set_value(*value as f32); }
255 node_param_map.insert(name.clone(), param);
256 processor_param_map.insert(name, proc);
257 }
258
259 let node = AudioWorkletNode {
260 registration,
261 channel_config: channel_config.into(),
262 number_of_inputs,
263 number_of_outputs,
264 audio_param_map: node_param_map,
265 };
266
267 let render: AudioWorkletRenderer<P> = AudioWorkletRenderer {
268 processor: Processor::new(processor_options),
269 audio_param_map: processor_param_map,
270 output_channel_count,
271 inputs_flat: Vec::with_capacity(number_of_inputs * MAX_CHANNELS),
272 inputs_grouped: Vec::with_capacity(number_of_inputs),
273 outputs_flat: Vec::with_capacity(number_of_output_channels),
274 outputs_grouped: Vec::with_capacity(number_of_outputs),
275 };
276
277 (node, Box::new(render))
278 });
279
280 node
281 }
282
283 pub fn parameters(&self) -> &HashMap<String, AudioParam> {
288 &self.audio_param_map
289 }
290
291 pub fn port(&self) -> MessagePort<'_> {
297 MessagePort::from_node(self)
298 }
299}
300
301enum Processor<P: AudioWorkletProcessor> {
302 Uninit(Option<P::ProcessorOptions>),
303 Init(P),
304}
305
306impl<P: AudioWorkletProcessor> Processor<P> {
307 fn new(opts: P::ProcessorOptions) -> Self {
308 Self::Uninit(Some(opts))
309 }
310
311 fn load(&mut self) -> &mut dyn AudioWorkletProcessor<ProcessorOptions = P::ProcessorOptions> {
312 if let Processor::Uninit(opts) = self {
313 *self = Self::Init(P::constructor(opts.take().unwrap()));
314 }
315
316 match self {
317 Self::Init(p) => p,
318 Self::Uninit(_) => unreachable!(),
319 }
320 }
321}
322
323struct AudioWorkletRenderer<P: AudioWorkletProcessor> {
324 processor: Processor<P>,
325 audio_param_map: HashMap<String, AudioParamId>,
326 output_channel_count: Vec<usize>,
327
328 inputs_flat: Vec<&'static [f32]>,
330 inputs_grouped: Vec<&'static [&'static [f32]]>,
331 outputs_flat: Vec<&'static mut [f32]>,
332 outputs_grouped: Vec<&'static mut [&'static mut [f32]]>,
333}
334
335unsafe impl<P: AudioWorkletProcessor> Send for AudioWorkletRenderer<P> {}
339
340impl<P: AudioWorkletProcessor> AudioProcessor for AudioWorkletRenderer<P> {
341 fn process(
342 &mut self,
343 inputs: &[AudioRenderQuantum],
344 outputs: &mut [AudioRenderQuantum],
345 params: crate::render::AudioParamValues<'_>,
346 scope: &AudioWorkletGlobalScope,
347 ) -> bool {
348 let processor = self.processor.load();
349
350 inputs
353 .iter()
354 .flat_map(|input| input.channels())
355 .map(|input_channel| input_channel.as_ref())
356 .map(|input_channel| unsafe { std::mem::transmute(input_channel) })
361 .for_each(|c| self.inputs_flat.push(c));
362
363 let mut inputs_flat = &self.inputs_flat[..];
364 for input in inputs {
365 let c = input.number_of_channels();
366 let (left, right) = inputs_flat.split_at(c);
367 #[cfg(feature = "spec-compliant-worklet-inputs")]
368 let left_static = {
369 if input.is_silent() {
376 &[]
377 } else {
378 unsafe { std::mem::transmute::<&[&[f32]], &[&[f32]]>(left) }
380 }
381 };
382
383 #[cfg(not(feature = "spec-compliant-worklet-inputs"))]
384 let left_static = unsafe { std::mem::transmute::<&[&[f32]], &[&[f32]]>(left) };
386
387 self.inputs_grouped.push(left_static);
388 inputs_flat = right;
389 }
390
391 if !outputs.is_empty() && self.output_channel_count.is_empty() {
393 outputs[0].set_number_of_channels(inputs[0].number_of_channels());
395 } else {
396 outputs
397 .iter_mut()
398 .zip(self.output_channel_count.iter())
399 .for_each(|(output, &channel_count)| output.set_number_of_channels(channel_count));
400 }
401
402 let single_case = [inputs
405 .first()
406 .map(|i| i.number_of_channels())
407 .unwrap_or_default()];
408 let output_channel_count = if self.output_channel_count.is_empty() {
409 &single_case[..]
410 } else {
411 &self.output_channel_count[..]
412 };
413
414 outputs
415 .iter_mut()
416 .flat_map(|output| output.channels_mut())
417 .map(|output_channel| output_channel.deref_mut())
418 .map(|output_channel| unsafe { std::mem::transmute(output_channel) })
423 .for_each(|c| self.outputs_flat.push(c));
424
425 if !outputs.is_empty() {
426 let mut outputs_flat = &mut self.outputs_flat[..];
427 for c in output_channel_count {
428 let (left, right) = outputs_flat.split_at_mut(*c);
429 let left_static =
431 unsafe { std::mem::transmute::<&mut [&mut [f32]], &mut [&mut [f32]]>(left) };
432 self.outputs_grouped.push(left_static);
433 outputs_flat = right;
434 }
435 }
436
437 let param_getter = AudioParamValues {
438 values: params,
439 map: &self.audio_param_map,
440 };
441
442 let tail_time = processor.process(
443 &self.inputs_grouped[..],
444 &mut self.outputs_grouped[..],
445 param_getter,
446 scope,
447 );
448
449 self.inputs_grouped.clear();
450 self.inputs_flat.clear();
451 self.outputs_grouped.clear();
452 self.outputs_flat.clear();
453
454 tail_time
455 }
456
457 fn onmessage(&mut self, msg: &mut dyn Any) {
458 self.processor.load().onmessage(msg)
459 }
460
461 fn has_side_effects(&self) -> bool {
462 true }
464}
465
466#[cfg(test)]
467mod tests {
468 use super::*;
469 use crate::context::OfflineAudioContext;
470 use float_eq::assert_float_eq;
471 use std::sync::atomic::{AtomicBool, Ordering};
472 use std::sync::Arc;
473
474 struct TestProcessor;
475
476 impl AudioWorkletProcessor for TestProcessor {
477 type ProcessorOptions = ();
478
479 fn constructor(_opts: Self::ProcessorOptions) -> Self {
480 TestProcessor {}
481 }
482
483 fn process<'a, 'b>(
484 &mut self,
485 _inputs: &'b [&'a [&'a [f32]]],
486 _outputs: &'b mut [&'a mut [&'a mut [f32]]],
487 _params: AudioParamValues<'b>,
488 _scope: &'b AudioWorkletGlobalScope,
489 ) -> bool {
490 true
491 }
492 }
493
494 #[test]
495 fn test_worklet_render() {
496 let mut context = OfflineAudioContext::new(1, 128, 48000.);
497 let options = AudioWorkletNodeOptions::default();
498 let worklet = AudioWorkletNode::new::<TestProcessor>(&context, options);
499 worklet.connect(&context.destination());
500 let buffer = context.start_rendering_sync();
501 assert_float_eq!(
502 buffer.get_channel_data(0)[..],
503 &[0.; 128][..],
504 abs_all <= 0.
505 );
506 }
507
508 #[test]
509 fn test_worklet_inputs_outputs() {
510 let matrix = [0, 1, 2];
511 let mut context = OfflineAudioContext::new(1, 128, 48000.);
512 for inputs in matrix {
513 for outputs in matrix {
514 if inputs == 0 && outputs == 0 {
515 continue; }
517 let options = AudioWorkletNodeOptions {
518 number_of_inputs: inputs,
519 number_of_outputs: outputs,
520 ..AudioWorkletNodeOptions::default()
521 };
522 let worklet = AudioWorkletNode::new::<TestProcessor>(&context, options);
523
524 if outputs > 0 {
525 worklet.connect(&context.destination());
526 }
527 }
528 }
529 let buffer = context.start_rendering_sync();
530 assert_float_eq!(
531 buffer.get_channel_data(0)[..],
532 &[0.; 128][..],
533 abs_all <= 0.
534 );
535 }
536
537 #[test]
538 fn test_worklet_only_input() {
539 struct SetBoolWhenRunProcessor(Arc<AtomicBool>);
540
541 impl AudioWorkletProcessor for SetBoolWhenRunProcessor {
542 type ProcessorOptions = Arc<AtomicBool>;
543
544 fn constructor(opts: Self::ProcessorOptions) -> Self {
545 Self(opts)
546 }
547
548 fn process<'a, 'b>(
549 &mut self,
550 _inputs: &'b [&'a [&'a [f32]]],
551 _outputs: &'b mut [&'a mut [&'a mut [f32]]],
552 _params: AudioParamValues<'b>,
553 _scope: &'b AudioWorkletGlobalScope,
554 ) -> bool {
555 self.0.store(true, Ordering::Relaxed);
556 false
557 }
558 }
559
560 let has_run = Arc::new(AtomicBool::new(false));
561
562 let mut context = OfflineAudioContext::new(1, 128, 48000.);
563 let options = AudioWorkletNodeOptions {
564 number_of_inputs: 1,
565 number_of_outputs: 0,
566 processor_options: Arc::clone(&has_run),
567 ..AudioWorkletNodeOptions::default()
568 };
569 let _ = AudioWorkletNode::new::<SetBoolWhenRunProcessor>(&context, options);
570
571 let _ = context.start_rendering_sync();
572 assert!(has_run.load(Ordering::Relaxed));
573 }
574
575 #[cfg(feature = "spec-compliant-worklet-inputs")]
576 #[test]
577 fn test_worklet_input_not_actively_processing() {
578 use crate::node::AudioScheduledSourceNode;
579 use crate::node::OscillatorNode;
580
581 struct SetBoolWhenInputNotActivelyProcessing(Arc<AtomicBool>);
582
583 impl AudioWorkletProcessor for SetBoolWhenInputNotActivelyProcessing {
584 type ProcessorOptions = Arc<AtomicBool>;
585
586 fn constructor(opts: Self::ProcessorOptions) -> Self {
587 Self(opts)
588 }
589
590 fn process<'a, 'b>(
591 &mut self,
592 inputs: &'b [&'a [&'a [f32]]],
593 _outputs: &'b mut [&'a mut [&'a mut [f32]]],
594 _params: AudioParamValues<'b>,
595 _scope: &'b AudioWorkletGlobalScope,
596 ) -> bool {
597 if inputs[0].is_empty() {
598 self.0.store(true, Ordering::Relaxed);
599 }
600 false
601 }
602 }
603
604 let mark_empty_input = Arc::new(AtomicBool::new(false));
605
606 let mut context = OfflineAudioContext::new(1, 48000, 48000.);
607 let options = AudioWorkletNodeOptions {
608 number_of_inputs: 1,
609 number_of_outputs: 0,
610 processor_options: Arc::clone(&mark_empty_input),
611 ..AudioWorkletNodeOptions::default()
612 };
613 let worklet =
614 AudioWorkletNode::new::<SetBoolWhenInputNotActivelyProcessing>(&context, options);
615 let mut osc = OscillatorNode::new(&context, Default::default());
616 osc.connect(&worklet);
617 osc.start();
618 osc.stop_at(0.1);
622
623 let _ = context.start_rendering_sync();
624 assert!(mark_empty_input.load(Ordering::Relaxed));
625 }
626
627 #[cfg(feature = "spec-compliant-worklet-inputs")]
628 #[test]
629 fn test_worklet_no_input() {
630 struct SetBoolWhenInputNotActivelyProcessing(Arc<AtomicBool>);
631
632 impl AudioWorkletProcessor for SetBoolWhenInputNotActivelyProcessing {
633 type ProcessorOptions = Arc<AtomicBool>;
634
635 fn constructor(opts: Self::ProcessorOptions) -> Self {
636 Self(opts)
637 }
638
639 fn process<'a, 'b>(
640 &mut self,
641 inputs: &'b [&'a [&'a [f32]]],
642 _outputs: &'b mut [&'a mut [&'a mut [f32]]],
643 _params: AudioParamValues<'b>,
644 _scope: &'b AudioWorkletGlobalScope,
645 ) -> bool {
646 if inputs[0].is_empty() {
647 self.0.store(true, Ordering::Relaxed);
648 }
649 false
650 }
651 }
652
653 let mark_empty_input = Arc::new(AtomicBool::new(false));
654
655 let mut context = OfflineAudioContext::new(1, 128, 48000.);
656 let options = AudioWorkletNodeOptions {
657 number_of_inputs: 1,
658 number_of_outputs: 0,
659 processor_options: Arc::clone(&mark_empty_input),
660 ..AudioWorkletNodeOptions::default()
661 };
662 let _ = AudioWorkletNode::new::<SetBoolWhenInputNotActivelyProcessing>(&context, options);
663
664 let _ = context.start_rendering_sync();
665 assert!(mark_empty_input.load(Ordering::Relaxed));
666 }
667
668 #[test]
669 fn test_worklet_output_channel_count() {
670 let mut context = OfflineAudioContext::new(1, 128, 48000.);
671
672 let options1 = AudioWorkletNodeOptions {
673 output_channel_count: vec![],
674 ..AudioWorkletNodeOptions::default()
675 };
676 let worklet1 = AudioWorkletNode::new::<TestProcessor>(&context, options1);
677 worklet1.connect(&context.destination());
678
679 let options2 = AudioWorkletNodeOptions {
680 output_channel_count: vec![1],
681 ..AudioWorkletNodeOptions::default()
682 };
683 let worklet2 = AudioWorkletNode::new::<TestProcessor>(&context, options2);
684 worklet2.connect(&context.destination());
685
686 let options3 = AudioWorkletNodeOptions {
687 number_of_outputs: 2,
688 output_channel_count: vec![1, 2],
689 ..AudioWorkletNodeOptions::default()
690 };
691 let worklet3 = AudioWorkletNode::new::<TestProcessor>(&context, options3);
692 worklet3.connect(&context.destination());
693
694 let buffer = context.start_rendering_sync();
695 assert_float_eq!(
696 buffer.get_channel_data(0)[..],
697 &[0.; 128][..],
698 abs_all <= 0.
699 );
700 }
701
702 #[test]
703 fn send_bound() {
704 #[derive(Default)]
705 struct RcProcessor {
706 _rc: std::rc::Rc<()>, }
708
709 impl AudioWorkletProcessor for RcProcessor {
710 type ProcessorOptions = ();
711
712 fn constructor(_opts: Self::ProcessorOptions) -> Self {
713 Self::default()
714 }
715
716 fn process<'a, 'b>(
717 &mut self,
718 _inputs: &'b [&'a [&'a [f32]]],
719 _outputs: &'b mut [&'a mut [&'a mut [f32]]],
720 _params: AudioParamValues<'b>,
721 _scope: &'b AudioWorkletGlobalScope,
722 ) -> bool {
723 true
724 }
725 }
726
727 let context = OfflineAudioContext::new(1, 128, 48000.);
728 let options = AudioWorkletNodeOptions::default();
729 let _worklet = AudioWorkletNode::new::<RcProcessor>(&context, options);
730 }
731}