Skip to main content

rill_core_model/modal/
params.rs

1use rill_core::Transcendental;
2
3/// Definition of a single resonant mode.
4#[derive(Debug, Clone, Copy)]
5pub struct ModeParams<T: Transcendental> {
6    /// Frequency ratio relative to fundamental (1.0 = fundamental).
7    pub freq_ratio: T,
8    /// Amplitude of this mode (0.0–1.0).
9    pub amplitude: T,
10    /// Decay time in seconds (time to -60 dB).
11    pub decay_time: T,
12}
13
14/// Parameters for a modal resonator model.
15#[derive(Debug, Clone)]
16pub struct ModalParams<T: Transcendental, const MAX_MODES: usize> {
17    /// Number of active modes (1–MAX_MODES).
18    pub num_modes: usize,
19    /// Mode definitions (frequency ratio, amplitude, decay time).
20    pub modes: [ModeParams<T>; MAX_MODES],
21    /// Fundamental frequency in Hz.
22    pub fundamental: T,
23    /// Global damping multiplier (1.0 = natural, > 1 = heavier damping).
24    pub damping: T,
25}
26
27impl<T: Transcendental, const MAX_MODES: usize> Default for ModalParams<T, MAX_MODES> {
28    fn default() -> Self {
29        let default_mode = ModeParams {
30            freq_ratio: T::ONE,
31            amplitude: T::ONE,
32            decay_time: T::from_f32(1.0),
33        };
34        Self {
35            num_modes: 1,
36            modes: [default_mode; MAX_MODES],
37            fundamental: T::from_f32(440.0),
38            damping: T::ONE,
39        }
40    }
41}
42
43/// Pre-computed bell modal parameters (5 modes).
44///
45/// Ratios approximate a tuned bell: 1.0, 2.76, 5.40, 8.93, 13.34
46/// with amplitudes decaying as 1/n².
47pub fn bell_modes<T: Transcendental, const MAX_MODES: usize>() -> ModalParams<T, MAX_MODES> {
48    let ratios = [1.0, 2.76, 5.40, 8.93, 13.34];
49    let amplitudes = [1.0, 0.67, 0.34, 0.12, 0.06];
50    let decays = [2.0, 1.5, 0.8, 0.3, 0.1];
51    let mut modes = [ModeParams {
52        freq_ratio: T::ONE,
53        amplitude: T::ZERO,
54        decay_time: T::from_f32(0.01),
55    }; MAX_MODES];
56    let limit = 5.min(MAX_MODES);
57    for i in 0..limit {
58        modes[i] = ModeParams {
59            freq_ratio: T::from_f64(ratios[i]),
60            amplitude: T::from_f64(amplitudes[i]),
61            decay_time: T::from_f64(decays[i]),
62        };
63    }
64    ModalParams {
65        num_modes: limit,
66        modes,
67        fundamental: T::from_f32(440.0),
68        damping: T::ONE,
69    }
70}
71
72/// Pre-computed marimba modal parameters (3 modes).
73///
74/// Ratios approximate a tuned marimba bar: 1.0, 4.0, 9.0
75/// with amplitudes decaying as 1/n.
76pub fn marimba_modes<T: Transcendental, const MAX_MODES: usize>() -> ModalParams<T, MAX_MODES> {
77    let ratios = [1.0, 4.0, 9.0];
78    let amplitudes = [1.0, 0.5, 0.2];
79    let decays = [3.0, 1.5, 0.5];
80    let mut modes = [ModeParams {
81        freq_ratio: T::ONE,
82        amplitude: T::ZERO,
83        decay_time: T::from_f32(0.01),
84    }; MAX_MODES];
85    let limit = 3.min(MAX_MODES);
86    for i in 0..limit {
87        modes[i] = ModeParams {
88            freq_ratio: T::from_f64(ratios[i]),
89            amplitude: T::from_f64(amplitudes[i]),
90            decay_time: T::from_f64(decays[i]),
91        };
92    }
93    ModalParams {
94        num_modes: limit,
95        modes,
96        fundamental: T::from_f32(440.0),
97        damping: T::ONE,
98    }
99}