1pub(crate) mod arrow;
4mod ball;
5pub mod botteldooren;
6pub mod bound;
7pub mod chaigne_askenfelt;
8pub mod chaigne_doutaut;
9pub mod darabundit_scavone;
10pub mod hammer;
11pub mod rhaouti_chaigne_joly;
12pub(crate) mod stiff_string;
13pub(crate) mod string_energy;
14pub mod tonehole;
15#[cfg(test)]
16mod twofold;
17mod unison_energy;
18pub mod willemsen_bilbao_serafin;
19
20use std::any::Any;
21
22use crate::error::SampleError;
23
24use botteldooren::{BotteldoorenParams, BotteldoorenSite};
25use chaigne_askenfelt::{ChaigneAskenfeltParams, ChaigneAskenfeltSite};
26use chaigne_doutaut::{ChaigneDoutautParams, ChaigneDoutautSite};
27use darabundit_scavone::{BoreParams, BoreSite};
28use rhaouti_chaigne_joly::{RhaoutiChaigneJolyParams, RhaoutiChaigneJolySite};
29use willemsen_bilbao_serafin::{WillemsenBilbaoSerafinParams, WillemsenBilbaoSerafinSite};
30
31pub trait Solver: Held + Send + Sync {
33 fn step(&mut self, args: &[f64]) -> Result<f64, SampleError>;
34
35 fn bytes(&self) -> usize;
37}
38
39pub trait Held {
40 fn boxed(&self) -> Box<dyn Solver>;
41 fn as_any(&self) -> &dyn Any;
42}
43
44impl<T: Solver + Clone + 'static> Held for T {
45 fn boxed(&self) -> Box<dyn Solver> {
46 Box::new(self.clone())
47 }
48
49 fn as_any(&self) -> &dyn Any {
50 self
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum Varies {
57 PerSample,
58 Piecewise,
60}
61
62pub const MAX_VARYING: usize = 2;
63
64#[derive(Clone, Debug, PartialEq)]
65pub enum Params {
66 ChaigneAskenfelt(ChaigneAskenfeltParams),
67 WillemsenBilbaoSerafin(WillemsenBilbaoSerafinParams),
68 DarabunditScavone(BoreParams),
69 RhaoutiChaigneJoly(RhaoutiChaigneJolyParams),
70 ChaigneDoutaut(ChaigneDoutautParams),
71 Botteldooren(BotteldoorenParams),
72}
73
74impl Params {
75 pub fn name(&self) -> &'static str {
76 match self {
77 Params::ChaigneAskenfelt(_) => "chaigne_askenfelt",
78 Params::WillemsenBilbaoSerafin(_) => "willemsen_bilbao_serafin",
79 Params::DarabunditScavone(_) => "darabundit_scavone",
80 Params::RhaoutiChaigneJoly(_) => "rhaouti_chaigne_joly",
81 Params::ChaigneDoutaut(_) => "chaigne_doutaut",
82 Params::Botteldooren(_) => "botteldooren",
83 }
84 }
85
86 pub fn varying(&self) -> &'static [(&'static str, Varies)] {
88 varying(self.name())
89 }
90
91 pub fn structural(&self) -> Params {
92 match self {
93 Params::ChaigneAskenfelt(p) => Params::ChaigneAskenfelt(ChaigneAskenfeltParams {
94 damper_r: 0.0,
95 damper_k: 0.0,
96 ..p.clone()
97 }),
98 Params::WillemsenBilbaoSerafin(p) => {
99 Params::WillemsenBilbaoSerafin(WillemsenBilbaoSerafinParams {
100 bow_vel: 0.0,
101 bow_force: 1.0,
102 ..p.clone()
103 })
104 }
105 other => other.clone(),
106 }
107 }
108
109 pub fn words(&self) -> Vec<u64> {
113 let model = match self {
114 Params::ChaigneAskenfelt(_) => 0,
115 Params::WillemsenBilbaoSerafin(_) => 1,
116 Params::DarabunditScavone(_) => 2,
117 Params::RhaoutiChaigneJoly(_) => 3,
118 Params::ChaigneDoutaut(_) => 4,
119 Params::Botteldooren(_) => 5,
120 };
121 let mut out = vec![model];
122 let mut put = |vs: &[f64]| out.extend(vs.iter().map(|v| v.to_bits()));
123 match self {
124 Params::ChaigneAskenfelt(ChaigneAskenfeltParams {
125 f0,
126 b,
127 strike_pos,
128 vel,
129 hammer_mass,
130 hammer_k,
131 hammer_p,
132 damp_dc,
133 damp_freq,
134 unison_count,
135 detune,
136 bridge_coupling,
137 bridge_mass,
138 string_cents,
139 string_hammer_k_ratio,
140 damper_pos,
141 damper_r,
142 damper_k,
143 }) => {
144 put(&[
145 *f0,
146 *b,
147 *strike_pos,
148 *vel,
149 *hammer_mass,
150 *hammer_k,
151 *hammer_p,
152 ]);
153 put(&[
154 *damp_dc,
155 *damp_freq,
156 *unison_count,
157 *detune,
158 *bridge_coupling,
159 ]);
160 put(&[*bridge_mass]);
161 put(string_cents);
162 put(string_hammer_k_ratio);
163 put(&[*damper_pos, *damper_r, *damper_k]);
164 }
165 Params::WillemsenBilbaoSerafin(WillemsenBilbaoSerafinParams {
166 f0,
167 b,
168 bow_pos,
169 bow_vel,
170 bow_force,
171 mu_s,
172 mu_c,
173 stribeck_vel,
174 bristle_stiffness,
175 bristle_damping,
176 viscous_friction,
177 damp_dc,
178 damp_freq,
179 }) => {
180 put(&[*f0, *b, *bow_pos, *bow_vel, *bow_force, *mu_s, *mu_c]);
181 put(&[*stribeck_vel, *bristle_stiffness, *bristle_damping]);
182 put(&[*viscous_friction, *damp_dc, *damp_freq]);
183 }
184 Params::DarabunditScavone(BoreParams {
185 length,
186 radius_in,
187 radius_out,
188 excite_pos,
189 pulse_amp,
190 pulse_width,
191 damp_dc,
192 damp_freq,
193 holes,
194 }) => {
195 put(&[*length, *radius_in, *radius_out, *excite_pos, *pulse_amp]);
196 put(&[*pulse_width, *damp_dc, *damp_freq]);
197 for hole in holes {
198 match hole {
199 None => put(&[0.0]),
200 Some(darabundit_scavone::ToneholeSpec {
201 pos,
202 open,
203 radius,
204 height,
205 }) => put(&[1.0, *pos, f64::from(u8::from(*open)), *radius, *height]),
206 }
207 }
208 }
209 Params::RhaoutiChaigneJoly(RhaoutiChaigneJolyParams {
210 f0,
211 aspect_ratio,
212 strike_x,
213 strike_y,
214 vel,
215 hammer_mass,
216 hammer_k,
217 hammer_p,
218 damp_dc,
219 damp_freq,
220 }) => {
221 put(&[*f0, *aspect_ratio, *strike_x, *strike_y, *vel, *hammer_mass]);
222 put(&[*hammer_k, *hammer_p, *damp_dc, *damp_freq]);
223 }
224 Params::ChaigneDoutaut(ChaigneDoutautParams {
225 f0,
226 strike_pos,
227 vel,
228 hammer_mass,
229 hammer_k,
230 hammer_p,
231 damp_dc,
232 damp_freq,
233 }) => {
234 put(&[*f0, *strike_pos, *vel, *hammer_mass, *hammer_k, *hammer_p]);
235 put(&[*damp_dc, *damp_freq]);
236 }
237 Params::Botteldooren(BotteldoorenParams {
238 f0,
239 aspect_y,
240 aspect_z,
241 listener_x,
242 listener_y,
243 listener_z,
244 pulse_amp,
245 pulse_width,
246 damp_dc,
247 damp_freq,
248 }) => {
249 put(&[
250 *f0,
251 *aspect_y,
252 *aspect_z,
253 *listener_x,
254 *listener_y,
255 *listener_z,
256 ]);
257 put(&[*pulse_amp, *pulse_width, *damp_dc, *damp_freq]);
258 }
259 }
260 out
261 }
262
263 pub fn valid(&self) -> bool {
264 match self {
265 Params::ChaigneAskenfelt(p) => p.valid(),
266 Params::WillemsenBilbaoSerafin(p) => p.valid(),
267 Params::DarabunditScavone(p) => p.valid(),
268 Params::RhaoutiChaigneJoly(p) => p.valid(),
269 Params::ChaigneDoutaut(p) => p.valid(),
270 Params::Botteldooren(p) => p.valid(),
271 }
272 }
273}
274
275pub fn varying(model: &str) -> &'static [(&'static str, Varies)] {
276 match model {
277 "chaigne_askenfelt" => chaigne_askenfelt::VARYING,
278 "willemsen_bilbao_serafin" => willemsen_bilbao_serafin::VARYING,
279 _ => &[],
280 }
281}
282
283pub fn site(p: &Params, sr: f64) -> Result<Box<dyn Solver>, SampleError> {
286 Ok(match p {
287 Params::ChaigneAskenfelt(p) => Box::new(ChaigneAskenfeltSite::new(p, sr)?),
288 Params::WillemsenBilbaoSerafin(p) => Box::new(WillemsenBilbaoSerafinSite::new(p, sr)?),
289 Params::DarabunditScavone(p) => Box::new(BoreSite::new(p, sr)),
290 Params::RhaoutiChaigneJoly(p) => {
291 under_ceiling(
292 "rhaouti_chaigne_joly",
293 rhaouti_chaigne_joly::node_count(p, sr),
294 rhaouti_chaigne_joly::NODE_COUNT_CEILING,
295 )?;
296 Box::new(RhaoutiChaigneJolySite::new(p, sr))
297 }
298 Params::ChaigneDoutaut(p) => Box::new(ChaigneDoutautSite::new(p, sr)),
299 Params::Botteldooren(p) => {
300 under_ceiling(
301 "botteldooren",
302 botteldooren::node_count(p.f0, p.aspect_y, p.aspect_z, sr),
303 botteldooren::NODE_COUNT_CEILING,
304 )?;
305 Box::new(BotteldoorenSite::new(p, sr))
306 }
307 })
308}
309
310pub(crate) fn floats(held: &[f64]) -> usize {
312 std::mem::size_of_val(held)
313}
314
315fn under_ceiling(model: &'static str, nodes: f64, ceiling: usize) -> Result<(), SampleError> {
316 match nodes > ceiling as f64 {
317 true => Err(SampleError::GridTooLarge {
318 model,
319 nodes: nodes as usize,
320 ceiling,
321 }),
322 false => Ok(()),
323 }
324}
325
326pub(crate) fn raised_cosine_pulse(t: f64, amp: f64, width: f64) -> f64 {
329 match t < width {
330 true => amp * 0.5 * (1.0 - (std::f64::consts::TAU * t / width).cos()),
331 false => 0.0,
332 }
333}