Skip to main content

rill_patchbay/
servo_constructor.rs

1//! ServoConstructor — creates Servo actors from `ModuleDef::Servo` descriptors.
2
3use std::sync::Arc;
4
5use rill_core::queues::CommandEnum;
6use rill_core_actor::{ActorRef, ActorSystem};
7
8use crate::automaton::envelope::EnvelopeAutomaton;
9use crate::automaton::lfo::LfoAutomaton;
10use crate::automaton::sequencer::{SequencerAutomaton, Step};
11use crate::engine::Servo;
12use crate::module_def::{AutomatonDef, ModuleDef};
13use crate::module_factory::{ModuleConstructor, ModuleError};
14
15#[cfg(feature = "debug")]
16use crate::debug::PatchbayInspector;
17
18/// Module constructor for the `"servo"` type — bridges an automaton to a graph parameter.
19pub struct ServoConstructor;
20
21impl ModuleConstructor for ServoConstructor {
22    fn type_name(&self) -> &'static str {
23        "servo"
24    }
25
26    fn construct(
27        &self,
28        module: &ModuleDef,
29        automaton_defs: &[AutomatonDef],
30        system: &Arc<ActorSystem>,
31        graph_ref: &ActorRef<CommandEnum>,
32        #[cfg(feature = "debug")] inspector: Option<&PatchbayInspector>,
33    ) -> Result<ActorRef<CommandEnum>, ModuleError> {
34        let ModuleDef::Servo(s) = module else {
35            return Err(ModuleError::ConstructionFailed(
36                "ServoConstructor requires ModuleDef::Servo".into(),
37            ));
38        };
39
40        let def = automaton_defs
41            .iter()
42            .find(|a| a.id() == s.automaton_id)
43            .ok_or_else(|| {
44                ModuleError::ConstructionFailed(format!(
45                    "servo '{}' references unknown automaton '{}'",
46                    s.automaton_id, s.automaton_id
47                ))
48            })?;
49
50        let nid = s.target_node;
51        let mapping = s.mapping.to_parameter_mapping();
52
53        let actor_ref = match def {
54            AutomatonDef::Lfo {
55                id,
56                frequency,
57                amplitude,
58                offset,
59                waveform,
60            } => {
61                let a = LfoAutomaton::new(id, *frequency, *amplitude, *offset, *waveform);
62                let mut servo = Servo::new(
63                    id,
64                    a,
65                    nid,
66                    &s.target_param,
67                    mapping,
68                    s.min,
69                    s.max,
70                    system.clone(),
71                    graph_ref.clone(),
72                );
73                if let Some(ref t) = s.table {
74                    servo = servo.with_table(t.clone());
75                }
76                if let Some(ref cs) = s.control_strategy {
77                    servo = servo.with_control(*cs);
78                }
79                if let Some(ref cf) = s.conflict_strategy {
80                    servo = servo.with_conflict(*cf);
81                }
82                if let Some(ref anchor) = s.target_anchor {
83                    servo = servo.with_anchor(anchor.clone());
84                }
85                #[cfg(feature = "debug")]
86                if let Some(inspector) = inspector {
87                    let automaton_inspector = servo.inspector();
88                    inspector
89                        .register_automaton(module.type_name().to_string(), automaton_inspector);
90                }
91                servo.spawn(system)
92            }
93            AutomatonDef::Envelope {
94                id,
95                attack,
96                decay,
97                sustain,
98                release,
99                curve,
100                ..
101            } => {
102                let a = EnvelopeAutomaton::adsr(id, *attack, *decay, *sustain, *release)
103                    .with_curve(*curve);
104                let mut servo = Servo::new(
105                    id,
106                    a,
107                    nid,
108                    &s.target_param,
109                    mapping,
110                    s.min,
111                    s.max,
112                    system.clone(),
113                    graph_ref.clone(),
114                );
115                if let Some(ref cs) = s.control_strategy {
116                    servo = servo.with_control(*cs);
117                }
118                if let Some(ref cf) = s.conflict_strategy {
119                    servo = servo.with_conflict(*cf);
120                }
121                if let Some(ref anchor) = s.target_anchor {
122                    servo = servo.with_anchor(anchor.clone());
123                }
124                #[cfg(feature = "debug")]
125                if let Some(inspector) = inspector {
126                    let automaton_inspector = servo.inspector();
127                    inspector
128                        .register_automaton(module.type_name().to_string(), automaton_inspector);
129                }
130                servo.spawn(system)
131            }
132            AutomatonDef::Sequencer {
133                id,
134                steps,
135                play_mode,
136                tempo,
137            } => {
138                let seq_steps: Vec<Step> = steps
139                    .iter()
140                    .map(|sd| Step {
141                        duration: sd.duration,
142                    })
143                    .collect();
144                let a = SequencerAutomaton::new(id, seq_steps)
145                    .with_mode(*play_mode)
146                    .with_tempo(*tempo);
147                let mut servo = Servo::new(
148                    id,
149                    a,
150                    nid,
151                    &s.target_param,
152                    mapping,
153                    s.min,
154                    s.max,
155                    system.clone(),
156                    graph_ref.clone(),
157                );
158                if let Some(ref t) = s.table {
159                    servo = servo.with_table(t.clone());
160                }
161                if let Some(ref cs) = s.control_strategy {
162                    servo = servo.with_control(*cs);
163                }
164                if let Some(ref cf) = s.conflict_strategy {
165                    servo = servo.with_conflict(*cf);
166                }
167                if let Some(ref anchor) = s.target_anchor {
168                    servo = servo.with_anchor(anchor.clone());
169                }
170                #[cfg(feature = "debug")]
171                if let Some(inspector) = inspector {
172                    let automaton_inspector = servo.inspector();
173                    inspector
174                        .register_automaton(module.type_name().to_string(), automaton_inspector);
175                }
176                servo.spawn(system)
177            }
178            AutomatonDef::NamedFunction { id, .. } => {
179                return Err(ModuleError::ConstructionFailed(format!(
180                    "NamedFunction automaton '{}' requires manual setup",
181                    id
182                )));
183            }
184            AutomatonDef::Custom { id, type_name, .. } => {
185                return Err(ModuleError::ConstructionFailed(format!(
186                    "Custom automaton '{}' (type '{}') not yet supported via ServoConstructor",
187                    id, type_name,
188                )));
189            }
190        };
191
192        Ok(actor_ref)
193    }
194
195    fn clone_box(&self) -> Box<dyn ModuleConstructor> {
196        Box::new(ServoConstructor)
197    }
198}