#![allow(missing_docs)]
use std::collections::HashMap;
use rill_core::traits::ParamValue;
use crate::automaton::envelope::EnvelopeType;
use crate::automaton::lfo::LfoWaveform;
use crate::automaton::sequencer::PlayMode;
use crate::engine::{ParameterMapping, Transform};
use crate::strategy::{ConflictStrategy, ControlStrategy};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum AutomatonDef {
Lfo {
id: String,
frequency: f64,
amplitude: f64,
offset: f64,
waveform: LfoWaveform,
},
Envelope {
id: String,
envelope_type: EnvelopeType,
attack: f64,
decay: f64,
sustain: f64,
release: f64,
curve: f64,
},
Sequencer {
id: String,
steps: Vec<StepDef>,
play_mode: PlayMode,
tempo: f64,
},
NamedFunction {
id: String,
function_name: String,
params: HashMap<String, f64>,
},
Custom {
id: String,
type_name: String,
#[cfg_attr(feature = "serde", serde(default))]
params: HashMap<String, ParamValue>,
},
}
impl AutomatonDef {
pub fn id(&self) -> &str {
match self {
AutomatonDef::Lfo { id, .. } => id,
AutomatonDef::Envelope { id, .. } => id,
AutomatonDef::Sequencer { id, .. } => id,
AutomatonDef::NamedFunction { id, .. } => id,
AutomatonDef::Custom { id, .. } => id,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct StepDef {
pub duration: f64,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MappingType {
Linear,
Exponential,
Logarithmic,
Inverted,
}
impl MappingType {
pub fn to_parameter_mapping(self) -> ParameterMapping {
match self {
MappingType::Linear => ParameterMapping::Linear,
MappingType::Exponential => ParameterMapping::Exponential,
MappingType::Logarithmic => ParameterMapping::Logarithmic,
MappingType::Inverted => ParameterMapping::Inverted,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct ServoDef {
pub automaton_id: String,
pub target_node: u32,
pub target_param: String,
pub mapping: MappingType,
pub min: f64,
pub max: f64,
pub enabled: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub async_interval_ms: Option<f64>,
#[cfg_attr(feature = "serde", serde(default))]
pub control_strategy: Option<ControlStrategy>,
#[cfg_attr(feature = "serde", serde(default))]
pub conflict_strategy: Option<ConflictStrategy>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub table: Option<Vec<ParamValue>>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum TransformDef {
Linear,
Exponential,
Logarithmic,
Inverted,
}
impl TransformDef {
pub fn to_transform(&self) -> Transform {
match self {
TransformDef::Linear => Transform::Linear,
TransformDef::Exponential => Transform::Exponential,
TransformDef::Logarithmic => Transform::Logarithmic,
TransformDef::Inverted => Transform::Inverted,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct MappingDef {
pub event_pattern: crate::engine::EventPattern,
pub target_node: u32,
pub target_param: String,
pub transform: TransformDef,
pub min: f64,
pub max: f64,
pub enabled: bool,
}
impl MappingDef {
pub fn to_mapping(&self) -> crate::engine::Mapping {
use crate::engine::Target;
crate::engine::Mapping::new(
self.event_pattern.clone(),
Target {
node_id: rill_core::traits::NodeId(self.target_node),
param_name: self.target_param.clone(),
min: self.min as f32,
max: self.max as f32,
},
self.transform.to_transform(),
)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum SensorDef {
Midi {
backend: String,
port_name: String,
#[cfg_attr(feature = "serde", serde(default))]
mappings: Vec<MappingDef>,
},
}
impl SensorDef {
pub fn get_mappings(&self) -> Vec<crate::engine::Mapping> {
match self {
SensorDef::Midi { mappings, .. } => mappings.iter().map(|m| m.to_mapping()).collect(),
}
}
#[cfg(feature = "midi")]
pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
match self {
SensorDef::Midi {
backend,
port_name,
mappings: _,
} => {
use rill_io::midi_backend::MidiBackend;
let be: Box<dyn MidiBackend> = match backend.as_str() {
"midir" => Box::new(rill_io::backends::MidirBackend::new(port_name).ok()?),
"alsa_seq" => {
#[cfg(feature = "alsa")]
{
Box::new(
rill_io::backends::AlsaSeqBackend::new(port_name)
.map_err(|e| log::warn!("AlsaSeqBackend: {e}"))
.ok()?,
)
}
#[cfg(not(feature = "alsa"))]
{
log::warn!("ALSA seq backend requires 'alsa' feature");
return None;
}
}
_ => {
log::warn!("unknown MIDI backend '{backend}'");
return None;
}
};
let hub = crate::midi::MidiHub::new(port_name.as_str(), be);
Some(Box::new(hub))
}
}
}
#[cfg(not(feature = "midi"))]
pub fn into_sensor(&self) -> Option<Box<dyn crate::sensor::Sensor>> {
None
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ModuleDef {
Servo(ServoDef),
Sensor(SensorDef),
Custom {
type_name: String,
#[cfg_attr(feature = "serde", serde(default))]
params: HashMap<String, ParamValue>,
},
}
impl ModuleDef {
pub fn type_name(&self) -> &str {
match self {
ModuleDef::Servo(_) => "servo",
ModuleDef::Sensor(_) => "midi",
ModuleDef::Custom { type_name, .. } => type_name,
}
}
}