aerothesis

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 0600c269b7ffc9fcf4198d62a049e135bd6ee2f2
parent dcffd3b14c4cb6bdc3b9207a3233c47aa3e42d49
Author: minerva-jupiter <ryouturn@gmail.com>
Date:   Sat, 13 Jun 2026 18:06:43 +0900

feat: implement physical oscillation model and MIDI CC control

- Added parameters for base mass, stiffness, damping, pressure, and feedback.
- Updated Aerothesis struct to store oscillation state and MIDI CC mappings.
- Enabled Basic MIDI input and implemented event processing for breath and bite CC values.
- Updated documentation with the physical oscillation formula.

Diffstat:
MREADME.md | 11+++++++++--
Msrc/lib.rs | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 142 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md @@ -84,6 +84,13 @@ $$v[n] = \frac{2}{T}(x[n] - x[n-1]) - v[n-1]$$ </details> -##### Formula for Ocillation +##### Formula for Oscillation -From above, +From above, formula for oscillation is +$$x[n] = \frac{1}{\frac{4m[n]}{T^2} + \frac{2r[n]}{T} + k[n]} \left[ \left( \frac{4m[n]}{T^2} + \frac{2r[n]}{T} \right) x[n-1] + 2m[n]v[n-1] + F[n] + \frac{m[n]}{m[n-1]} \left( F[n-1] - r[n-1]v[n-1] - k[n-1]x[n-1] \right) \right]$$ +$$v[n] = \frac{2}{T}(x[n] - x[n-1]) - v[n-1]$$ +Define bite strength $V_{\text{bite}}[n] \in [0.0, 1.0]$,and define breath strength $V_{\text{breath}}[n] \in [0.0, 1.0]$ mass $m[n]$ and others as follows: +$$m[n] = m_{\text{base}} \cdot \big( 1.0 - \gamma \cdot V_{\text{bite}}[n] \big)$$ +$$r[n] = r_{\text{base}} \cdot \big( 1.0 + \beta \cdot V_{\text{bite}}[n] \big) + \delta \cdot V_{\text{breath}}[n]$$ +$$k[n] = k_{\text{base}} \cdot \big( 1.0 + \alpha \cdot V_{\text{bite}}[n] \big)$$ +$$F[n] = G_{\text{breath}} \cdot V_{\text{breath}}[n] - G_{\text{feedback}} \cdot P_{\text{downstream}}[n]$$$$ diff --git a/src/lib.rs b/src/lib.rs @@ -7,6 +7,18 @@ use std::sync::Arc; struct Aerothesis { params: Arc<AerothesisParams>, + + x_prev: f32, + v_prev: f32, + + m_prev: f32, + r_prev: f32, + k_prev: f32, + f_prev: f32, + sample_rate: f32, + + v_breath: f32, + v_bite: f32, } #[derive(Params)] @@ -17,12 +29,46 @@ struct AerothesisParams { /// gain parameter is stored as linear gain while the values are displayed in decibels. #[id = "gain"] pub gain: FloatParam, + #[id = "base_mass"] + pub base_mass: FloatParam, + #[id = "bite_mass_scale"] + pub bite_mass_scale: FloatParam, + #[id = "base_stiffness"] + pub base_stiffness: FloatParam, + #[id = "bite_stiffness_scale"] + pub bite_stiffness_scale: FloatParam, + #[id = "base_damping"] + pub base_damping: FloatParam, + #[id = "bite_damping_scale"] + pub bite_damping_scale: FloatParam, + #[id = "breath_damping"] + pub breath_damping: FloatParam, + #[id = "pressure_scale"] + pub pressure_scale: FloatParam, + #[id = "feedback_gain"] + pub feedback_gain: FloatParam, + + #[id = "breath_cc"] + pub breath_cc: IntParam, + #[id = "bite_cc"] + pub bite_cc: IntParam, } impl Default for Aerothesis { fn default() -> Self { Self { params: Arc::new(AerothesisParams::default()), + x_prev: 0.0, + v_prev: 0.0, + + m_prev: 1.0, + r_prev: 1.0, + k_prev: 1.0, + f_prev: 0.0, + sample_rate: 44100.0, + + v_breath: 0.0, + v_bite: 0.0, } } } @@ -53,6 +99,78 @@ impl Default for AerothesisParams { // `.with_step_size(0.1)` function to get internal rounding. .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()), + + base_mass: FloatParam::new( + "Base Mass", + 1.0, + FloatRange::Skewed { + min: 0.01, + max: 10.0, + factor: 0.2, + }, + ) + .with_unit(" mg"), + bite_mass_scale: FloatParam::new( + "Bite Mass Reduction", + 0.5, + FloatRange::Linear { min: 0.0, max: 0.9 }, + ), + base_stiffness: FloatParam::new( + "Base Stiffness", + 1000.0, + FloatRange::Skewed { + min: 100.0, + max: 100000.0, + factor: 0.3, + }, + ) + .with_unit(" N/m"), + bite_stiffness_scale: FloatParam::new( + "Bite Stiffness Incr.", + 1.0, + FloatRange::Linear { + min: 0.0, + max: 10.0, + }, + ), + base_damping: FloatParam::new( + "Base Damping", + 0.1, + FloatRange::Skewed { + min: 0.001, + max: 1.0, + factor: 0.2, + }, + ), + bite_damping_scale: FloatParam::new( + "Bite Damping Incr.", + 1.0, + FloatRange::Linear { + min: 0.0, + max: 10.0, + }, + ), + breath_damping: FloatParam::new( + "Breath Damping", + 0.1, + FloatRange::Linear { min: 0.0, max: 1.0 }, + ), + pressure_scale: FloatParam::new( + "Pressure Gain", + 10.0, + FloatRange::Linear { + min: 0.0, + max: 100.0, + }, + ), + feedback_gain: FloatParam::new( + "Feedback Gain", + 0.1, + FloatRange::Linear { min: 0.0, max: 1.0 }, + ), + + breath_cc: IntParam::new("Breath CC", 2, IntRange::Linear { min: 0, max: 127 }), + bite_cc: IntParam::new("Bite CC", 11, IntRange::Linear { min: 0, max: 127 }), } } } @@ -80,7 +198,7 @@ impl Plugin for Aerothesis { names: PortNames::const_default(), }]; - const MIDI_INPUT: MidiConfig = MidiConfig::None; + const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const MIDI_OUTPUT: MidiConfig = MidiConfig::None; const SAMPLE_ACCURATE_AUTOMATION: bool = true; @@ -119,8 +237,21 @@ impl Plugin for Aerothesis { &mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, - _context: &mut impl ProcessContext<Self>, + context: &mut impl ProcessContext<Self>, ) -> ProcessStatus { + while let Some(event) = context.next_event() { + match event { + NoteEvent::MidiCC { cc, value, .. } => { + if cc == self.params.breath_cc.value() as u8 { + self.v_breath = value; + } else if cc == self.params.bite_cc.value() as u8 { + self.v_bite = value; + } + } + _ => (), + } + } + for channel_samples in buffer.iter_samples() { // Smoothing is optionally built into the parameters themselves let gain = self.params.gain.smoothed.next();