Skip to main content

pedalkernel/compiler/components/
delay.rs

1//! Delay component structs: Bbd, DelayLine, Tap.
2
3use crate::compiler::component::{
4    Component, ComponentEdge, ControlParam, ControlParamKind, EdgeKind, GraphRole, ModulationSink,
5    ModulationSinkKind, PinConfig, StampResult,
6};
7use crate::dsl::{BbdType, SpringTankType};
8use crate::elements::{Interpolation, Medium};
9use crate::tree::MnaSystem;
10
11use super::impl_component_dyn;
12
13// ═══════════════════════════════════════════════════════════════════════════
14// Bbd
15// ═══════════════════════════════════════════════════════════════════════════
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct Bbd {
19    pub bbd_type: BbdType,
20}
21
22impl Component for Bbd {
23    impl_component_dyn!();
24
25    fn type_tag(&self) -> &'static str {
26        "BBD delay"
27    }
28
29    fn is_passive(&self) -> bool {
30        false
31    }
32
33    fn pin_config(&self) -> PinConfig {
34        PinConfig {
35            valid_pins: &["in", "input", "out", "output", "clock"],
36            aliases: &[("in", "input"), ("out", "output")],
37        }
38    }
39
40    fn modulation_pins(&self) -> &'static [&'static str] {
41        &["clock"]
42    }
43
44    fn graph_role(&self) -> GraphRole {
45        GraphRole::Virtual
46    }
47
48    fn stamp_mna(
49        &self,
50        _comp_id: &str,
51        _n1: Option<usize>,
52        _n2: Option<usize>,
53        _mna: &mut MnaSystem,
54        _sample_rate: f64,
55    ) -> StampResult {
56        StampResult::Skip
57    }
58
59    fn edges(&self) -> Vec<ComponentEdge> {
60        vec![ComponentEdge {
61            pin_a: "input",
62            pin_b: "output",
63            kind: EdgeKind::Behavioral,
64            port_group: None,
65        }]
66    }
67
68    fn controls(&self) -> Vec<ControlParam> {
69        vec![
70            ControlParam {
71                name: "clock",
72                kind: ControlParamKind::BbdClockRate,
73            },
74            ControlParam {
75                name: "feedback",
76                kind: ControlParamKind::BbdFeedback,
77            },
78        ]
79    }
80
81    fn modulation_sink(&self, pin: &str) -> Option<ModulationSink> {
82        match pin {
83            "clock" => Some(ModulationSink {
84                target_kind: ModulationSinkKind::BbdClock,
85                bias: 0.15,
86                range: 0.10,
87            }),
88            _ => None,
89        }
90    }
91
92    fn footprint_ref(&self) -> (&'static str, &'static str) {
93        match self.bbd_type {
94            BbdType::Mn3207 => ("Analog_Delay:MN3207", "IC"),
95            BbdType::Mn3007 => ("Analog_Delay:MN3007", "IC"),
96            BbdType::Mn3005 => ("Analog_Delay:MN3005", "IC"),
97        }
98    }
99
100    fn symbol_name(&self) -> &'static str {
101        "ic_chip"
102    }
103    fn layout_class(&self) -> &'static str {
104        "bbd"
105    }
106
107    fn signal_adjacencies(&self) -> Vec<(&'static str, &'static str)> {
108        vec![
109            ("in", "out"),
110            ("input", "output"),
111            ("in", "input"),
112            ("out", "output"),
113        ]
114    }
115}
116
117// ═══════════════════════════════════════════════════════════════════════════
118// Spring (spring reverb tank — behavioral island)
119// ═══════════════════════════════════════════════════════════════════════════
120
121/// Spring-reverb tank: a `GraphRole::Virtual` behavioral island (like `Bbd`),
122/// contributing one `Behavioral` in→out edge and `StampResult::Skip`. The
123/// electromechanical spring pair is modeled by the runtime `SpringTank`; the
124/// driver/recovery circuit stays OUTSIDE the island (real WDF stages).
125#[derive(Debug, Clone, PartialEq)]
126pub struct Spring {
127    pub tank_type: SpringTankType,
128}
129
130impl Component for Spring {
131    impl_component_dyn!();
132
133    fn type_tag(&self) -> &'static str {
134        "spring reverb"
135    }
136
137    fn is_passive(&self) -> bool {
138        false
139    }
140
141    fn pin_config(&self) -> PinConfig {
142        PinConfig {
143            valid_pins: &[
144                "in", "input", "out", "output", "dwell", "decay", "damping", "mix",
145            ],
146            aliases: &[("in", "input"), ("out", "output")],
147        }
148    }
149
150    fn modulation_pins(&self) -> &'static [&'static str] {
151        &["dwell"]
152    }
153
154    fn graph_role(&self) -> GraphRole {
155        GraphRole::Virtual
156    }
157
158    fn stamp_mna(
159        &self,
160        _comp_id: &str,
161        _n1: Option<usize>,
162        _n2: Option<usize>,
163        _mna: &mut MnaSystem,
164        _sample_rate: f64,
165    ) -> StampResult {
166        StampResult::Skip
167    }
168
169    fn edges(&self) -> Vec<ComponentEdge> {
170        vec![ComponentEdge {
171            pin_a: "input",
172            pin_b: "output",
173            kind: EdgeKind::Behavioral,
174            port_group: None,
175        }]
176    }
177
178    fn controls(&self) -> Vec<ControlParam> {
179        vec![
180            ControlParam {
181                name: "dwell",
182                kind: ControlParamKind::SpringDwell,
183            },
184            ControlParam {
185                name: "decay",
186                kind: ControlParamKind::SpringDecay,
187            },
188            ControlParam {
189                name: "damping",
190                kind: ControlParamKind::SpringDamping,
191            },
192            ControlParam {
193                name: "mix",
194                kind: ControlParamKind::SpringMix,
195            },
196        ]
197    }
198
199    fn modulation_sink(&self, pin: &str) -> Option<ModulationSink> {
200        match pin {
201            "dwell" => Some(ModulationSink {
202                target_kind: ModulationSinkKind::SpringDwell,
203                bias: 0.5,
204                range: 0.5,
205            }),
206            _ => None,
207        }
208    }
209
210    fn footprint_ref(&self) -> (&'static str, &'static str) {
211        match self.tank_type {
212            SpringTankType::Type4 => ("Reverb:Accutronics_Type4", "RV"),
213            SpringTankType::Type8 => ("Reverb:Accutronics_Type8", "RV"),
214            SpringTankType::Type9 => ("Reverb:Accutronics_Type9", "RV"),
215            SpringTankType::Re201Tank => ("Reverb:RE201_Tank", "RV"),
216        }
217    }
218
219    fn symbol_name(&self) -> &'static str {
220        "spring"
221    }
222    fn layout_class(&self) -> &'static str {
223        "spring"
224    }
225
226    fn signal_adjacencies(&self) -> Vec<(&'static str, &'static str)> {
227        vec![
228            ("in", "out"),
229            ("input", "output"),
230            ("in", "input"),
231            ("out", "output"),
232        ]
233    }
234}
235
236// ═══════════════════════════════════════════════════════════════════════════
237// DelayLineComp
238// ═══════════════════════════════════════════════════════════════════════════
239
240#[derive(Debug, Clone, PartialEq)]
241pub struct DelayLineComp {
242    pub min_delay: f64,
243    pub max_delay: f64,
244    pub interpolation: Interpolation,
245    pub medium: Medium,
246}
247
248impl Component for DelayLineComp {
249    impl_component_dyn!();
250
251    fn type_tag(&self) -> &'static str {
252        "delay line"
253    }
254
255    fn is_passive(&self) -> bool {
256        false
257    }
258
259    fn pin_config(&self) -> PinConfig {
260        PinConfig {
261            valid_pins: &[
262                "input",
263                "in",
264                "output",
265                "out",
266                "rate",
267                "speed_mod",
268                "delay_time",
269                "feedback",
270            ],
271            aliases: &[("in", "input"), ("out", "output")],
272        }
273    }
274
275    fn modulation_pins(&self) -> &'static [&'static str] {
276        &["speed_mod", "delay_time"]
277    }
278
279    fn graph_role(&self) -> GraphRole {
280        GraphRole::Virtual
281    }
282
283    fn stamp_mna(
284        &self,
285        _comp_id: &str,
286        _n1: Option<usize>,
287        _n2: Option<usize>,
288        _mna: &mut MnaSystem,
289        _sample_rate: f64,
290    ) -> StampResult {
291        StampResult::Skip
292    }
293
294    fn edges(&self) -> Vec<ComponentEdge> {
295        vec![ComponentEdge {
296            pin_a: "input",
297            pin_b: "output",
298            kind: EdgeKind::Behavioral,
299            port_group: None,
300        }]
301    }
302
303    fn controls(&self) -> Vec<ControlParam> {
304        vec![
305            ControlParam {
306                name: "delay_time",
307                kind: ControlParamKind::DelayTime,
308            },
309            ControlParam {
310                name: "feedback",
311                kind: ControlParamKind::DelayFeedback,
312            },
313        ]
314    }
315
316    fn modulation_sink(&self, pin: &str) -> Option<ModulationSink> {
317        match pin {
318            "speed_mod" => Some(ModulationSink {
319                target_kind: ModulationSinkKind::DelaySpeed,
320                bias: 0.0,
321                range: 0.02,
322            }),
323            "delay_time" => Some(ModulationSink {
324                target_kind: ModulationSinkKind::DelayTime,
325                bias: 0.5,
326                range: 0.5,
327            }),
328            _ => None,
329        }
330    }
331
332    fn footprint_ref(&self) -> (&'static str, &'static str) {
333        ("", "DL")
334    }
335
336    fn symbol_name(&self) -> &'static str {
337        "delay"
338    }
339    fn layout_class(&self) -> &'static str {
340        "delay"
341    }
342
343    fn signal_adjacencies(&self) -> Vec<(&'static str, &'static str)> {
344        vec![
345            ("in", "out"),
346            ("input", "output"),
347            ("in", "input"),
348            ("out", "output"),
349        ]
350    }
351}
352
353// ═══════════════════════════════════════════════════════════════════════════
354// Tap
355// ═══════════════════════════════════════════════════════════════════════════
356
357#[derive(Debug, Clone, PartialEq)]
358pub struct Tap {
359    pub parent_id: String,
360    pub ratio: f64,
361}
362
363impl Component for Tap {
364    impl_component_dyn!();
365
366    fn type_tag(&self) -> &'static str {
367        "tap"
368    }
369
370    fn is_passive(&self) -> bool {
371        false
372    }
373
374    fn pin_config(&self) -> PinConfig {
375        PinConfig {
376            valid_pins: &["output", "out"],
377            aliases: &[("out", "output")],
378        }
379    }
380
381    fn graph_role(&self) -> GraphRole {
382        GraphRole::Virtual
383    }
384
385    fn stamp_mna(
386        &self,
387        _comp_id: &str,
388        _n1: Option<usize>,
389        _n2: Option<usize>,
390        _mna: &mut MnaSystem,
391        _sample_rate: f64,
392    ) -> StampResult {
393        StampResult::Skip
394    }
395
396    fn footprint_ref(&self) -> (&'static str, &'static str) {
397        ("", "TAP")
398    }
399
400    fn symbol_name(&self) -> &'static str {
401        "tap"
402    }
403    fn layout_class(&self) -> &'static str {
404        "tap"
405    }
406}