PC Music Generator - a Virtual Modular Synthesizer
1use std::{
2 collections::BTreeMap,
3 ops::{
4 Index,
5 IndexMut,
6 },
7};
8
9use slotmap::{
10 new_key_type,
11 SecondaryMap,
12 SlotMap,
13};
14
15use crate::devices::description::DeviceKind;
16
17use self::modules::Module;
18
19pub mod compiled;
20pub mod modules;
21
22new_key_type! {
23 pub struct ModuleId;
24 pub struct DeviceId;
25 pub struct VisualId;
26
27 pub struct InputId;
28 pub struct OutputId;
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
32pub enum Connector {
33 /// Connected to an input of a device
34 In(InputId),
35 /// Connected to an output of a device
36 Out(OutputId),
37}
38
39#[derive(Default, Debug)]
40pub struct Graph {
41 pub modules: SlotMap<ModuleId, Module>,
42 pub devices: SlotMap<DeviceId, DeviceKind>,
43
44 /// What device a given input belongs o
45 pub ins: SlotMap<InputId, (DeviceId, u8)>,
46 /// What device a given output belongs to
47 pub outs: SlotMap<OutputId, (DeviceId, u8)>,
48 /// What ins a given device has
49 pub dev_ins: SecondaryMap<DeviceId, Vec<InputId>>,
50 /// What outs a given device has
51 pub dev_outs: SecondaryMap<DeviceId, Vec<OutputId>>,
52 /// Connections linking node's inputs back to outputs
53 pub cables: SecondaryMap<InputId, OutputId>,
54}
55
56type CtlGraphGraph = BTreeMap<DeviceId, (DeviceKind, [Option<(DeviceId, u8)>; 16])>;
57
58#[derive(Debug, Default)]
59pub struct CtlGraph {
60 pub end: DeviceId,
61 pub dev_map: BTreeMap<Connector, (DeviceId, u8)>,
62 pub midis: SecondaryMap<OutputId, (DeviceId, u8)>,
63 graph: CtlGraphGraph,
64}
65
66struct Walker {
67 dev_map: BTreeMap<Connector, (DeviceId, u8)>,
68 midis: SecondaryMap<OutputId, (DeviceId, u8)>,
69 /// from node closer to output backwards
70 graph: CtlGraphGraph,
71}
72
73impl Walker {
74 fn walk(to: InputId, graph: &Graph) -> CtlGraph {
75 let end = graph.ins[to].0;
76 let mut this = Self {
77 dev_map: Default::default(),
78 midis: Default::default(),
79 graph: Default::default(),
80 };
81
82 this.walk_input(to, graph);
83
84 let Walker {
85 dev_map,
86 midis,
87 graph,
88 } = this;
89
90 CtlGraph {
91 end,
92 dev_map,
93 midis,
94 graph,
95 }
96 }
97
98 fn walk_input(&mut self, input: InputId, graph: &Graph) {
99 let (dev, param) = graph[input];
100 let dev_desc = graph.devices[dev];
101 println!("Walking input {param} for {dev_desc:?}");
102
103 self.dev_map.insert(Connector::In(input), (dev, param));
104
105 let prev = graph.cables.get(input).copied().map(|out| graph[out]);
106 if let Some((prev_dev, _)) = prev {
107 // for each of this device's previous devices' outputs
108 let prevs = graph
109 .dev_outs
110 .get(prev_dev)
111 .map(|outs| &**outs)
112 .unwrap_or(&[])
113 .iter()
114 .copied()
115 .map(|out| (out, graph.outs.get(out).copied()));
116
117 for (output, _) in prevs {
118 let r = self.walk_output(output, graph);
119
120 let (_, params) = self.graph.entry(dev).or_insert((dev_desc, [None; 16]));
121 params[param as usize] = Some(r);
122 }
123 }
124 }
125
126 fn walk_output(&mut self, output: OutputId, graph: &Graph) -> (DeviceId, u8) {
127 let (dev, param) = graph[output];
128 let dev_desc = graph.devices[dev];
129 println!("Walking output {param} for {dev_desc:?}");
130
131 if matches!(dev_desc, DeviceKind::MidiControl) {
132 self.midis.insert(output, (dev, param));
133 }
134 let (_, _) = self.graph.entry(dev).or_insert((dev_desc, [None; 16]));
135
136 self.dev_map.insert(Connector::Out(output), (dev, param));
137
138 // for each of this device's inputs
139 let prevs = graph
140 .dev_ins
141 .get(dev)
142 .map(|ins| &**ins)
143 .unwrap_or(&[])
144 .iter()
145 .copied();
146
147 for input in prevs {
148 self.walk_input(input, graph);
149 }
150 (dev, param)
151 }
152}
153
154impl Graph {
155 pub fn new() -> Self {
156 Default::default()
157 }
158
159 pub fn remove_module(&mut self, mid: ModuleId) {
160 let (mut removed_ins, mut removed_outs) = (Vec::new(), Vec::new());
161
162 let module = self.modules.remove(mid).unwrap();
163 for did in module.devices {
164 self.ins.retain(|i, (dev, _)| {
165 if *dev != did {
166 removed_ins.push(i);
167
168 true
169 } else {
170 false
171 }
172 });
173 self.outs.retain(|o, (dev, _)| {
174 if *dev != did {
175 removed_outs.push(o);
176
177 true
178 } else {
179 false
180 }
181 });
182 }
183 for i in removed_ins {
184 self.cables.remove(i);
185 }
186
187 for o in removed_outs {
188 self.cables.retain(|_, co| o != *co);
189 }
190 }
191
192 pub fn walk_to(&self, end: InputId) -> CtlGraph {
193 Walker::walk(end, self)
194 }
195}
196
197impl Index<InputId> for Graph {
198 type Output = (DeviceId, u8);
199
200 fn index(&self, index: InputId) -> &Self::Output {
201 &self.ins[index]
202 }
203}
204
205impl Index<OutputId> for Graph {
206 type Output = (DeviceId, u8);
207
208 fn index(&self, index: OutputId) -> &Self::Output {
209 &self.outs[index]
210 }
211}
212
213impl Index<ModuleId> for Graph {
214 type Output = Module;
215
216 fn index(&self, index: ModuleId) -> &Self::Output {
217 self.modules.get(index).unwrap()
218 }
219}
220
221impl IndexMut<ModuleId> for Graph {
222 fn index_mut(&mut self, index: ModuleId) -> &mut Self::Output {
223 self.modules.get_mut(index).unwrap()
224 }
225}