Skip to main content

sva_engine/
vocabulary.rs

1// Concern: the callable names this language answers and the named arguments each takes | Non-concern: what any of them denotes, or evaluating one | IO: (name) -> is-a-builtin, named keys
2
3use sva_formula::filter::Shape;
4
5use crate::lower::physics::MODAL;
6use crate::overload::FINITE_DIFFERENCE as PHYSICS;
7
8/// Enough for surround; ambisonics is deferred rather than pretended at.
9pub const MAX_WIDTH: usize = 8;
10
11pub use sva_ast::{JOIN, SERIES};
12pub const CHANNEL: &str = "ch";
13
14const ARITHMETIC: [&str; 21] = [
15    "sin", "cos", "exp", "log", "pow", "sqrt", "abs", "tanh", "max", "min", "saw", "square",
16    "triangle", "sat", "crop", "rand", "delta", "pv", SERIES, JOIN, CHANNEL,
17];
18
19/// The five written crossings of FORMAT 7, callable like any other name.
20pub const CASTS: [&str; 5] = ["sample", "fourier", "ifourier", "stft", "istft"];
21
22/// The noise realization, beside the modal instruments `lower/physics.rs` dispatches:
23/// every one of them a closed form.
24const WRITTEN_LAWS: [&str; 1] = ["noise"];
25
26const LAWS: [&str; WRITTEN_LAWS.len() + MODAL.len()] = {
27    let mut all = [""; WRITTEN_LAWS.len() + MODAL.len()];
28    let mut i = 0;
29    while i < WRITTEN_LAWS.len() {
30        all[i] = WRITTEN_LAWS[i];
31        i += 1;
32    }
33    let mut j = 0;
34    while j < MODAL.len() {
35        all[WRITTEN_LAWS.len() + j] = MODAL[j];
36        j += 1;
37    }
38    all
39};
40
41/// Every callable name besides the filter shapes.
42pub const BUILTINS: [&str; ARITHMETIC.len() + PHYSICS.len() + CASTS.len() + LAWS.len()] = {
43    let mut all = [""; ARITHMETIC.len() + PHYSICS.len() + CASTS.len() + LAWS.len()];
44    let mut i = 0;
45    while i < ARITHMETIC.len() {
46        all[i] = ARITHMETIC[i];
47        i += 1;
48    }
49    let mut j = 0;
50    while j < PHYSICS.len() {
51        all[ARITHMETIC.len() + j] = PHYSICS[j];
52        j += 1;
53    }
54    let mut k = 0;
55    while k < CASTS.len() {
56        all[ARITHMETIC.len() + PHYSICS.len() + k] = CASTS[k];
57        k += 1;
58    }
59    let mut n = 0;
60    while n < LAWS.len() {
61        all[ARITHMETIC.len() + PHYSICS.len() + CASTS.len() + n] = LAWS[n];
62        n += 1;
63    }
64    all
65};
66
67pub fn is_builtin(name: &str) -> bool {
68    Shape::from_name(name).is_some() || BUILTINS.contains(&name)
69}
70
71/// Consts, so `recognized_named` hands back the same data a call site checks against.
72const NO_NAMED: &[&str] = &[];
73const WAVE_NAMED: &[&str] = &["tol"];
74const SAT_NAMED: &[&str] = &["drive"];
75const CROP_NAMED: &[&str] = &["start", "end", "rise", "fall"];
76const RAND_NAMED: &[&str] = &["seed"];
77const DELTA_NAMED: &[&str] = &["k"];
78const STFT_NAMED: &[&str] = &["window", "hop"];
79const NOISE_NAMED: &[&str] = &["period", "color"];
80/// Every modal bank reads the same damping, mode budget and excitation beside its own
81/// geometry's names. A cavity is one mode, so `one_mode` leaves the budget out.
82macro_rules! modal_named {
83    (one_mode $($own:literal),+ $(,)?) => {
84        &[$($own,)+ "damp_dc", "damp_freq", "vel", "at", "contact", "p", "force"]
85    };
86    ($($own:literal),+ $(,)?) => {
87        &[$($own,)+ "damp_dc", "damp_freq", "modes", "vel", "at", "contact", "p", "force"]
88    };
89}
90
91const STRING_NAMED: &[&str] = modal_named!("inharmonicity", "strike");
92const MEMBRANE_NAMED: &[&str] = modal_named!("tension", "density", "strike_x", "strike_y");
93const BAR_NAMED: &[&str] = modal_named!("thickness", "young", "density");
94const BORE_NAMED: &[&str] = modal_named!("radius", "speed", "closed");
95const ROOM_NAMED: &[&str] = modal_named!("speed");
96const HELMHOLTZ_NAMED: &[&str] =
97    modal_named!(one_mode "neck_area", "neck_length", "radius", "speed");
98const HAMMER_NAMED: &[&str] = &["at", "contact", "p", "force"];
99const FILTER_NAMED: &[&str] = &["cutoff", "q", "gain"];
100const CHAIGNE_ASKENFELT_NAMED: &[&str] = &[
101    "b",
102    "strike_pos",
103    "vel",
104    "hammer_mass",
105    "hammer_k",
106    "hammer_p",
107    "damp_dc",
108    "damp_freq",
109    "unison_count",
110    "detune",
111    "bridge_coupling",
112    "bridge_mass",
113    "string1_cents",
114    "string2_cents",
115    "string3_cents",
116    "string1_hammer_k_ratio",
117    "string2_hammer_k_ratio",
118    "string3_hammer_k_ratio",
119];
120const WILLEMSEN_BILBAO_SERAFIN_NAMED: &[&str] = &[
121    "b",
122    "bow_pos",
123    "bow_vel",
124    "bow_force",
125    "mu_s",
126    "mu_c",
127    "stribeck_vel",
128    "bristle_stiffness",
129    "bristle_damping",
130    "viscous_friction",
131    "damp_dc",
132    "damp_freq",
133];
134/// `holeN_*` is flat and capped at 6: no array-valued argument exists in this grammar.
135const DARABUNDIT_SCAVONE_NAMED: &[&str] = &[
136    "radius_in",
137    "radius_out",
138    "excite_pos",
139    "pulse_amp",
140    "pulse_width",
141    "damp_dc",
142    "damp_freq",
143    "hole1_pos",
144    "hole1_open",
145    "hole1_radius",
146    "hole1_height",
147    "hole2_pos",
148    "hole2_open",
149    "hole2_radius",
150    "hole2_height",
151    "hole3_pos",
152    "hole3_open",
153    "hole3_radius",
154    "hole3_height",
155    "hole4_pos",
156    "hole4_open",
157    "hole4_radius",
158    "hole4_height",
159    "hole5_pos",
160    "hole5_open",
161    "hole5_radius",
162    "hole5_height",
163    "hole6_pos",
164    "hole6_open",
165    "hole6_radius",
166    "hole6_height",
167];
168const RHAOUTI_CHAIGNE_JOLY_NAMED: &[&str] = &[
169    "aspect_ratio",
170    "strike_x",
171    "strike_y",
172    "vel",
173    "hammer_mass",
174    "hammer_k",
175    "hammer_p",
176    "damp_dc",
177    "damp_freq",
178];
179const CHAIGNE_DOUTAUT_NAMED: &[&str] = &[
180    "strike_pos",
181    "vel",
182    "hammer_mass",
183    "hammer_k",
184    "hammer_p",
185    "damp_dc",
186    "damp_freq",
187];
188const BOTTELDOOREN_NAMED: &[&str] = &[
189    "aspect_y",
190    "aspect_z",
191    "listener_x",
192    "listener_y",
193    "listener_z",
194    "pulse_amp",
195    "pulse_width",
196    "damp_dc",
197    "damp_freq",
198];
199
200/// `None` for a name `is_builtin` does not recognize.
201pub fn recognized_named(name: &str) -> Option<&'static [&'static str]> {
202    if Shape::from_name(name).is_some() {
203        return Some(FILTER_NAMED);
204    }
205    Some(match name {
206        "sin" | "cos" | "exp" | "sqrt" | "abs" | "tanh" | "max" | "min" | "pow" | "log" => NO_NAMED,
207        "saw" | "square" | "triangle" => WAVE_NAMED,
208        "sat" => SAT_NAMED,
209        SERIES | "pv" => NO_NAMED,
210        "crop" => CROP_NAMED,
211        "rand" => RAND_NAMED,
212        "delta" => DELTA_NAMED,
213        "chaigne_askenfelt" => CHAIGNE_ASKENFELT_NAMED,
214        "willemsen_bilbao_serafin" => WILLEMSEN_BILBAO_SERAFIN_NAMED,
215        "darabundit_scavone" => DARABUNDIT_SCAVONE_NAMED,
216        "rhaouti_chaigne_joly" => RHAOUTI_CHAIGNE_JOLY_NAMED,
217        "chaigne_doutaut" => CHAIGNE_DOUTAUT_NAMED,
218        "botteldooren" => BOTTELDOOREN_NAMED,
219        JOIN | CHANNEL => NO_NAMED,
220        "stft" => STFT_NAMED,
221        "sample" | "fourier" | "ifourier" | "istft" => NO_NAMED,
222        "noise" => NOISE_NAMED,
223        "string" => STRING_NAMED,
224        "membrane" => MEMBRANE_NAMED,
225        "bar" => BAR_NAMED,
226        "bore" => BORE_NAMED,
227        "room" => ROOM_NAMED,
228        "helmholtz" => HELMHOLTZ_NAMED,
229        "hammer_pulse" => HAMMER_NAMED,
230        _ => return None,
231    })
232}