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 #[cfg_attr(feature = "serde", serde(default))]
146 pub target_anchor: Option<String>,
147}
148
149#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
155#[derive(Debug, Clone)]
156pub enum TransformDef {
157 Linear,
158 Exponential,
159 Logarithmic,
160 Inverted,
161}
162
163impl TransformDef {
164 pub fn to_transform(&self) -> Transform {
165 match self {
166 TransformDef::Linear => Transform::Linear,
167 TransformDef::Exponential => Transform::Exponential,
168 TransformDef::Logarithmic => Transform::Logarithmic,
169 TransformDef::Inverted => Transform::Inverted,
170 }
171 }
172}
173
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176#[derive(Debug, Clone)]
177pub struct MappingDef {
178 pub event_pattern: crate::engine::EventPattern,
179 pub target_node: u32,
180 pub target_param: String,
181 pub transform: TransformDef,
182 pub min: f64,
183 pub max: f64,
184 pub enabled: bool,
185}
186
187impl MappingDef {
188 pub fn to_mapping(&self) -> crate::engine::Mapping {
189 use crate::engine::Target;
190 crate::engine::Mapping::new(
191 self.event_pattern.clone(),
192 Target {
193 node_id: self.target_node,
194 param_name: self.target_param.clone(),
195 min: self.min as f32,
196 max: self.max as f32,
197 },
198 self.transform.to_transform(),
199 )
200 }
201}
202
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209#[derive(Debug, Clone)]
210pub enum SensorDef {
211 Midi {
213 backend: String,
215 port_name: String,
217 #[cfg_attr(feature = "serde", serde(default))]
219 mappings: Vec<MappingDef>,
220 },
221 Osc {
223 port: u16,
225 #[cfg_attr(feature = "serde", serde(default))]
227 mappings: Vec<MappingDef>,
228 },
229}
230
231impl SensorDef {
232 pub fn get_mappings(&self) -> Vec<crate::engine::Mapping> {
234 match self {
235 SensorDef::Midi { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
236 SensorDef::Osc { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
237 }
238 }
239
240 #[cfg(any(feature = "midi", feature = "osc"))]
241 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
242 match self {
243 #[cfg(feature = "midi")]
244 SensorDef::Midi {
245 backend,
246 port_name,
247 mappings: _,
248 } => {
249 use rill_io::midi_input::MidiInput;
250 let be: Box<dyn MidiInput> = match backend.as_str() {
251 "midir" => Box::new(rill_io::backends::MidirBackend::new(port_name).ok()?),
252 "alsa_seq" => {
253 #[cfg(feature = "alsa")]
254 {
255 Box::new(
256 rill_io::backends::AlsaSeqBackend::new(port_name)
257 .map_err(|e| log::warn!("AlsaSeqBackend: {e}"))
258 .ok()?,
259 )
260 }
261 #[cfg(not(feature = "alsa"))]
262 {
263 log::warn!("ALSA seq backend requires 'alsa' feature");
264 return None;
265 }
266 }
267 _ => {
268 log::warn!("unknown MIDI backend '{backend}'");
269 return None;
270 }
271 };
272 let hub = crate::midi::MidiHub::new(port_name.as_str(), be);
273 Some(Box::new(hub))
274 }
275 #[cfg(feature = "osc")]
276 SensorDef::Osc { port, mappings: _ } => {
277 let addr = std::net::SocketAddr::from(([0, 0, 0, 0], *port));
278 let sensor = crate::osc::OscSensor::new(format!("osc_{port}"), addr);
279 Some(Box::new(sensor))
280 }
281 #[allow(unreachable_patterns)]
282 _ => None,
283 }
284 }
285 #[cfg(not(any(feature = "midi", feature = "osc")))]
286 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
287 None
288 }
289}
290
291#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297#[derive(Debug, Clone)]
298pub struct ClockDef {
299 pub backend: String,
301 pub port_name: String,
303 #[cfg_attr(feature = "serde", serde(default))]
305 pub auto_start: bool,
306}
307
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
315#[derive(Debug, Clone)]
316pub enum ModuleDef {
317 Clock(ClockDef),
319 Servo(ServoDef),
321 Sensor(SensorDef),
323 Custom {
325 type_name: String,
327 #[cfg_attr(feature = "serde", serde(default))]
329 params: HashMap<String, ParamValue>,
330 },
331}
332
333impl ModuleDef {
334 pub fn type_name(&self) -> &str {
336 match self {
337 ModuleDef::Clock(_) => "clock",
338 ModuleDef::Servo(_) => "servo",
339 ModuleDef::Sensor(SensorDef::Midi { .. }) => "midi",
340 ModuleDef::Sensor(SensorDef::Osc { .. }) => "osc",
341 ModuleDef::Custom { type_name, .. } => type_name,
342 }
343 }
344}