PC Music Generator - a Virtual Modular Synthesizer
at main 288 lines 9.3 kB view raw
1use eframe::{ 2 egui::Ui, 3 epaint::{ 4 vec2, 5 Rect, 6 }, 7}; 8 9use egui::{ 10 Context, 11 InnerResponse, 12 Rounding, 13 Stroke, 14}; 15use emath::Pos2; 16use itertools::Itertools; 17use quadtree_rs::{ 18 area::AreaBuilder, 19 point::Point, 20 Quadtree, 21}; 22use slotmap::SecondaryMap; 23use wmidi::MidiMessage; 24 25use crate::{ 26 devices::description::DeviceKind, 27 graph::{ 28 modules::ModuleResponse, 29 Connector, 30 CtlGraph, 31 Graph, 32 InputId, 33 ModuleId, 34 OutputId, 35 }, 36 widgets::connector::draw_catenary, 37 STQueue, 38}; 39 40use self::sizing::*; 41 42pub mod sizing; 43 44#[derive(Clone, Copy)] 45enum ConnAttempt { 46 None, 47 In(InputId), 48 Out(OutputId), 49} 50 51pub struct Stack { 52 pub graph: Graph, 53 end: Option<InputId>, 54 pub events: STQueue<StackResponse>, 55 attempting_connection: ConnAttempt, 56 qt: Quadtree<u8, ModuleId>, 57} 58 59impl Stack { 60 pub fn new(events: STQueue<StackResponse>) -> Self { 61 Self { 62 graph: Default::default(), 63 end: None, 64 events, 65 attempting_connection: ConnAttempt::None, 66 qt: Quadtree::new(3), 67 } 68 } 69 70 pub fn with_module(&mut self, id: ModuleId) -> Option<ModuleId> { 71 let sz = self.graph.modules[id].size; 72 73 let mut ab = AreaBuilder::default(); 74 ab.dimensions(sz.size_in_units()); 75 76 let a = (0..self.qt.width()) 77 .cartesian_product(0..self.qt.height()) 78 .map(|(x, y)| { 79 let (x, y) = (x as _, y as _); 80 ab.anchor(Point { x, y }); 81 ab.build().unwrap() 82 }) 83 .filter(|c| self.qt.query(*c).count() == 0) 84 .min_by(|a, b| { 85 let Point { x: ax, y: ay } = a.anchor(); 86 let Point { x: bx, y: by } = b.anchor(); 87 let (ax, ay, bx, by) = (ax as f32, ay as f32, bx as f32, by as f32); 88 (ax.powi(2) + ay.powi(2)) 89 .sqrt() 90 .total_cmp(&(bx.powi(2) + by.powi(2)).sqrt()) 91 }); 92 93 if let Some(a) = a { 94 let devs = &self.graph.devices; 95 96 for (did, kind) in devs { 97 if matches!(kind, DeviceKind::Output) { 98 self.end = self 99 .graph 100 .dev_ins 101 .get(did) 102 .and_then(|ins| ins.first()) 103 .copied(); 104 } 105 } 106 // TODO: Handle insertion of multiple outputs 107 108 self.qt.insert(a, id); 109 110 None 111 } else { 112 Some(id) 113 } 114 } 115 116 pub fn show(&mut self, ctx: &Context, ui: &mut Ui) { 117 let rect = ui.available_rect_before_wrap(); 118 let top = rect.left_top(); 119 let rects: SecondaryMap<_, _> = self 120 .qt 121 .iter() 122 .map(|e| { 123 let a = e.area(); 124 let Point { x, y } = a.anchor(); 125 let tl = top + vec2(x as f32 * U1_WIDTH, y as f32 * U1_HEIGHT); 126 let sz = vec2(a.width() as f32 * U1_WIDTH, a.height() as f32 * U1_HEIGHT); 127 (*e.value_ref(), Rect::from_min_size(tl, sz)) 128 }) 129 .collect(); 130 131 let mut trigger_rebuild = false; 132 let mut control_change = None; 133 134 for (mid, r) in &rects { 135 let p = ui.painter(); 136 let theme = self.graph[mid].theme; 137 p.rect( 138 *r, 139 Rounding::ZERO, 140 theme.background_color, 141 Stroke { 142 width: 2.0, 143 color: theme.background_accent_color, 144 }, 145 ); 146 147 ui.put(*r, |ui: &mut Ui| { 148 let InnerResponse { inner, response } = self.graph[mid].show(ui); 149 150 match (self.attempting_connection, inner) { 151 (_, ModuleResponse::None) => {} 152 (ConnAttempt::None, ModuleResponse::AttemptConnection(Connector::Out(id))) => { 153 self.attempting_connection = ConnAttempt::Out(id); 154 } 155 (ConnAttempt::None, ModuleResponse::AttemptConnection(Connector::In(id))) => { 156 self.attempting_connection = ConnAttempt::In(id); 157 } 158 (ConnAttempt::In(_), ModuleResponse::AttemptConnection(Connector::In(_))) => {} 159 ( 160 ConnAttempt::In(inid), 161 ModuleResponse::AttemptConnection(Connector::Out(outid)), 162 ) => { 163 self.graph.cables.insert(inid, outid); 164 self.attempting_connection = ConnAttempt::None; 165 trigger_rebuild = true; 166 } 167 ( 168 ConnAttempt::Out(outid), 169 ModuleResponse::AttemptConnection(Connector::In(inid)), 170 ) => { 171 self.graph.cables.insert(inid, outid); 172 self.attempting_connection = ConnAttempt::None; 173 trigger_rebuild = true; 174 } 175 (ConnAttempt::Out(_), ModuleResponse::AttemptConnection(Connector::Out(_))) => { 176 } 177 (_, ModuleResponse::Changed(conn, v)) => control_change = Some((conn, v)), 178 ( 179 ConnAttempt::In(_) | ConnAttempt::Out(_), 180 ModuleResponse::AttemptDisconnect(_), 181 ) => self.attempting_connection = ConnAttempt::None, 182 (_, ModuleResponse::AttemptDisconnect(c)) => { 183 match c { 184 Connector::In(i) => { 185 self.graph.cables.remove(i); 186 } 187 Connector::Out(o) => self.graph.cables.retain(|_, co| *co != o), 188 }; 189 trigger_rebuild = true; 190 } 191 } 192 193 response 194 }); 195 } 196 197 self.draw_wires(&rects, ctx, ui); 198 199 if trigger_rebuild { 200 if let Some(end) = &self.end { 201 self.events 202 .put(StackResponse::Rebuild(self.graph.walk_to(*end))); 203 204 for (mid, _) in rects { 205 let module = &self.graph[mid]; 206 for (knob, &conn) in &module.values { 207 let value = module.visuals[knob].value(); 208 self.events.put(StackResponse::ControlChange(conn, value)); 209 } 210 } 211 } 212 } else if let Some((c, v)) = control_change { 213 self.events.put(StackResponse::ControlChange(c, v)); 214 } 215 } 216 217 fn draw_wires(&mut self, rects: &SecondaryMap<ModuleId, Rect>, ctx: &Context, ui: &mut Ui) { 218 match self.attempting_connection { 219 ConnAttempt::None => {} 220 ConnAttempt::Out(start) => { 221 let start = self.get_output_pos(start, rects); 222 if let Some(end) = ctx.pointer_latest_pos() { 223 draw_catenary(start, end, ui.painter()); 224 } 225 } 226 ConnAttempt::In(start) => { 227 let start = self.get_input_pos(start, rects); 228 if let Some(end) = ctx.pointer_latest_pos() { 229 draw_catenary(start, end, ui.painter()); 230 } 231 } 232 } 233 234 for (inp, &out) in &self.graph.cables { 235 let start = self.get_input_pos(inp, rects); 236 237 let end = self.get_output_pos(out, rects); 238 239 draw_catenary(start, end, ui.painter()); 240 } 241 } 242 243 fn get_output_pos(&self, out: OutputId, rects: &SecondaryMap<ModuleId, Rect>) -> Pos2 { 244 let (mid, module, vid) = self 245 .graph 246 .modules 247 .iter() 248 .find_map(|(mid, m)| m.outs.get(out).map(|o| (mid, m, *o))) 249 .unwrap(); 250 let mod_tl = rects[mid].min; 251 let widget = &module.visuals[vid]; 252 253 mod_tl + (widget.pos().to_vec2() + widget.size() / 2.0) 254 } 255 256 fn get_input_pos(&self, inp: InputId, rects: &SecondaryMap<ModuleId, Rect>) -> Pos2 { 257 let (mid, module, vid) = self 258 .graph 259 .modules 260 .iter() 261 .find_map(|(mid, m)| m.ins.get(inp).map(|o| (mid, m, *o))) 262 .unwrap(); 263 let mod_tl = rects[mid].min; 264 let widget = &module.visuals[vid]; 265 266 mod_tl + (widget.pos().to_vec2() + widget.size() / 2.0) 267 } 268} 269 270pub enum StackResponse { 271 Rebuild(CtlGraph), 272 ControlChange(Connector, f32), 273 MidiChange(STQueue<(u64, MidiMessage<'static>)>), 274} 275 276impl std::fmt::Debug for StackResponse { 277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 278 match self { 279 Self::Rebuild(arg0) => f.debug_tuple("Rebuild").field(arg0).finish(), 280 Self::ControlChange(arg0, arg1) => f 281 .debug_tuple("ControlChange") 282 .field(arg0) 283 .field(arg1) 284 .finish(), 285 Self::MidiChange(_) => f.debug_tuple("MidiChange").finish(), 286 } 287 } 288}