1use hashbrown::HashMap;
4
5use crate::dsl::*;
6use crate::PedalProcessor;
7
8use super::compile::compile_pedal;
9use super::compiled::CompiledPedal;
10
11fn pin_node_name(pin: &Pin) -> String {
13 match pin {
14 Pin::Reserved(n) => n.clone(),
15 Pin::ComponentPin { component, .. } => component.clone(),
16 Pin::Fork { switch, .. } => switch.clone(),
18 Pin::SubcircuitPort { subcircuit, .. } => subcircuit.clone(),
20 }
21}
22
23pub fn split_pedal_def(pedal: &PedalDef) -> Option<(PedalDef, PedalDef)> {
32 if !pedal.has_fx_loop() {
33 return None;
34 }
35
36 let mut component_nodes: HashMap<String, Vec<String>> = HashMap::new();
38 for net in &pedal.nets {
39 let node_name = pin_node_name(&net.from);
40 for pin in &net.to {
41 let to_name = pin_node_name(pin);
42 component_nodes
43 .entry(node_name.clone())
44 .or_default()
45 .push(to_name.clone());
46 component_nodes
47 .entry(to_name)
48 .or_default()
49 .push(node_name.clone());
50 }
51 }
52
53 let pre_components = bfs_collect_components(&component_nodes, "in", "fx_send");
56 let post_components = bfs_collect_components(&component_nodes, "fx_return", "out");
57
58 let pre_def = build_half(pedal, &pre_components, "fx_send", "out", "pre");
60 let post_def = build_half(pedal, &post_components, "fx_return", "in", "post");
62
63 Some((pre_def, post_def))
64}
65
66fn bfs_collect_components(
69 adj: &HashMap<String, Vec<String>>,
70 start_node: &str,
71 stop_node: &str,
72) -> std::collections::HashSet<String> {
73 use std::collections::{HashSet, VecDeque};
74 let mut visited = HashSet::new();
75 let mut queue = VecDeque::new();
76 queue.push_back(start_node.to_string());
77 visited.insert(start_node.to_string());
78 visited.insert("gnd".to_string());
80 visited.insert("vcc".to_string());
81
82 while let Some(node) = queue.pop_front() {
83 if node == stop_node {
84 continue;
86 }
87 if let Some(neighbors) = adj.get(&node) {
88 for nb in neighbors {
89 if !visited.contains(nb) {
90 visited.insert(nb.clone());
91 queue.push_back(nb.clone());
92 }
93 }
94 }
95 }
96 visited
97}
98
99fn build_half(
101 pedal: &PedalDef,
102 component_ids: &std::collections::HashSet<String>,
103 old_node: &str,
104 new_node: &str,
105 suffix: &str,
106) -> PedalDef {
107 let components: Vec<ComponentDef> = pedal
109 .components
110 .iter()
111 .filter(|c| component_ids.contains(&c.id))
112 .cloned()
113 .collect();
114
115 let rename = |pin: &Pin| -> Pin {
117 match pin {
118 Pin::Reserved(n) if n == old_node => Pin::Reserved(new_node.to_string()),
119 Pin::Reserved(n) if (n == "fx_send" || n == "fx_return") && n != old_node => {
121 pin.clone() }
123 _ => pin.clone(),
124 }
125 };
126
127 let nets: Vec<NetDef> = pedal
128 .nets
129 .iter()
130 .filter_map(|net| {
131 let from = rename(&net.from);
132 let to: Vec<Pin> = net.to.iter().map(&rename).collect();
133
134 let relevant = |p: &Pin| match p {
137 Pin::Reserved(n) => {
138 n == "in" || n == "out" || n == "gnd" || n == "vcc" || n == new_node
139 || pedal.is_supply_rail(n)
140 }
141 Pin::ComponentPin { component, .. } => component_ids.contains(component),
142 Pin::Fork { switch, destinations } => {
143 component_ids.contains(switch)
144 || destinations.iter().any(|d| match d {
145 Pin::ComponentPin { component, .. } => component_ids.contains(component),
146 _ => false,
147 })
148 }
149 Pin::SubcircuitPort { .. } => false,
150 };
151
152 let belongs = |p: &Pin| match p {
154 Pin::Reserved(_) => true,
155 Pin::ComponentPin { component, .. } => component_ids.contains(component),
156 Pin::Fork { switch, .. } => component_ids.contains(switch),
157 Pin::SubcircuitPort { .. } => false,
158 };
159
160 let all_pins = std::iter::once(&from).chain(to.iter());
163 let has_local_component = all_pins.clone().any(|p| {
164 matches!(p, Pin::ComponentPin { component, .. } if component_ids.contains(component))
165 });
166
167 if has_local_component {
168 let filtered_to: Vec<Pin> = to.into_iter().filter(|p| belongs(p)).collect();
169 if !filtered_to.is_empty() && relevant(&from) {
170 Some(NetDef {
171 from,
172 to: filtered_to,
173 })
174 } else {
175 None
176 }
177 } else {
178 None
179 }
180 })
181 .collect();
182
183 let controls: Vec<ControlDef> = pedal
185 .controls
186 .iter()
187 .filter(|c| component_ids.contains(&c.component))
188 .cloned()
189 .collect();
190
191 let trims: Vec<ControlDef> = pedal
193 .trims
194 .iter()
195 .filter(|c| component_ids.contains(&c.component))
196 .cloned()
197 .collect();
198
199 let monitors: Vec<MonitorDef> = pedal
200 .monitors
201 .iter()
202 .filter(|m| {
203 component_ids.contains(&m.component)
204 || m.component == "input"
205 || m.component == "output"
206 })
207 .cloned()
208 .collect();
209
210 let mirrors = pedal
212 .mirrors
213 .iter()
214 .filter(|(k, v)| component_ids.contains(*k) && component_ids.contains(*v))
215 .map(|(k, v)| (k.clone(), v.clone()))
216 .collect();
217
218 PedalDef {
219 name: format!("{} ({})", pedal.name, suffix),
220 subtitle: None,
221 supplies: pedal.supplies.clone(),
222 components,
223 nets,
224 controls,
225 trims,
226 monitors,
227 sidechains: vec![],
228 mirrors,
229 calibrate: false,
230 subcircuits: vec![],
231 ports: vec![],
232 init_hints: vec![],
233 uses: vec![],
234 }
235}
236
237pub struct SplitCompiledPedal {
239 pub pre: CompiledPedal,
240 pub post: CompiledPedal,
241}
242
243impl SplitCompiledPedal {
244 pub fn process(&mut self, input: f64) -> f64 {
246 let send = self.pre.process(input as crate::Wave);
247 self.post.process(send) as f64
248 }
249
250 pub fn process_pre(&mut self, input: f64) -> f64 {
252 self.pre.process(input as crate::Wave) as f64
253 }
254
255 pub fn process_post(&mut self, fx_return: f64) -> f64 {
257 self.post.process(fx_return as crate::Wave) as f64
258 }
259
260 pub fn set_sample_rate(&mut self, rate: f64) {
261 self.pre.set_sample_rate(rate as crate::Wave);
262 self.post.set_sample_rate(rate as crate::Wave);
263 }
264
265 pub fn reset(&mut self) {
266 self.pre.reset();
267 self.post.reset();
268 }
269
270 pub fn set_control(&mut self, label: &str, value: f64) {
271 self.pre.set_control(label, value as crate::Wave);
274 self.post.set_control(label, value as crate::Wave);
275 }
276
277 pub fn input_impedance(&self) -> f64 {
281 self.pre.input_impedance() as f64
282 }
283
284 pub fn output_impedance(&self) -> f64 {
288 self.post.output_impedance() as f64
289 }
290}
291
292pub fn compile_split_pedal(
297 pedal: &PedalDef,
298 sample_rate: f64,
299) -> Result<SplitCompiledPedal, String> {
300 let (pre_def, post_def) =
301 split_pedal_def(pedal).ok_or_else(|| "Pedal has no fx_send/fx_return nodes".to_string())?;
302
303 let pre = compile_pedal(&pre_def, sample_rate)
304 .map_err(|e| format!("Failed to compile pre-section: {e}"))?;
305 let post = compile_pedal(&post_def, sample_rate)
306 .map_err(|e| format!("Failed to compile post-section: {e}"))?;
307
308 Ok(SplitCompiledPedal { pre, post })
309}