aerothesis

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

commit 300a8706024a8d05e60d050be90f5bca191d108a
parent 0600c269b7ffc9fcf4198d62a049e135bd6ee2f2
Author: minerva-jupiter <ryouturn@gmail.com>
Date:   Sat, 13 Jun 2026 19:01:36 +0900

feat: implement physical model calculation in process loop

- Update README with clarified physical formula notation.
- Rename `.cargo/config` to `.cargo/config.toml`.
- Implement physics calculation methods (`x`, `v`, `m`, `r`, `k`, `f`) in `Aerothesis`.
- Update `process` loop to utilize physical model state and handle sample rate initialization.
- Default breath velocity set to 0.5.

Diffstat:
R.cargo/config -> .cargo/config.toml | 0
MREADME.md | 15+++++++++++----
Msrc/lib.rs | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/.cargo/config b/.cargo/config.toml diff --git a/README.md b/README.md @@ -89,8 +89,15 @@ $$v[n] = \frac{2}{T}(x[n] - x[n-1]) - v[n-1]$$ 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]$$$$ + +$$m[n] = \text{base\_mass} \cdot \big( 1.0 - \text{bite\_mass\_scale} \cdot V_{\text{bite}}[n] \big)$$ + +$$r[n] = \text{base\_damping} \cdot \big( 1.0 + \text{bite\_damping\_scale} \cdot V_{\text{bite}}[n] \big) + \text{breath\_damping} \cdot V_{\text{breath}}[n]$$ + +$$k[n] = \text{base\_stiffness} \cdot \big( 1.0 + \text{bite\_stiffness\_scale} \cdot V_{\text{bite}}[n] \big)$$ + +$$F[n] = \text{pressure\_scale} \cdot V_{\text{breath}}[n] - \text{feedback\_gain} \cdot P_{\text{downstream}}[n]$$ + +but P_downstream is under construction. diff --git a/src/lib.rs b/src/lib.rs @@ -67,7 +67,7 @@ impl Default for Aerothesis { f_prev: 0.0, sample_rate: 44100.0, - v_breath: 0.0, + v_breath: 0.5, v_bite: 0.0, } } @@ -175,6 +175,54 @@ impl Default for AerothesisParams { } } +impl Aerothesis { + fn m(&self) -> f32 { + self.params.base_mass.value() * (1.0 - self.params.bite_mass_scale.value() * self.v_bite) + } + + fn r(&self) -> f32 { + self.params.base_damping.value() + * (1.0 + self.params.bite_damping_scale.value() * self.v_bite) + + self.params.breath_damping.value() * self.v_breath + } + + fn k(&self) -> f32 { + self.params.base_stiffness.value() + * (1.0 + self.params.bite_stiffness_scale.value() * self.v_bite) + } + + fn f(&self /*, p_downstream: f32*/) -> f32 { + self.params.pressure_scale.value() * self.v_breath + /* - self.params.feedback_gain.value() * p_downstream */ + } + + fn x(&self) -> f32 { + let m = self.m(); + let r = self.r(); + let k = self.k(); + let f = self.f(); + + let t = 1.0 / self.sample_rate; + let t_sq = t * t; + + let a = (4.0 * m) / t_sq + (2.0 * r) / t + k; + + let term1 = (4.0 * m / t_sq + 2.0 * r / t) * self.x_prev; + let term2 = 2.0 * m * self.v_prev; + let term3 = f; + let term4 = (m / self.m_prev) + * (self.f_prev - self.r_prev * self.v_prev - self.k_prev * self.x_prev); + + let x_calculated = (1.0 / a) * (term1 + term2 + term3 + term4); + x_calculated.clamp(-1.0, 1.0) + } + + fn v(&self, x: f32) -> f32 { + let t = 1.0 / self.sample_rate; + (2.0 / t) * (x - self.x_prev) - self.v_prev + } +} + impl Plugin for Aerothesis { const NAME: &'static str = "Aerothesis"; const VENDOR: &'static str = "Minerva_Juppiter"; @@ -219,12 +267,10 @@ impl Plugin for Aerothesis { fn initialize( &mut self, _audio_io_layout: &AudioIOLayout, - _buffer_config: &BufferConfig, + buffer_config: &BufferConfig, _context: &mut impl InitContext<Self>, ) -> bool { - // Resize buffers and perform other potentially expensive initialization operations here. - // The `reset()` function is always called right after this function. You can remove this - // function if you do not need it. + self.sample_rate = buffer_config.sample_rate; true } @@ -240,24 +286,38 @@ impl Plugin for Aerothesis { 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; - } + if let NoteEvent::MidiCC { cc, value, .. } = event { + 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(); + // Calculate current physical parameters + let m = self.m(); + let r = self.r(); + let k = self.k(); + let f = self.f(); + + // Calculate next displacement x[n] and velocity v[n] + let x_n = self.x(); + let v_n = self.v(x_n); + + // Update state for next sample + self.x_prev = x_n; + self.v_prev = v_n; + self.m_prev = m; + self.r_prev = r; + self.k_prev = k; + self.f_prev = f; + for sample in channel_samples { - *sample *= gain; + *sample = x_n * gain; } }