devalang_wasm/engine/audio/interpreter/
audio_graph.rs

1/// Audio Graph - Representation of routing and mixing graph
2use crate::language::syntax::ast::Value;
3use std::collections::HashMap;
4
5/// Configuration of a node in the audio graph
6#[derive(Debug, Clone)]
7pub struct Node {
8    pub name: String,
9    pub alias: Option<String>,
10    pub effects: Option<Value>, // Effect chain to apply to this node
11}
12
13/// A connection/route between two nodes
14#[derive(Debug, Clone)]
15pub enum Connection {
16    /// Simple route: mix source to destination with gain
17    Route {
18        source: String,
19        destination: String,
20        gain: f32,
21    },
22    /// Duck: compress source based on destination envelope
23    Duck {
24        source: String,
25        destination: String,
26        effect_params: Value, // Compressor parameters
27    },
28    /// Sidechain: apply effect to source driven by destination
29    Sidechain {
30        source: String,
31        destination: String,
32        effect_params: Value, // Gate or other effect parameters
33    },
34}
35
36/// The complete audio graph
37#[derive(Debug, Clone)]
38pub struct AudioGraph {
39    /// All nodes (channels/tracks)
40    pub nodes: HashMap<String, Node>,
41    /// All connections between nodes
42    pub connections: Vec<Connection>,
43    /// Master node (final mixer) - always "$master"
44    pub master_node: String,
45}
46
47impl AudioGraph {
48    pub fn new() -> Self {
49        let mut nodes = HashMap::new();
50
51        // Create default master node
52        nodes.insert(
53            "$master".to_string(),
54            Node {
55                name: "$master".to_string(),
56                alias: None,
57                effects: None,
58            },
59        );
60
61        Self {
62            nodes,
63            connections: Vec::new(),
64            master_node: "$master".to_string(),
65        }
66    }
67
68    /// Add a node to the graph
69    pub fn add_node(&mut self, name: String, alias: Option<String>, effects: Option<Value>) {
70        self.nodes.insert(
71            name.clone(),
72            Node {
73                name,
74                alias,
75                effects,
76            },
77        );
78    }
79
80    /// Add a connection to the graph
81    pub fn add_connection(&mut self, connection: Connection) {
82        self.connections.push(connection);
83    }
84
85    /// Build the graph from routing configuration
86    pub fn from_routing_setup(
87        routing_setup: &crate::engine::audio::interpreter::driver::RoutingSetup,
88    ) -> Self {
89        let mut graph = AudioGraph::new();
90
91        // Add all nodes
92        for (name, node_config) in &routing_setup.nodes {
93            graph.add_node(
94                name.clone(),
95                node_config.alias.clone(),
96                node_config.effects.clone(),
97            );
98        }
99
100        // Add routes
101        for route in &routing_setup.routes {
102            let gain = if let Some(Value::Map(effects_map)) = &route.effects {
103                if let Some(Value::Map(volume_map)) = effects_map.get("volume") {
104                    if let Some(Value::Number(g)) = volume_map.get("gain") {
105                        *g
106                    } else {
107                        1.0
108                    }
109                } else if let Some(Value::Number(g)) = effects_map.get("volume") {
110                    *g
111                } else {
112                    1.0
113                }
114            } else {
115                1.0
116            };
117
118            graph.add_connection(Connection::Route {
119                source: route.source.clone(),
120                destination: route.destination.clone(),
121                gain,
122            });
123        }
124
125        // Add ducks
126        for duck in &routing_setup.ducks {
127            graph.add_connection(Connection::Duck {
128                source: duck.source.clone(),
129                destination: duck.destination.clone(),
130                effect_params: duck.effect.clone(),
131            });
132        }
133
134        // Add sidechains
135        for sidechain in &routing_setup.sidechains {
136            graph.add_connection(Connection::Sidechain {
137                source: sidechain.source.clone(),
138                destination: sidechain.destination.clone(),
139                effect_params: sidechain.effect.clone(),
140            });
141        }
142
143        graph
144    }
145
146    /// Get outgoing routes from a node
147    pub fn get_outgoing_routes(&self, node_name: &str) -> Vec<&Connection> {
148        self.connections
149            .iter()
150            .filter(|conn| match conn {
151                Connection::Route { source, .. } => source == node_name,
152                _ => false,
153            })
154            .collect()
155    }
156
157    /// Get ducks affecting a node
158    pub fn get_incoming_ducks(&self, node_name: &str) -> Vec<&Connection> {
159        self.connections
160            .iter()
161            .filter(|conn| match conn {
162                Connection::Duck { source, .. } if source == node_name => true,
163                _ => false,
164            })
165            .collect()
166    }
167
168    /// Get sidechains affecting a node
169    pub fn get_incoming_sidechains(&self, node_name: &str) -> Vec<&Connection> {
170        self.connections
171            .iter()
172            .filter(|conn| match conn {
173                Connection::Sidechain { source, .. } if source == node_name => true,
174                _ => false,
175            })
176            .collect()
177    }
178
179    /// Check if a node exists
180    pub fn has_node(&self, node_name: &str) -> bool {
181        self.nodes.contains_key(node_name)
182    }
183
184    /// Get all node names
185    pub fn node_names(&self) -> Vec<String> {
186        self.nodes.keys().cloned().collect()
187    }
188
189    /// Print debug information about the audio graph
190    #[allow(dead_code)]
191    pub fn debug_print(&self) {
192        // This method is kept for debugging purposes but not called in normal operation
193    }
194}
195
196impl Default for AudioGraph {
197    fn default() -> Self {
198        Self::new()
199    }
200}