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}
216
217impl SensorDef {
218 pub fn get_mappings(&self) -> Vec<crate::engine::Mapping> {
220 match self {
221 SensorDef::Midi { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
222 }
223 }
224
225 #[cfg(feature = "midi")]
226 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
227 match self {
228 SensorDef::Midi {
229 backend,
230 port_name,
231 mappings: _,
232 } => {
233 use rill_io::midi_backend::MidiBackend;
234 let be: Box<dyn MidiBackend> = match backend.as_str() {
235 "midir" => Box::new(rill_io::backends::MidirBackend::new(port_name).ok()?),
236 "alsa_seq" => {
237 #[cfg(feature = "alsa")]
238 {
239 Box::new(
240 rill_io::backends::AlsaSeqBackend::new(port_name)
241 .map_err(|e| log::warn!("AlsaSeqBackend: {e}"))
242 .ok()?,
243 )
244 }
245 #[cfg(not(feature = "alsa"))]
246 {
247 log::warn!("ALSA seq backend requires 'alsa' feature");
248 return None;
249 }
250 }
251 _ => {
252 log::warn!("unknown MIDI backend '{backend}'");
253 return None;
254 }
255 };
256 let hub = crate::midi::MidiHub::new(port_name.as_str(), be);
257 Some(Box::new(hub))
258 }
259 }
260 }
261 #[cfg(not(feature = "midi"))]
262 pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
263 None
264 }
265}
266
267#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
274#[derive(Debug, Clone)]
275pub enum ModuleDef {
276 Servo(ServoDef),
278 Sensor(SensorDef),
280 Custom {
282 type_name: String,
284 #[cfg_attr(feature = "serde", serde(default))]
286 params: HashMap<String, ParamValue>,
287 },
288}
289
290impl ModuleDef {
291 pub fn type_name(&self) -> &str {
293 match self {
294 ModuleDef::Servo(_) => "servo",
295 ModuleDef::Sensor(_) => "midi",
296 ModuleDef::Custom { type_name, .. } => type_name,
297 }
298 }
299}