Skip to main content

melody_bay/render/
graph_state.rs

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 {}