aerothesis

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

commit eb3ae5aded2c3dc3c126fd4c0bb3d5ddce90c9de
parent fb561e985a9da9517946ce8531eb8d9db9e6f308
Author: minerva-jupiter <ryouturn@gmail.com>
Date:   Wed,  1 Jul 2026 20:49:28 +0900

feat: refactor oscillation physics and internal state management

- Introduced explicit state fields for velocity, acceleration, and resonance tracking in the Aerothesis struct.
- Refactored core simulation methods, renaming `step` to `osc` and `x` to `osc_x` for clarity.
- Updated pressure calculation logic to integrate resonance and feedback gain.
- Adjusted parameter ranges and default values for feedback gain.
- Added a binary target to Cargo.toml and updated analysis logic in main.rs to support frequency spectrum reporting.

Diffstat:
MCargo.toml | 4++++
Msrc/lib.rs | 65+++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/main.rs | 15+++++++++++----
3 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -13,6 +13,10 @@ members = ["xtask"] [lib] crate-type = ["cdylib", "rlib"] +[[bin]] +name = "aerothesis-bin" +path = "src/main.rs" + [dependencies] # Remove the `assert_process_allocs` feature to allow allocations on the audio # thread in debug builds. diff --git a/src/lib.rs b/src/lib.rs @@ -27,6 +27,11 @@ pub struct Aerothesis { pub note_frequency: f32, pub displacement_prev: f32, + pub velocity_prev: f32, + pub displacement_prev2: f32, + pub accel_prev: f32, + pub f: f32, + pub resonance: f32, } #[derive(Enum, PartialEq, Clone, Copy)] @@ -111,6 +116,11 @@ impl Default for Aerothesis { note_frequency: 0.0, displacement_prev: 0.0, + velocity_prev: 0.0, + displacement_prev2: 0.0, + accel_prev: 0.0, + f: 0.0, + resonance: 0.0, } } } @@ -217,8 +227,11 @@ impl Default for AerothesisParams { ), feedback_gain: FloatParam::new( "Feedback Gain", - 0.1, - FloatRange::Linear { min: 0.0, max: 1.0 }, + 5.0, + FloatRange::Linear { + min: 0.0, + max: 10.0, + }, ), breath_cc: IntParam::new("Breath CC", 2, IntRange::Linear { min: 0, max: 127 }), @@ -239,15 +252,15 @@ impl Default for AerothesisParams { } impl Aerothesis { - pub fn step(&mut self) -> f32 { - let x_n = self.x(); + pub fn osc(&mut self) -> f32 { + let x_n = self.osc_x(); let v_n = self.v(x_n); self.x_prev2 = self.x_prev; self.x_prev = x_n; self.f_prev2 = self.f_prev; - self.f_prev = self.f(); + self.f_prev = self.f; self.v_prev = v_n; self.v_fluid_prev = self.vf(); @@ -277,7 +290,9 @@ impl Aerothesis { let gap_prev = (2.0 - self.x_prev).clamp(EPS, 2.0); let b_prev = RHO / (4.0 * (gap_prev * gap_prev)); - let c_prev = self.v_breath - b_prev * (self.v_fluid_prev * self.v_fluid_prev); + let pressure = self.params.base_damping.value() + * (self.v_breath + self.resonance * 100.0 * self.params.feedback_gain.value()); + let c_prev = pressure - b_prev * (self.v_fluid_prev * self.v_fluid_prev); // Current gap is also based on x_prev in this discrete model for stability let gap_curr = (2.0 - self.x_prev).clamp(EPS, 2.0); @@ -306,14 +321,15 @@ impl Aerothesis { } else { 0.5 * RHO * (v_fluid_current * v_fluid_current) * gap_curr }; + f_current } - pub fn x(&self) -> f32 { + pub fn osc_x(&mut self) -> f32 { let m = self.m(); let r = self.r(); let k = self.k(); let t = 1.0 / self.sample_rate; - let f_current = self.f(); + let f_current = self.f; let b0 = t * t; let b1 = 2.0 * t * t; @@ -352,28 +368,29 @@ impl Aerothesis { decay * x_delay } } - pub fn displacement(&mut self) -> f32 { - let x_n = self.step(); - let x_oscillator = x_n - self.equilibrium_offset(); - - let resonance = self.resonance(); - - let x_current = x_oscillator + resonance; + self.resonance = self.resonance(); + self.f = self.f(); + let x_n = self.osc(); + let x_current = x_n - self.equilibrium_offset(); + + // let displacement = x_current + // * (self.params.resonance_decay.value() + // * (x_current - self.displacement_prev) + // * (x_current - self.displacement_prev)) + // .clamp(0.0, 1.0); + let displacement = x_current; - let displacement = x_current - * (self.params.resonance_decay.value() - * (x_current - self.displacement_prev) - * (x_current - self.displacement_prev)) - .clamp(0.0, 1.0); self.displacement_prev = displacement; self.x_history.push_front(displacement); - x_oscillator + // displacement + self.resonance + displacement + // self.resonance } fn equilibrium_offset(&self) -> f32 { - let f = self.f(); + let f = self.f; let k = self.k(); if k > 0.0 { (f / k).clamp(0.0, 1.8) @@ -465,7 +482,7 @@ impl Plugin for Aerothesis { value, } => { if cc as i32 == self.params.breath_cc.value() { - self.v_breath = value * self.params.pressure_scale.value(); + self.v_breath = value; } else if cc as i32 == self.params.bite_cc.value() { self.v_bite = value; } @@ -486,7 +503,7 @@ impl Plugin for Aerothesis { for channel_samples in buffer.iter_samples() { let gain = self.params.gain.smoothed.next(); - let x_current = self.displacement() - self.avg_x_history(); + let x_current = self.displacement(); for sample in channel_samples { if self.note_frequency == 0.0 { diff --git a/src/main.rs b/src/main.rs @@ -10,8 +10,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { let num_samples = (sample_rate * seconds) as usize; plugin.sample_rate = sample_rate; - // plugin.note_frequency = util::midi_note_to_freq(48); // C3 (u8) - plugin.note_frequency = util::midi_note_to_freq(54); // C4 (u8) + plugin.note_frequency = util::midi_note_to_freq(60); // C4 (u8) + // plugin.note_frequency = util::midi_note_to_freq(54); // F#3 (u8) // For simulation in main.rs, we use the default parameters from AerothesisParams::default() // because nih-plug parameters are designed to be managed by a host and don't have @@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { // Default resonance: OpenPipe, Decay: 0.9 let mut displacements = Vec::with_capacity(num_samples); - let mut resonances = Vec::with_capacity(num_samples); + let mut osc = Vec::with_capacity(num_samples); for i in 0..num_samples { // let x_current = self.resonance() - self.avg_x_history(); @@ -36,8 +36,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { }; let sample = plugin.displacement() - plugin.avg_x_history(); + osc.push(plugin.osc_x()); displacements.push(sample); - resonances.push(plugin.resonance()); } let len = (sample_rate * 0.05) as usize; @@ -71,6 +71,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { } spectrum_data.push((freq, complex.norm())); } + if let Some(max) = spectrum_data + .iter() + .enumerate() + .max_by(|(_, a), (_, b)| a.1.partial_cmp(&b.1).unwrap()) + { + println!("max freq is {} Hz", max.0); + } println!("\n--- Spectrum (0 - 2000Hz) ---"); Chart::new(180, 60, 0.0, 2000.0)