1use sva_ast::{CHANNEL, FILTERS, JOIN, SERIES};
4use sva_formula::filter::{ALL_SHAPES, Shape};
5
6pub const MAX_WIDTH: usize = 8;
8
9pub fn shape(name: &str) -> Option<Shape> {
11 FILTERS
12 .iter()
13 .position(|held| *held == name)
14 .map(|at| ALL_SHAPES[at])
15}
16
17const _: () = assert!(FILTERS.len() == ALL_SHAPES.len());
18
19pub fn shape_name(shape: Shape) -> &'static str {
20 let at = ALL_SHAPES.iter().position(|held| *held == shape);
21 FILTERS[at.expect("ALL_SHAPES holds every shape")]
22}
23
24pub(crate) fn note_hz(name: &str) -> Option<f64> {
25 sva_ast::note_midi(name).map(sva_formula::note::frequency)
26}
27
28const NO_NAMED: &[&str] = &[];
30const WAVE_NAMED: &[&str] = &["tol"];
31const SAT_NAMED: &[&str] = &["drive"];
32const CROP_NAMED: &[&str] = &["start", "end", "rise", "fall"];
33const RAND_NAMED: &[&str] = &["seed"];
34const DELTA_NAMED: &[&str] = &["k"];
35const STFT_NAMED: &[&str] = &["window", "hop"];
36const NOISE_NAMED: &[&str] = &["period", "color"];
37macro_rules! modal_named {
40 (one_mode $($own:literal),+ $(,)?) => {
41 &[$($own,)+ "damp_dc", "damp_freq", "vel", "at", "contact", "p", "force"]
42 };
43 ($($own:literal),+ $(,)?) => {
44 &[$($own,)+ "damp_dc", "damp_freq", "modes", "vel", "at", "contact", "p", "force"]
45 };
46}
47
48const STRING_NAMED: &[&str] = modal_named!("inharmonicity", "strike");
49const MEMBRANE_NAMED: &[&str] = modal_named!("tension", "density", "strike_x", "strike_y");
50const BAR_NAMED: &[&str] = modal_named!("thickness", "young", "density");
51const BORE_NAMED: &[&str] = modal_named!("radius", "speed", "closed");
52const ROOM_NAMED: &[&str] = modal_named!("speed");
53const HELMHOLTZ_NAMED: &[&str] =
54 modal_named!(one_mode "neck_area", "neck_length", "radius", "speed");
55const HAMMER_NAMED: &[&str] = &["at", "contact", "p", "force"];
56const FILTER_NAMED: &[&str] = &["cutoff", "q", "gain"];
57const CHAIGNE_ASKENFELT_NAMED: &[&str] = &[
58 "b",
59 "strike_pos",
60 "vel",
61 "hammer_mass",
62 "hammer_k",
63 "hammer_p",
64 "damp_dc",
65 "damp_freq",
66 "unison_count",
67 "detune",
68 "bridge_coupling",
69 "bridge_mass",
70 "string1_cents",
71 "string2_cents",
72 "string3_cents",
73 "string1_hammer_k_ratio",
74 "string2_hammer_k_ratio",
75 "string3_hammer_k_ratio",
76 "damper_pos",
77 "damper_r",
78 "damper_k",
79];
80const WILLEMSEN_BILBAO_SERAFIN_NAMED: &[&str] = &[
81 "b",
82 "bow_pos",
83 "bow_vel",
84 "bow_force",
85 "mu_s",
86 "mu_c",
87 "stribeck_vel",
88 "bristle_stiffness",
89 "bristle_damping",
90 "viscous_friction",
91 "damp_dc",
92 "damp_freq",
93];
94const DARABUNDIT_SCAVONE_NAMED: &[&str] = &[
96 "radius_in",
97 "radius_out",
98 "excite_pos",
99 "pulse_amp",
100 "pulse_width",
101 "damp_dc",
102 "damp_freq",
103 "hole1_pos",
104 "hole1_open",
105 "hole1_radius",
106 "hole1_height",
107 "hole2_pos",
108 "hole2_open",
109 "hole2_radius",
110 "hole2_height",
111 "hole3_pos",
112 "hole3_open",
113 "hole3_radius",
114 "hole3_height",
115 "hole4_pos",
116 "hole4_open",
117 "hole4_radius",
118 "hole4_height",
119 "hole5_pos",
120 "hole5_open",
121 "hole5_radius",
122 "hole5_height",
123 "hole6_pos",
124 "hole6_open",
125 "hole6_radius",
126 "hole6_height",
127];
128const RHAOUTI_CHAIGNE_JOLY_NAMED: &[&str] = &[
129 "aspect_ratio",
130 "strike_x",
131 "strike_y",
132 "vel",
133 "hammer_mass",
134 "hammer_k",
135 "hammer_p",
136 "damp_dc",
137 "damp_freq",
138];
139const CHAIGNE_DOUTAUT_NAMED: &[&str] = &[
140 "strike_pos",
141 "vel",
142 "hammer_mass",
143 "hammer_k",
144 "hammer_p",
145 "damp_dc",
146 "damp_freq",
147];
148const BOTTELDOOREN_NAMED: &[&str] = &[
149 "aspect_y",
150 "aspect_z",
151 "listener_x",
152 "listener_y",
153 "listener_z",
154 "pulse_amp",
155 "pulse_width",
156 "damp_dc",
157 "damp_freq",
158];
159
160pub fn named_may_move(name: &str, key: &str) -> bool {
163 (shape(name).is_some() && FILTER_NAMED.contains(&key))
164 || sva_samples::physics::varying(name)
165 .iter()
166 .any(|(k, _)| *k == key)
167}
168
169pub fn recognized_named(name: &str) -> Option<&'static [&'static str]> {
171 if shape(name).is_some() {
172 return Some(FILTER_NAMED);
173 }
174 Some(match name {
175 "sin" | "cos" | "exp" | "sqrt" | "abs" | "tanh" | "step" | "max" | "min" | "pow"
176 | "log" => NO_NAMED,
177 "saw" | "square" | "triangle" => WAVE_NAMED,
178 "sat" => SAT_NAMED,
179 SERIES | "pv" => NO_NAMED,
180 "crop" => CROP_NAMED,
181 "rand" => RAND_NAMED,
182 "delta" => DELTA_NAMED,
183 "chaigne_askenfelt" => CHAIGNE_ASKENFELT_NAMED,
184 "willemsen_bilbao_serafin" => WILLEMSEN_BILBAO_SERAFIN_NAMED,
185 "darabundit_scavone" => DARABUNDIT_SCAVONE_NAMED,
186 "rhaouti_chaigne_joly" => RHAOUTI_CHAIGNE_JOLY_NAMED,
187 "chaigne_doutaut" => CHAIGNE_DOUTAUT_NAMED,
188 "botteldooren" => BOTTELDOOREN_NAMED,
189 JOIN | CHANNEL => NO_NAMED,
190 "stft" => STFT_NAMED,
191 "sample" | "fourier" | "ifourier" | "istft" => NO_NAMED,
192 "noise" => NOISE_NAMED,
193 "string" => STRING_NAMED,
194 "membrane" => MEMBRANE_NAMED,
195 "bar" => BAR_NAMED,
196 "bore" => BORE_NAMED,
197 "room" => ROOM_NAMED,
198 "helmholtz" => HELMHOLTZ_NAMED,
199 "hammer_pulse" => HAMMER_NAMED,
200 _ => return None,
201 })
202}