1use crate::nodes::NodeType;
2use crate::types::{AudioUnit, empty_audio_unit};
3use crossbeam_channel::Receiver;
4use std::collections::HashMap;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct NodeId(pub Uuid);
10
11impl Default for NodeId {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl NodeId {
18 pub fn new() -> Self {
19 Self(Uuid::new_v4())
20 }
21}
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum NodeParameter {
26 Gain(f32),
28 Frequency(f32),
30 Switch(bool),
32 DelayUnits(usize),
34 Cutoff(f32),
36 Q(f32),
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
42pub enum ControlMessage {
43 SetParameter(NodeId, NodeParameter),
45}
46
47pub struct GraphBuilder {
52 nodes: Vec<NodeType>,
53 edges: Vec<Vec<usize>>,
55 feedback_edges: Vec<(usize, usize)>,
57 id_to_index: HashMap<NodeId, usize>,
58}
59
60impl Default for GraphBuilder {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl GraphBuilder {
67 pub fn new() -> Self {
68 Self {
69 nodes: Vec::new(),
70 edges: Vec::new(),
71 feedback_edges: Vec::new(),
72 id_to_index: HashMap::new(),
73 }
74 }
75
76 pub fn add_node(&mut self, node: NodeType) -> NodeId {
78 let index = self.nodes.len();
79 self.nodes.push(node);
80 self.edges.push(Vec::new()); let id = NodeId::new();
82 self.id_to_index.insert(id, index);
83 id
84 }
85
86 pub fn connect(&mut self, source: NodeId, destination: NodeId) {
88 if let (Some(&src_idx), Some(&dest_idx)) = (
89 self.id_to_index.get(&source),
90 self.id_to_index.get(&destination),
91 ) {
92 self.edges[src_idx].push(dest_idx);
93 }
94 }
95
96 pub fn connect_feedback(&mut self, source: NodeId, destination: NodeId) {
101 if let (Some(&src_idx), Some(&dest_idx)) = (
102 self.id_to_index.get(&source),
103 self.id_to_index.get(&destination),
104 ) {
105 self.feedback_edges.push((src_idx, dest_idx));
106 }
107 }
108
109 pub fn build(
111 self,
112 destination_id: NodeId,
113 msg_receiver: Receiver<ControlMessage>,
114 ) -> StaticGraph {
115 let mut pet_graph = petgraph::graph::DiGraph::<(), ()>::new();
117 let mut pet_indices = Vec::with_capacity(self.nodes.len());
118
119 for _ in 0..self.nodes.len() {
120 pet_indices.push(pet_graph.add_node(()));
121 }
122
123 for (src, targets) in self.edges.iter().enumerate() {
124 for &dest in targets {
125 pet_graph.add_edge(pet_indices[src], pet_indices[dest], ());
126 }
127 }
128 let sorted_pet_indices = petgraph::algo::toposort(&pet_graph, None)
131 .expect("Audio graph contains a cycle! Use connect_feedback() for feedback loops.");
132
133 let sorted_indices: Vec<usize> = sorted_pet_indices
134 .into_iter()
135 .map(|idx| idx.index())
136 .collect();
137 let final_dest_idx = self.id_to_index[&destination_id];
138
139 let mut last_usage = vec![0; self.nodes.len()];
142 for (exec_idx, &node_idx) in sorted_indices.iter().enumerate() {
143 let mut last_used_at = exec_idx;
144 for &dest_idx in &self.edges[node_idx] {
145 let dest_exec_idx = sorted_indices.iter().position(|&x| x == dest_idx).unwrap();
146 last_used_at = last_used_at.max(dest_exec_idx);
147 }
148 if node_idx == final_dest_idx {
149 last_used_at = usize::MAX; }
151 for &(fb_src, _) in &self.feedback_edges {
153 if fb_src == node_idx {
154 last_used_at = usize::MAX;
155 }
156 }
157 last_usage[node_idx] = last_used_at;
158 }
159
160 let mut buffer_assignment = vec![0; self.nodes.len()];
161 let mut buffer_free_list = Vec::new();
162 let mut next_buffer_id = 0;
163 let mut active_nodes = Vec::new();
164
165 for (exec_idx, &node_idx) in sorted_indices.iter().enumerate() {
167 let assigned_buffer = if let Some(buf_id) = buffer_free_list.pop() {
169 buf_id
170 } else {
171 let id = next_buffer_id;
172 next_buffer_id += 1;
173 id
174 };
175 buffer_assignment[node_idx] = assigned_buffer;
176 active_nodes.push(node_idx);
177
178 active_nodes.retain(|&active_node| {
180 if last_usage[active_node] == exec_idx {
181 buffer_free_list.push(buffer_assignment[active_node]);
182 false
183 } else {
184 true
185 }
186 });
187 }
188
189 let buffers_count = next_buffer_id;
190 let buffers = vec![empty_audio_unit(); buffers_count];
191
192 let mut inputs_map = vec![Vec::new(); self.nodes.len()];
194 for (src_idx, targets) in self.edges.iter().enumerate() {
195 for &dest_idx in targets {
196 inputs_map[dest_idx].push(src_idx);
197 }
198 }
199
200 let mut feedback_inputs_map = vec![Vec::new(); self.nodes.len()];
202 for &(src_idx, dest_idx) in &self.feedback_edges {
203 feedback_inputs_map[dest_idx].push(src_idx);
204 }
205
206 let mut feedback_source_set = vec![false; self.nodes.len()];
210 for &(src_idx, _) in &self.feedback_edges {
211 feedback_source_set[src_idx] = true;
212 }
213 let prev_frame_buffers: Vec<Option<AudioUnit>> = feedback_source_set
214 .iter()
215 .map(|&is_fb| {
216 if is_fb {
217 Some(empty_audio_unit())
218 } else {
219 None
220 }
221 })
222 .collect();
223
224 StaticGraph {
225 nodes: self.nodes,
226 node_buffers: buffers,
227 buffer_assignment,
228 inputs_map,
229 feedback_inputs_map,
230 prev_frame_buffers,
231 final_destination_index: final_dest_idx,
232 msg_receiver,
233 id_to_index: self.id_to_index,
234 execution_order: sorted_indices,
235 }
236 }
237}
238
239pub struct StaticGraph {
241 nodes: Vec<NodeType>,
242 node_buffers: Vec<AudioUnit>,
244 buffer_assignment: Vec<usize>,
246 inputs_map: Vec<Vec<usize>>,
248 feedback_inputs_map: Vec<Vec<usize>>,
250 prev_frame_buffers: Vec<Option<AudioUnit>>,
252 final_destination_index: usize,
253 msg_receiver: Receiver<ControlMessage>,
254 id_to_index: HashMap<NodeId, usize>,
255 execution_order: Vec<usize>,
257}
258
259impl StaticGraph {
260 #[inline(always)]
262 pub fn pull_next_unit(&mut self) -> &AudioUnit {
263 while let Ok(msg) = self.msg_receiver.try_recv() {
265 self.handle_message(msg);
266 }
267
268 for &i in &self.execution_order {
271 let mut combined_input = empty_audio_unit();
273 let sources = &self.inputs_map[i];
274 let feedback_sources = &self.feedback_inputs_map[i];
275
276 let has_input = if sources.is_empty() && feedback_sources.is_empty() {
277 false
278 } else {
279 for &src_idx in sources {
281 let src_buf_idx = self.buffer_assignment[src_idx];
282 let src_buf = &self.node_buffers[src_buf_idx];
283 dasp::slice::add_in_place(&mut combined_input[..], &src_buf[..]);
284 }
285 for &src_idx in feedback_sources {
287 if let Some(ref prev_buf) = self.prev_frame_buffers[src_idx] {
288 dasp::slice::add_in_place(&mut combined_input[..], &prev_buf[..]);
289 }
290 }
291 true
292 };
293
294 let input_ref = if has_input {
296 Some(&combined_input)
297 } else {
298 None
299 };
300 let output_buf_idx = self.buffer_assignment[i];
301 let output_ref = &mut self.node_buffers[output_buf_idx];
302
303 self.nodes[i].process(input_ref, output_ref);
304 }
305
306 for (node_idx, prev_buf) in self.prev_frame_buffers.iter_mut().enumerate() {
308 if let Some(buf) = prev_buf {
309 let src_buf_idx = self.buffer_assignment[node_idx];
310 buf.copy_from_slice(&self.node_buffers[src_buf_idx]);
311 }
312 }
313
314 let final_buf_idx = self.buffer_assignment[self.final_destination_index];
316 &self.node_buffers[final_buf_idx]
317 }
318
319 fn handle_message(&mut self, msg: ControlMessage) {
320 match msg {
321 ControlMessage::SetParameter(node_id, parameter) => {
322 if let Some(&index) = self.id_to_index.get(&node_id)
323 && let Some(node) = self.nodes.get_mut(index)
324 {
325 match (node, parameter) {
327 (NodeType::Gain(g), NodeParameter::Gain(val)) => g.set_gain(val),
328 (NodeType::Oscillator(o), NodeParameter::Gain(val)) => o.set_gain(val),
329 (NodeType::Mixer(m), NodeParameter::Gain(val)) => m.set_gain(val),
330 (NodeType::Delay(d), NodeParameter::DelayUnits(val)) => {
331 d.set_delay_units(val)
332 }
333 (NodeType::Filter(f), NodeParameter::Cutoff(val)) => f.set_cutoff(val),
334 (NodeType::Filter(f), NodeParameter::Q(val)) => f.set_q(val),
335 _ => {}
339 }
340 }
341 }
342 }
343 }
344}