1impl Default for AudioContext {
2 fn default() -> Self {
3 Self::new()
4 }
5}
6
7#[derive(Debug, Clone)]
8struct GraphInner {
9 nodes: Vec<NodeDef>,
10 connections: Vec<NodeConnection>,
11 param_connections: Vec<ParamConnection>,
12 listener: ListenerState,
13 sample_rate: u32,
14 latency_hint: Option<AudioContextLatencyHint>,
15 current_time: f64,
16 state: OfflineAudioContextState,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20struct NodeConnection {
21 source: NodeId,
22 output: usize,
23 destination: NodeId,
24 input: usize,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28struct ParamConnection {
29 source: NodeId,
30 output: usize,
31 destination: ParamId,
32}
33
34impl Default for GraphInner {
35 fn default() -> Self {
36 Self {
37 nodes: Vec::new(),
38 connections: Vec::new(),
39 param_connections: Vec::new(),
40 listener: ListenerState::default(),
41 sample_rate: 44_100,
42 latency_hint: None,
43 current_time: 0.0,
44 state: OfflineAudioContextState::Suspended,
45 }
46 }
47}
48
49#[derive(Debug, Clone, PartialEq)]
50struct ListenerState {
51 position: [ParamTimeline; 3],
52 forward: [ParamTimeline; 3],
53 up: [ParamTimeline; 3],
54}
55
56impl Default for ListenerState {
57 fn default() -> Self {
58 Self {
59 position: [
60 ParamTimeline::new(0.0),
61 ParamTimeline::new(0.0),
62 ParamTimeline::new(0.0),
63 ],
64 forward: [
65 ParamTimeline::new(0.0),
66 ParamTimeline::new(0.0),
67 ParamTimeline::new(-1.0),
68 ],
69 up: [
70 ParamTimeline::new(0.0),
71 ParamTimeline::new(1.0),
72 ParamTimeline::new(0.0),
73 ],
74 }
75 }
76}
77
78impl ListenerState {
79 fn position_value(&self) -> [f32; 3] {
80 self.position_at(0.0)
81 }
82
83 fn position_at(&self, time: f64) -> [f32; 3] {
84 [
85 self.position[0].value_at(time),
86 self.position[1].value_at(time),
87 self.position[2].value_at(time),
88 ]
89 }
90
91 fn forward_value(&self) -> [f32; 3] {
92 self.forward_at(0.0)
93 }
94
95 fn forward_at(&self, time: f64) -> [f32; 3] {
96 [
97 self.forward[0].value_at(time),
98 self.forward[1].value_at(time),
99 self.forward[2].value_at(time),
100 ]
101 }
102
103 fn up_value(&self) -> [f32; 3] {
104 self.up_at(0.0)
105 }
106
107 fn up_at(&self, time: f64) -> [f32; 3] {
108 [
109 self.up[0].value_at(time),
110 self.up[1].value_at(time),
111 self.up[2].value_at(time),
112 ]
113 }
114
115 fn param(&self, param: ParamKind) -> Option<&ParamTimeline> {
116 match param {
117 ParamKind::PositionX => Some(&self.position[0]),
118 ParamKind::PositionY => Some(&self.position[1]),
119 ParamKind::PositionZ => Some(&self.position[2]),
120 ParamKind::ForwardX => Some(&self.forward[0]),
121 ParamKind::ForwardY => Some(&self.forward[1]),
122 ParamKind::ForwardZ => Some(&self.forward[2]),
123 ParamKind::UpX => Some(&self.up[0]),
124 ParamKind::UpY => Some(&self.up[1]),
125 ParamKind::UpZ => Some(&self.up[2]),
126 _ => None,
127 }
128 }
129
130 fn param_mut(&mut self, param: ParamKind) -> Option<&mut ParamTimeline> {
131 match param {
132 ParamKind::PositionX => Some(&mut self.position[0]),
133 ParamKind::PositionY => Some(&mut self.position[1]),
134 ParamKind::PositionZ => Some(&mut self.position[2]),
135 ParamKind::ForwardX => Some(&mut self.forward[0]),
136 ParamKind::ForwardY => Some(&mut self.forward[1]),
137 ParamKind::ForwardZ => Some(&mut self.forward[2]),
138 ParamKind::UpX => Some(&mut self.up[0]),
139 ParamKind::UpY => Some(&mut self.up[1]),
140 ParamKind::UpZ => Some(&mut self.up[2]),
141 _ => None,
142 }
143 }
144}
145
146impl GraphInner {
147 fn validate_node(&self, id: NodeId) -> Result<(), GraphError> {
148 if id.0 < self.nodes.len() {
149 Ok(())
150 } else {
151 Err(GraphError::UnknownNode)
152 }
153 }
154
155 fn param(&self, id: ParamId) -> Option<&ParamTimeline> {
156 if id.node == LISTENER_PARAM_NODE {
157 return self.listener.param(id.param);
158 }
159 self.nodes
160 .get(id.node.0)
161 .and_then(|node| node.kind.param(id.param))
162 }
163
164 fn param_mut(&mut self, id: ParamId) -> Option<&mut ParamTimeline> {
165 if id.node == LISTENER_PARAM_NODE {
166 return self.listener.param_mut(id.param);
167 }
168 self.nodes
169 .get_mut(id.node.0)
170 .and_then(|node| node.kind.param_mut(id.param))
171 }
172
173 fn validate_output_index(&self, source: NodeId, output: usize) -> Result<(), GraphError> {
174 self.validate_node(source)?;
175 let (_, outputs) = self.nodes[source.0].kind.input_output_count();
176 if output < outputs {
177 Ok(())
178 } else {
179 Err(GraphError::InvalidConnectionIndex)
180 }
181 }
182
183 fn validate_input_index(&self, target: NodeId, input: usize) -> Result<(), GraphError> {
184 self.validate_node(target)?;
185 let (inputs, _) = self.nodes[target.0].kind.input_output_count();
186 if input < inputs {
187 Ok(())
188 } else {
189 Err(GraphError::InvalidConnectionIndex)
190 }
191 }
192
193 fn connect_nodes(&mut self, source: NodeId, target: NodeId) -> Result<(), GraphError> {
194 self.connect_nodes_with_indices(source, 0, target, 0)
195 }
196
197 fn connect_nodes_with_indices(
198 &mut self,
199 source: NodeId,
200 output: usize,
201 target: NodeId,
202 input: usize,
203 ) -> Result<(), GraphError> {
204 self.validate_output_index(source, output)?;
205 self.validate_input_index(target, input)?;
206 if (self.has_path(target, source) || source == target)
207 && !self.cycle_contains_delay(source, target)
208 {
209 return Err(GraphError::Cycle);
210 }
211 let connection = NodeConnection {
212 source,
213 output,
214 destination: target,
215 input,
216 };
217 if !self.connections.contains(&connection) {
218 self.connections.push(connection);
219 }
220 Ok(())
221 }
222
223 fn disconnect_nodes(&mut self, source: NodeId, target: NodeId) -> Result<(), GraphError> {
224 self.disconnect_nodes_with_indices(source, 0, target, 0)
225 }
226
227 fn disconnect_nodes_with_indices(
228 &mut self,
229 source: NodeId,
230 output: usize,
231 target: NodeId,
232 input: usize,
233 ) -> Result<(), GraphError> {
234 self.validate_output_index(source, output)?;
235 self.validate_input_index(target, input)?;
236 self.connections.retain(|connection| {
237 *connection
238 != NodeConnection {
239 source,
240 output,
241 destination: target,
242 input,
243 }
244 });
245 Ok(())
246 }
247
248 fn connect_param_node(&mut self, source: NodeId, target: ParamId) -> Result<(), GraphError> {
249 self.connect_param_node_from_output(source, 0, target)
250 }
251
252 fn connect_param_node_from_output(
253 &mut self,
254 source: NodeId,
255 output: usize,
256 target: ParamId,
257 ) -> Result<(), GraphError> {
258 self.validate_output_index(source, output)?;
259 self.validate_node(target.node)?;
260 let connection = ParamConnection {
261 source,
262 output,
263 destination: target,
264 };
265 if !self.param_connections.contains(&connection) {
266 self.param_connections.push(connection);
267 }
268 Ok(())
269 }
270
271 fn disconnect_param_node(&mut self, source: NodeId, target: ParamId) -> Result<(), GraphError> {
272 self.disconnect_param_node_from_output(source, 0, target)
273 }
274
275 fn disconnect_param_node_from_output(
276 &mut self,
277 source: NodeId,
278 output: usize,
279 target: ParamId,
280 ) -> Result<(), GraphError> {
281 self.validate_output_index(source, output)?;
282 self.validate_node(target.node)?;
283 self.param_connections.retain(|connection| {
284 *connection
285 != ParamConnection {
286 source,
287 output,
288 destination: target,
289 }
290 });
291 Ok(())
292 }
293
294 fn has_path(&self, from: NodeId, to: NodeId) -> bool {
295 let mut queue = VecDeque::from([from]);
296 let mut seen = vec![false; self.nodes.len()];
297 while let Some(node) = queue.pop_front() {
298 if node == to {
299 return true;
300 }
301 if seen[node.0] {
302 continue;
303 }
304 seen[node.0] = true;
305 for target in self
306 .connections
307 .iter()
308 .filter(|connection| connection.source == node)
309 .map(|connection| connection.destination)
310 {
311 queue.push_back(target);
312 }
313 for target in self
314 .param_connections
315 .iter()
316 .filter(|connection| connection.source == node)
317 .map(|connection| connection.destination)
318 {
319 queue.push_back(target.node);
320 }
321 }
322 false
323 }
324
325 fn cycle_contains_delay(&self, source: NodeId, target: NodeId) -> bool {
326 if self.is_delay_node(source) || self.is_delay_node(target) {
327 return true;
328 }
329 let mut queue = VecDeque::from([target]);
330 let mut seen = vec![false; self.nodes.len()];
331 while let Some(node) = queue.pop_front() {
332 if seen[node.0] {
333 continue;
334 }
335 seen[node.0] = true;
336 if self.is_delay_node(node) {
337 return true;
338 }
339 for next in self
340 .connections
341 .iter()
342 .filter(|connection| connection.source == node)
343 .map(|connection| connection.destination)
344 {
345 queue.push_back(next);
346 }
347 for next in self
348 .param_connections
349 .iter()
350 .filter(|connection| connection.source == node)
351 .map(|connection| connection.destination.node)
352 {
353 if next != LISTENER_PARAM_NODE {
354 queue.push_back(next);
355 }
356 }
357 }
358 false
359 }
360
361 fn is_delay_node(&self, id: NodeId) -> bool {
362 self.nodes
363 .get(id.0)
364 .is_some_and(|node| matches!(node.kind, NodeKind::Delay { .. }))
365 }
366
367 fn compile(&self) -> Result<CompiledGraph, GraphError> {
368 let mut indegree = vec![0usize; self.nodes.len()];
369 for connection in &self.connections {
370 if self.is_delay_node(connection.destination) {
371 continue;
372 }
373 indegree[connection.destination.0] += 1;
374 }
375 for connection in &self.param_connections {
376 indegree[connection.destination.node.0] += 1;
377 }
378 let mut queue = VecDeque::new();
379 for (index, degree) in indegree.iter().enumerate() {
380 if *degree == 0 {
381 queue.push_back(NodeId(index));
382 }
383 }
384 let mut order = Vec::with_capacity(self.nodes.len());
385 while let Some(node) = queue.pop_front() {
386 order.push(node);
387 for target in self
388 .connections
389 .iter()
390 .filter(|connection| connection.source == node)
391 .map(|connection| connection.destination)
392 {
393 if self.is_delay_node(target) {
394 continue;
395 }
396 indegree[target.0] -= 1;
397 if indegree[target.0] == 0 {
398 queue.push_back(target);
399 }
400 }
401 for target in self
402 .param_connections
403 .iter()
404 .filter(|connection| connection.source == node)
405 .map(|connection| connection.destination)
406 {
407 indegree[target.node.0] -= 1;
408 if indegree[target.node.0] == 0 {
409 queue.push_back(target.node);
410 }
411 }
412 }
413 if order.len() != self.nodes.len() {
414 return Err(GraphError::Cycle);
415 }
416 let mut inbound_connections = vec![Vec::new(); self.nodes.len()];
417 for connection in &self.connections {
418 if let Some(inbound) = inbound_connections.get_mut(connection.destination.0) {
419 inbound.push(*connection);
420 }
421 }
422 Ok(CompiledGraph {
423 nodes: self.nodes.clone(),
424 connections: self.connections.clone(),
425 inbound_connections,
426 param_connections: self.param_connections.clone(),
427 delay_cycle_nodes: self.delay_cycle_nodes(),
428 order,
429 sample_voice: self.compiled_sample_voice(),
430 listener: self.listener.clone(),
431 sample_rate: self.sample_rate,
432 })
433 }
434
435 fn compiled_sample_voice(&self) -> Option<CompiledSampleVoice> {
436 if !self.param_connections.is_empty()
437 || self.nodes.len() != 5
438 || self.connections.len() != 4
439 {
440 return None;
441 }
442
443 let source = self.nodes.iter().enumerate().find_map(|(index, node)| {
444 matches!(node.kind, NodeKind::AudioBufferSource { .. }).then_some(NodeId(index))
445 })?;
446 let pan = self.nodes.iter().enumerate().find_map(|(index, node)| {
447 matches!(node.kind, NodeKind::StereoPanner { .. }).then_some(NodeId(index))
448 })?;
449 let gains = self
450 .nodes
451 .iter()
452 .enumerate()
453 .filter_map(|(index, node)| {
454 matches!(node.kind, NodeKind::Gain { .. }).then_some(NodeId(index))
455 })
456 .collect::<Vec<_>>();
457 if gains.len() != 2 {
458 return None;
459 }
460
461 let destination = NodeId(0);
462 if !self
463 .connections
464 .iter()
465 .any(|connection| connection.source == pan && connection.destination == destination)
466 {
467 return None;
468 }
469 let channel_gain = gains.iter().copied().find(|gain| {
470 self.connections
471 .iter()
472 .any(|connection| connection.source == *gain && connection.destination == pan)
473 })?;
474 let envelope_gain = gains.iter().copied().find(|gain| {
475 *gain != channel_gain
476 && self.connections.iter().any(|connection| {
477 connection.source == *gain && connection.destination == channel_gain
478 })
479 })?;
480 if !self.connections.iter().any(|connection| {
481 connection.source == source && connection.destination == envelope_gain
482 }) {
483 return None;
484 }
485
486 Some(CompiledSampleVoice {
487 source,
488 envelope_gain,
489 channel_gain,
490 pan,
491 })
492 }
493
494 fn delay_cycle_nodes(&self) -> Vec<bool> {
495 self.nodes
496 .iter()
497 .enumerate()
498 .map(|(index, node)| {
499 let delay = NodeId(index);
500 matches!(node.kind, NodeKind::Delay { .. })
501 && (self.connections.iter().any(|connection| {
502 connection.source == delay
503 && (connection.destination == delay
504 || self.has_path(connection.destination, delay))
505 }) || self.param_connections.iter().any(|connection| {
506 connection.source == delay
507 && self.has_path(connection.destination.node, delay)
508 }))
509 })
510 .collect()
511 }
512}
513
514#[derive(Debug, Clone)]
515struct NodeDef {
516 kind: NodeKind,
517 channel_config: ChannelConfig,
518 label: Option<String>,
519}
520
521impl NodeDef {
522 fn new(kind: NodeKind) -> Self {
523 Self {
524 kind,
525 channel_config: ChannelConfig::default(),
526 label: None,
527 }
528 }
529
530 fn destination(channel_count: usize) -> Self {
531 let mut node = Self::new(NodeKind::Destination { channel_count });
532 node.channel_config = ChannelConfig {
533 channel_count,
534 channel_count_mode: ChannelCountMode::Explicit,
535 channel_interpretation: ChannelInterpretation::Speakers,
536 };
537 node
538 }
539
540 fn oscillator(waveform: Waveform) -> Self {
541 Self::new(NodeKind::Oscillator {
542 waveform,
543 frequency: ParamTimeline::new(440.0),
544 detune: ParamTimeline::new(0.0)
545 .with_nominal_range(-DETUNE_NOMINAL_LIMIT, DETUNE_NOMINAL_LIMIT),
546 periodic_wave: None,
547 start_time: 0.0,
548 stop_time: None,
549 start_scheduled: false,
550 stop_scheduled: false,
551 ended: Arc::new(AtomicBool::new(false)),
552 })
553 }
554
555 fn constant(value: f32) -> Self {
556 Self::new(NodeKind::Constant {
557 offset: ParamTimeline::new(value),
558 start_time: 0.0,
559 stop_time: None,
560 start_scheduled: false,
561 stop_scheduled: false,
562 ended: Arc::new(AtomicBool::new(false)),
563 })
564 }
565
566 fn gain() -> Self {
567 Self::new(NodeKind::Gain {
568 gain: ParamTimeline::new(1.0),
569 })
570 }
571
572 fn fixed_clamped_max(kind: NodeKind) -> Self {
573 let mut node = Self::new(kind);
574 node.channel_config = ChannelConfig {
575 channel_count: 2,
576 channel_count_mode: ChannelCountMode::ClampedMax,
577 channel_interpretation: ChannelInterpretation::Speakers,
578 };
579 node
580 }
581
582 fn channel_splitter(outputs: usize) -> Self {
583 let mut node = Self::new(NodeKind::ChannelSplitter { outputs });
584 node.channel_config = ChannelConfig {
585 channel_count: outputs,
586 channel_count_mode: ChannelCountMode::Explicit,
587 channel_interpretation: ChannelInterpretation::Discrete,
588 };
589 node
590 }
591
592 fn channel_merger(inputs: usize) -> Self {
593 let mut node = Self::new(NodeKind::ChannelMerger { inputs });
594 node.channel_config = ChannelConfig {
595 channel_count: 1,
596 channel_count_mode: ChannelCountMode::Explicit,
597 channel_interpretation: ChannelInterpretation::Speakers,
598 };
599 node
600 }
601
602 fn info(&self) -> AudioNodeInfo {
603 let (number_of_inputs, number_of_outputs) = self.kind.input_output_count();
604 AudioNodeInfo {
605 number_of_inputs,
606 number_of_outputs,
607 channel_count: self.channel_config.channel_count,
608 channel_count_mode: self.channel_config.channel_count_mode,
609 channel_interpretation: self.channel_config.channel_interpretation,
610 }
611 }
612}
613
614#[derive(Debug, Clone)]
615enum NodeKind {
616 Destination {
617 channel_count: usize,
618 },
619 Oscillator {
620 waveform: Waveform,
621 frequency: ParamTimeline,
622 detune: ParamTimeline,
623 periodic_wave: Option<PeriodicWave>,
624 start_time: f64,
625 stop_time: Option<f64>,
626 start_scheduled: bool,
627 stop_scheduled: bool,
628 ended: Arc<AtomicBool>,
629 },
630 Constant {
631 offset: ParamTimeline,
632 start_time: f64,
633 stop_time: Option<f64>,
634 start_scheduled: bool,
635 stop_scheduled: bool,
636 ended: Arc<AtomicBool>,
637 },
638 Gain {
639 gain: ParamTimeline,
640 },
641 AudioBufferSource {
642 buffer: Option<AudioBuffer>,
643 buffer_assigned: bool,
644 acquired_buffer: Option<AudioBuffer>,
645 playback_rate: ParamTimeline,
646 detune: ParamTimeline,
647 looping: bool,
648 loop_range: Option<(f64, f64)>,
649 start_time: f64,
650 stop_time: Option<f64>,
651 start_scheduled: bool,
652 stop_scheduled: bool,
653 ended: Arc<AtomicBool>,
654 offset: f64,
655 duration: Option<f64>,
656 },
657 ExternalSound {
658 data: ExternalSoundDataNode,
659 start_time: f64,
660 stop_time: Option<f64>,
661 start_scheduled: bool,
662 stop_scheduled: bool,
663 ended: Arc<AtomicBool>,
664 },
665 StereoPanner {
666 pan: ParamTimeline,
667 },
668 BiquadFilter {
669 kind: BiquadFilterType,
670 frequency: ParamTimeline,
671 detune: ParamTimeline,
672 q: ParamTimeline,
673 gain: ParamTimeline,
674 },
675 IirFilter {
676 feedforward: Vec<f32>,
677 feedback: Vec<f32>,
678 },
679 Delay {
680 delay_time: ParamTimeline,
681 max_delay_time: Option<f32>,
682 },
683 WaveShaper {
684 curve: Option<Vec<f32>>,
685 oversample: Oversample,
686 },
687 DynamicsCompressor {
688 threshold: ParamTimeline,
689 knee: ParamTimeline,
690 ratio: ParamTimeline,
691 attack: ParamTimeline,
692 release: ParamTimeline,
693 reduction: Arc<AtomicU32>,
694 },
695 Convolver {
696 buffer: Option<AudioBuffer>,
697 normalize: bool,
698 buffer_normalize: bool,
699 },
700 Analyser {
701 state: Arc<Mutex<AnalyserState>>,
702 },
703 Panner {
704 position_x: ParamTimeline,
705 position_y: ParamTimeline,
706 position_z: ParamTimeline,
707 orientation_x: ParamTimeline,
708 orientation_y: ParamTimeline,
709 orientation_z: ParamTimeline,
710 panning_model: PanningModel,
711 distance_model: DistanceModel,
712 ref_distance: f32,
713 max_distance: f32,
714 rolloff_factor: f32,
715 cone_inner_angle: f32,
716 cone_outer_angle: f32,
717 cone_outer_gain: f32,
718 },
719 ChannelSplitter {
720 outputs: usize,
721 },
722 ChannelMerger {
723 inputs: usize,
724 },
725 AudioWorklet {
726 inputs: usize,
727 outputs: usize,
728 output_channel_count: Option<Vec<usize>>,
729 parameters: Vec<(String, ParamTimeline)>,
730 processor_options: HashMap<String, String>,
731 processor: AudioWorkletProcessorNode,
732 },
733}
734
735impl NodeKind {
736 fn input_output_count(&self) -> (usize, usize) {
737 match self {
738 Self::Destination { .. } => (1, 0),
739 Self::Oscillator { .. }
740 | Self::Constant { .. }
741 | Self::AudioBufferSource { .. }
742 | Self::ExternalSound { .. } => (0, 1),
743 Self::ChannelSplitter { outputs } => (1, *outputs),
744 Self::ChannelMerger { inputs } => (*inputs, 1),
745 Self::AudioWorklet {
746 inputs, outputs, ..
747 } => (*inputs, *outputs),
748 Self::Gain { .. }
749 | Self::StereoPanner { .. }
750 | Self::BiquadFilter { .. }
751 | Self::IirFilter { .. }
752 | Self::Delay { .. }
753 | Self::WaveShaper { .. }
754 | Self::DynamicsCompressor { .. }
755 | Self::Convolver { .. }
756 | Self::Analyser { .. }
757 | Self::Panner { .. } => (1, 1),
758 }
759 }
760
761 fn param(&self, param: ParamKind) -> Option<&ParamTimeline> {
762 match (self, param) {
763 (Self::Gain { gain }, ParamKind::Gain)
764 | (
765 Self::Oscillator {
766 frequency: gain, ..
767 },
768 ParamKind::Frequency,
769 )
770 | (Self::Oscillator { detune: gain, .. }, ParamKind::Detune)
771 | (Self::Constant { offset: gain, .. }, ParamKind::Offset)
772 | (
773 Self::AudioBufferSource {
774 playback_rate: gain,
775 ..
776 },
777 ParamKind::PlaybackRate,
778 )
779 | (Self::AudioBufferSource { detune: gain, .. }, ParamKind::Detune)
780 | (Self::StereoPanner { pan: gain }, ParamKind::Pan)
781 | (
782 Self::BiquadFilter {
783 frequency: gain, ..
784 },
785 ParamKind::Frequency,
786 )
787 | (Self::BiquadFilter { detune: gain, .. }, ParamKind::Detune)
788 | (Self::BiquadFilter { q: gain, .. }, ParamKind::Q)
789 | (Self::BiquadFilter { gain, .. }, ParamKind::FilterGain)
790 | (
791 Self::Delay {
792 delay_time: gain, ..
793 },
794 ParamKind::DelayTime,
795 )
796 | (
797 Self::DynamicsCompressor {
798 threshold: gain, ..
799 },
800 ParamKind::Threshold,
801 )
802 | (Self::DynamicsCompressor { knee: gain, .. }, ParamKind::Knee)
803 | (Self::DynamicsCompressor { ratio: gain, .. }, ParamKind::Ratio)
804 | (Self::DynamicsCompressor { attack: gain, .. }, ParamKind::Attack)
805 | (Self::DynamicsCompressor { release: gain, .. }, ParamKind::Release)
806 | (
807 Self::Panner {
808 position_x: gain, ..
809 },
810 ParamKind::PositionX,
811 )
812 | (
813 Self::Panner {
814 position_y: gain, ..
815 },
816 ParamKind::PositionY,
817 )
818 | (
819 Self::Panner {
820 position_z: gain, ..
821 },
822 ParamKind::PositionZ,
823 )
824 | (
825 Self::Panner {
826 orientation_x: gain,
827 ..
828 },
829 ParamKind::OrientationX,
830 )
831 | (
832 Self::Panner {
833 orientation_y: gain,
834 ..
835 },
836 ParamKind::OrientationY,
837 )
838 | (
839 Self::Panner {
840 orientation_z: gain,
841 ..
842 },
843 ParamKind::OrientationZ,
844 ) => Some(gain),
845 (Self::AudioWorklet { parameters, .. }, ParamKind::WorkletParam(index)) => {
846 parameters.get(index).map(|(_, param)| param)
847 }
848 _ => None,
849 }
850 }
851
852 fn param_mut(&mut self, param: ParamKind) -> Option<&mut ParamTimeline> {
853 match (self, param) {
854 (Self::Gain { gain }, ParamKind::Gain)
855 | (
856 Self::Oscillator {
857 frequency: gain, ..
858 },
859 ParamKind::Frequency,
860 )
861 | (Self::Oscillator { detune: gain, .. }, ParamKind::Detune)
862 | (Self::Constant { offset: gain, .. }, ParamKind::Offset)
863 | (
864 Self::AudioBufferSource {
865 playback_rate: gain,
866 ..
867 },
868 ParamKind::PlaybackRate,
869 )
870 | (Self::AudioBufferSource { detune: gain, .. }, ParamKind::Detune)
871 | (Self::StereoPanner { pan: gain }, ParamKind::Pan)
872 | (
873 Self::BiquadFilter {
874 frequency: gain, ..
875 },
876 ParamKind::Frequency,
877 )
878 | (Self::BiquadFilter { detune: gain, .. }, ParamKind::Detune)
879 | (Self::BiquadFilter { q: gain, .. }, ParamKind::Q)
880 | (Self::BiquadFilter { gain, .. }, ParamKind::FilterGain)
881 | (
882 Self::Delay {
883 delay_time: gain, ..
884 },
885 ParamKind::DelayTime,
886 )
887 | (
888 Self::DynamicsCompressor {
889 threshold: gain, ..
890 },
891 ParamKind::Threshold,
892 )
893 | (Self::DynamicsCompressor { knee: gain, .. }, ParamKind::Knee)
894 | (Self::DynamicsCompressor { ratio: gain, .. }, ParamKind::Ratio)
895 | (Self::DynamicsCompressor { attack: gain, .. }, ParamKind::Attack)
896 | (Self::DynamicsCompressor { release: gain, .. }, ParamKind::Release)
897 | (
898 Self::Panner {
899 position_x: gain, ..
900 },
901 ParamKind::PositionX,
902 )
903 | (
904 Self::Panner {
905 position_y: gain, ..
906 },
907 ParamKind::PositionY,
908 )
909 | (
910 Self::Panner {
911 position_z: gain, ..
912 },
913 ParamKind::PositionZ,
914 )
915 | (
916 Self::Panner {
917 orientation_x: gain,
918 ..
919 },
920 ParamKind::OrientationX,
921 )
922 | (
923 Self::Panner {
924 orientation_y: gain,
925 ..
926 },
927 ParamKind::OrientationY,
928 )
929 | (
930 Self::Panner {
931 orientation_z: gain,
932 ..
933 },
934 ParamKind::OrientationZ,
935 ) => Some(gain),
936 (Self::AudioWorklet { parameters, .. }, ParamKind::WorkletParam(index)) => {
937 parameters.get_mut(index).map(|(_, param)| param)
938 }
939 _ => None,
940 }
941 }
942}
943
944fn validate_channel_config_for_node(
945 kind: &NodeKind,
946 channel_count: usize,
947 channel_count_mode: ChannelCountMode,
948 channel_interpretation: ChannelInterpretation,
949) -> Result<(), GraphError> {
950 match kind {
951 NodeKind::Destination {
952 channel_count: destination_channel_count,
953 } => {
954 if channel_count != *destination_channel_count
955 || channel_count_mode != ChannelCountMode::Explicit
956 || channel_interpretation != ChannelInterpretation::Speakers
957 {
958 return Err(GraphError::InvalidChannelCount);
959 }
960 }
961 NodeKind::Convolver { .. }
962 | NodeKind::DynamicsCompressor { .. }
963 | NodeKind::Panner { .. }
964 | NodeKind::StereoPanner { .. } => {
965 if channel_count > 2 || channel_count_mode == ChannelCountMode::Max {
966 return Err(GraphError::InvalidChannelCount);
967 }
968 }
969 NodeKind::ChannelSplitter { outputs } => {
970 if channel_count != *outputs {
971 return Err(GraphError::InvalidChannelCount);
972 }
973 if channel_count_mode != ChannelCountMode::Explicit
974 || channel_interpretation != ChannelInterpretation::Discrete
975 {
976 return Err(GraphError::InvalidChannelCount);
977 }
978 }
979 NodeKind::ChannelMerger { .. }
980 if channel_count != 1 || channel_count_mode != ChannelCountMode::Explicit =>
981 {
982 return Err(GraphError::InvalidChannelCount);
983 }
984 _ => {}
985 }
986 Ok(())
987}
988
989#[derive(Clone)]
990struct ExternalSoundDataNode {
991 data: Arc<Mutex<Option<Box<dyn ErasedSoundData>>>>,
992}
993
994impl ExternalSoundDataNode {
995 fn new<D>(data: D) -> Self
996 where
997 D: SoundData + Send + 'static,
998 D::Error: fmt::Debug + Send + Sync + 'static,
999 {
1000 Self {
1001 data: Arc::new(Mutex::new(Some(Box::new(TypedExternalSoundData {
1002 data: Some(data),
1003 })))),
1004 }
1005 }
1006
1007 fn take_sound(&self) -> Result<Box<dyn Sound>, GraphError> {
1008 let Some(mut data) = self
1009 .data
1010 .lock()
1011 .expect("external sound mutex poisoned")
1012 .take()
1013 else {
1014 return Err(GraphError::ExternalSound(
1015 "external sound data was already consumed".to_string(),
1016 ));
1017 };
1018 data.take_sound()
1019 }
1020}
1021
1022impl fmt::Debug for ExternalSoundDataNode {
1023 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1024 f.write_str("ExternalSoundDataNode")
1025 }
1026}
1027
1028trait ErasedSoundData: Send {
1029 fn take_sound(&mut self) -> Result<Box<dyn Sound>, GraphError>;
1030}
1031
1032struct TypedExternalSoundData<D> {
1033 data: Option<D>,
1034}
1035
1036impl<D> ErasedSoundData for TypedExternalSoundData<D>
1037where
1038 D: SoundData + Send + 'static,
1039 D::Error: fmt::Debug + Send + Sync + 'static,
1040{
1041 fn take_sound(&mut self) -> Result<Box<dyn Sound>, GraphError> {
1042 let Some(data) = self.data.take() else {
1043 return Err(GraphError::ExternalSound(
1044 "external sound data was already consumed".to_string(),
1045 ));
1046 };
1047 let (sound, _) = data
1048 .into_sound()
1049 .map_err(|error| GraphError::ExternalSound(format!("{error:?}")))?;
1050 Ok(sound)
1051 }
1052}
1053
1054#[derive(Debug, Clone, PartialEq, Eq)]
1055pub enum GraphError {
1056 UnknownNode,
1057 UnknownParam,
1058 WrongContext,
1059 InvalidChannel,
1060 InvalidChannelCount,
1061 InvalidAudioBuffer,
1062 InvalidNodeLabel,
1063 InvalidConnectionIndex,
1064 InvalidFrequencyResponse,
1065 InvalidIirFilter,
1066 InvalidPeriodicWave,
1067 InvalidWaveShaperCurve,
1068 InvalidConvolverBuffer,
1069 InvalidAnalyserConfig,
1070 InvalidDelayTime,
1071 InvalidPannerConfig,
1072 UnsupportedPanningModel,
1073 InvalidLoopRange,
1074 InvalidAudioWorkletOptions,
1075 Cycle,
1076 ContextClosed,
1077 InvalidState,
1078 NegativeTime,
1079 SourceAlreadyStarted,
1080 SourceNotStarted,
1081 SourceAlreadyStopped,
1082 StopBeforeStart,
1083 InvalidAutomationValue,
1084 InvalidAutomationRate,
1085 ExternalSound(String),
1086}
1087
1088impl fmt::Display for GraphError {
1089 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1090 match self {
1091 Self::UnknownNode => f.write_str("unknown graph node"),
1092 Self::UnknownParam => f.write_str("unknown graph parameter"),
1093 Self::WrongContext => f.write_str("node belongs to a different audio context"),
1094 Self::InvalidChannel => f.write_str("invalid audio buffer channel"),
1095 Self::InvalidChannelCount => f.write_str("invalid audio node channel count"),
1096 Self::InvalidAudioBuffer => f.write_str("invalid audio buffer arguments"),
1097 Self::InvalidNodeLabel => f.write_str("invalid audio node label"),
1098 Self::InvalidConnectionIndex => f.write_str("invalid audio connection index"),
1099 Self::InvalidFrequencyResponse => {
1100 f.write_str("invalid frequency response buffer lengths")
1101 }
1102 Self::InvalidIirFilter => f.write_str("invalid IIR filter coefficients"),
1103 Self::InvalidPeriodicWave => f.write_str("invalid periodic wave coefficients"),
1104 Self::InvalidWaveShaperCurve => f.write_str("invalid wave shaper curve"),
1105 Self::InvalidConvolverBuffer => f.write_str("invalid convolver buffer"),
1106 Self::InvalidAnalyserConfig => f.write_str("invalid analyser configuration"),
1107 Self::InvalidDelayTime => f.write_str("invalid delay max delay time"),
1108 Self::InvalidPannerConfig => f.write_str("invalid panner configuration"),
1109 Self::UnsupportedPanningModel => {
1110 f.write_str("unsupported panning model: HRTF is not implemented")
1111 }
1112 Self::InvalidLoopRange => f.write_str("invalid audio buffer source loop range"),
1113 Self::InvalidAudioWorkletOptions => f.write_str("invalid audio worklet options"),
1114 Self::Cycle => f.write_str("graph connection would create a cycle"),
1115 Self::ContextClosed => f.write_str("audio context is closed"),
1116 Self::InvalidState => f.write_str("invalid audio context state"),
1117 Self::NegativeTime => f.write_str("scheduled source time cannot be negative"),
1118 Self::SourceAlreadyStarted => f.write_str("source node was already started"),
1119 Self::SourceNotStarted => f.write_str("source node has not been started"),
1120 Self::SourceAlreadyStopped => f.write_str("source node was already stopped"),
1121 Self::StopBeforeStart => f.write_str("source stop time is before start time"),
1122 Self::InvalidAutomationValue => f.write_str("invalid audio parameter automation value"),
1123 Self::InvalidAutomationRate => f.write_str("invalid audio parameter automation rate"),
1124 Self::ExternalSound(error) => write!(f, "external sound source failed: {error}"),
1125 }
1126 }
1127}
1128
1129impl std::error::Error for GraphError {}