1#![allow(missing_docs)]
7
8use std::collections::HashMap;
9
10use rill_core::traits::ParamValue;
11
12use crate::automaton::envelope::EnvelopeType;
13use crate::automaton::lfo::LfoWaveform;
14use crate::automaton::sequencer::PlayMode;
15use crate::engine::{ParameterMapping, Transform};
16use crate::strategy::{ConflictStrategy, ControlStrategy};
17
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[derive(Debug, Clone)]
25pub enum AutomatonDef {
26 Lfo {
27 id: String,
28 frequency: f64,
29 amplitude: f64,
30 offset: f64,
31 waveform: LfoWaveform,
32 },
33 Envelope {
34 id: String,
35 envelope_type: EnvelopeType,
36 attack: f64,
37 decay: f64,
38 sustain: f64,
39 release: f64,
40 curve: f64,
41 },
42 Sequencer {
43 id: String,
44 steps: Vec<StepDef>,
45 play_mode: PlayMode,
46 tempo: f64,
47 },
48 NamedFunction {
49 id: String,
50 function_name: String,
51 params: HashMap<String, f64>,
52 },
53 Custom {
55 id: String,
56 type_name: String,
57 #[cfg_attr(feature = "serde", serde(default))]
58 params: HashMap<String, ParamValue>,
59 },
60}
61
62impl AutomatonDef {
63 pub fn id(&self) -> &str {
64 match self {
65 AutomatonDef::Lfo { id, .. } => id,
66 AutomatonDef::Envelope { id, .. } => id,
67 AutomatonDef::Sequencer { id, .. } => id,
68 AutomatonDef::NamedFunction { id, .. } => id,
69 AutomatonDef::Custom { id, .. } => id,
70 }
71 }
72}
73
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76#[derive(Debug, Clone)]
77pub struct StepDef {
78 pub duration: f64,
80}
81
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[derive(Debug, Clone, Copy, PartialEq)]
89pub enum MappingType {
90 Linear,
91 Exponential,
92 Logarithmic,
93 Inverted,
94}
95
96impl MappingType {
97 pub fn to_parameter_mapping(self) -> ParameterMapping {
98 match self {
99 MappingType::Linear => ParameterMapping::Linear,
100 MappingType::Exponential => ParameterMapping::Exponential,
101 MappingType::Logarithmic => ParameterMapping::Logarithmic,
102 MappingType::Inverted => ParameterMapping::Inverted,
103 }
104 }
105}
106
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109#[derive(Debug, Clone)]
110pub struct ServoDef {
111 pub automaton_id: String,
112 pub target_node: u32,
113 pub target_param: String,
114 pub mapping: MappingType,
115 pub min: f64,
116 pub max: f64,
117 pub enabled: bool,
118
119 #[cfg_attr(feature = "serde", serde(default))]
124 pub async_interval_ms: Option<f64>,
125
126 #[cfg_attr(feature = "serde", serde(default))]
128 pub control_strategy: Option<ControlStrategy>,
129
130 #[cfg_attr(feature = "serde", serde(default))]
132 pub conflict_strategy: Option<ConflictStrategy>,
133
134 #[cfg_attr(
137 feature = "serde",
138 serde(default, skip_serializing_if = "Option::is_none")
139 )]
140 pub table: Option<Vec<ParamValue>>,
141}
142
143#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149#[derive(Debug, Clone)]
150pub enum TransformDef {
151 Linear,
152 Exponential,
153 Logarithmic,
154 Inverted,
155}
156
157impl TransformDef {
158 pub fn to_transform(&self) -> Transform {
159 match self {
160 TransformDef::Linear => Transform::Linear,
161 TransformDef::Exponential => Transform::Exponential,
162 TransformDef::Logarithmic => Transform::Logarithmic,
163 TransformDef::Inverted => Transform::Inverted,
164 }
165 }
166}
167
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170#[derive(Debug, Clone)]
171pub struct MappingDef {
172 pub event_pattern: crate::engine::EventPattern,
173 pub target_node: u32,
174 pub target_param: String,
175 pub transform: TransformDef,
176 pub min: f64,
177 pub max: f64,
178 pub enabled: bool,
179}
180
181impl MappingDef {
182 pub fn to_mapping(&self) -> crate::engine::Mapping {
183 use crate::engine::Target;
184 crate::engine::Mapping::new(
185 self.event_pattern.clone(),
186 Target {
187 node_id: rill_core::traits::NodeId(self.target_node),
188 param_name: self.target_param.clone(),
189 min: self.min as f32,
190 max: self.max as f32,
191 },
192 self.transform.to_transform(),
193 )
194 }
195}
196
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203#[derive(Debug, Clone)]
204pub enum SensorDef {
205 Midi {
207 backend: String,
209 port_name: String,
211 #[cfg_attr(feature = "serde", serde(default))]
213 mappings: Vec<MappingDef>,
214 },
215 Osc {
217 port: u16,
219 #[cfg_attr(feature = "serde", serde(default))]
221 mappings: Vec<MappingDef>,
222 },
223}
224
225impl SensorDef {
226 pub fn get_mappings(&self) -> Vec<crate::engine::Mapping> {
228 match self {
229 SensorDef::Midi { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
230 SensorDef::Osc { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
231 }
232 }
233
234 #[cfg(any(feature = "midi", feature = "osc"))]
235 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
236 match self {
237 #[cfg(feature = "midi")]
238 SensorDef::Midi {
239 backend,
240 port_name,
241 mappings: _,
242 } => {
243 use rill_io::midi_input::MidiInput;
244 let be: Box<dyn MidiInput> = match backend.as_str() {
245 "midir" => Box::new(rill_io::backends::MidirBackend::new(port_name).ok()?),
246 "alsa_seq" => {
247 #[cfg(feature = "alsa")]
248 {
249 Box::new(
250 rill_io::backends::AlsaSeqBackend::new(port_name)
251 .map_err(|e| log::warn!("AlsaSeqBackend: {e}"))
252 .ok()?,
253 )
254 }
255 #[cfg(not(feature = "alsa"))]
256 {
257 log::warn!("ALSA seq backend requires 'alsa' feature");
258 return None;
259 }
260 }
261 _ => {
262 log::warn!("unknown MIDI backend '{backend}'");
263 return None;
264 }
265 };
266 let hub = crate::midi::MidiHub::new(port_name.as_str(), be);
267 Some(Box::new(hub))
268 }
269 #[cfg(feature = "osc")]
270 SensorDef::Osc { port, mappings: _ } => {
271 let addr = std::net::SocketAddr::from(([0, 0, 0, 0], *port));
272 let sensor = crate::osc::OscSensor::new(format!("osc_{port}"), addr);
273 Some(Box::new(sensor))
274 }
275 #[allow(unreachable_patterns)]
276 _ => None,
277 }
278 }
279 #[cfg(not(any(feature = "midi", feature = "osc")))]
280 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
281 None
282 }
283}
284
285#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
291#[derive(Debug, Clone)]
292pub struct ClockDef {
293 pub backend: String,
295 pub port_name: String,
297 #[cfg_attr(feature = "serde", serde(default))]
299 pub auto_start: bool,
300}
301
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309#[derive(Debug, Clone)]
310pub enum ModuleDef {
311 Clock(ClockDef),
313 Servo(ServoDef),
315 Sensor(SensorDef),
317 Custom {
319 type_name: String,
321 #[cfg_attr(feature = "serde", serde(default))]
323 params: HashMap<String, ParamValue>,
324 },
325}
326
327impl ModuleDef {
328 pub fn type_name(&self) -> &str {
330 match self {
331 ModuleDef::Clock(_) => "clock",
332 ModuleDef::Servo(_) => "servo",
333 ModuleDef::Sensor(SensorDef::Midi { .. }) => "midi",
334 ModuleDef::Sensor(SensorDef::Osc { .. }) => "osc",
335 ModuleDef::Custom { type_name, .. } => type_name,
336 }
337 }
338}