1use std::collections::HashMap;
8
9use crate::math::Transcendental;
10use crate::traits::ParamValue;
11
12pub trait SampleBuiltin<T: Transcendental>: Send + Sync {
14 fn process_sample(&mut self, inputs: &[T]) -> T;
16 fn init(&mut self, _sample_rate: f32) {}
18 fn reset(&mut self);
20 fn set_param(&mut self, _index: usize, _value: &ParamValue) {}
22}
23
24pub trait BlockBuiltin<T: Transcendental>: crate::traits::Algorithm<T> {
26 fn set_param(&mut self, _index: usize, _value: &ParamValue) {}
28}
29
30pub trait MultichannelBlockBuiltin<T: Transcendental>:
32 crate::traits::MultichannelAlgorithm<T> + Send + Sync
33{
34 fn set_param(&mut self, _index: usize, _value: &ParamValue) {}
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum BuiltinKind {
41 Sample,
43 Block,
45}
46
47#[derive(Debug, Clone, PartialEq)]
49pub enum ParamType {
50 Signal,
52 Float,
54 Int,
56 String,
58 Bool,
60 Record(RecordSchema),
62 Enum(&'static [&'static str]),
64 Variadic(Box<ParamType>),
66}
67
68#[derive(Debug, Clone, PartialEq)]
70pub struct RecordSchema {
71 pub fields: Vec<RecordField>,
73}
74
75#[derive(Debug, Clone, PartialEq)]
77pub struct RecordField {
78 pub name: &'static str,
80 pub ty: ParamType,
82 pub default: Option<f64>,
84}
85
86impl RecordSchema {
87 pub fn new(fields: Vec<RecordField>) -> Self {
89 Self { fields }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq)]
95pub struct BuiltinSig {
96 pub name: &'static str,
98 pub params: Vec<ParamType>,
100 pub signal_outs: usize,
102 pub kind: BuiltinKind,
104 pub param_names: Vec<&'static str>,
109}
110
111impl BuiltinSig {
112 pub fn simple(
115 name: &'static str,
116 signal_ins: usize,
117 signal_outs: usize,
118 num_params: usize,
119 kind: BuiltinKind,
120 ) -> Self {
121 let mut params = Vec::with_capacity(signal_ins + num_params);
122 for _ in 0..signal_ins {
123 params.push(ParamType::Signal);
124 }
125 for _ in 0..num_params {
126 params.push(ParamType::Float);
127 }
128 Self {
129 name,
130 params,
131 signal_outs,
132 kind,
133 param_names: Vec::new(),
134 }
135 }
136
137 pub fn with_names(mut self, names: Vec<&'static str>) -> Self {
144 self.param_names = names;
145 self
146 }
147
148 pub fn signal_ins(&self) -> usize {
150 self.params
151 .iter()
152 .filter(|p| matches!(p, ParamType::Signal))
153 .count()
154 }
155
156 pub fn min_args(&self) -> usize {
158 let mut count = 0;
159 for p in &self.params {
160 match p {
161 ParamType::Signal | ParamType::Variadic(_) => {}
162 _ => count += 1,
163 }
164 }
165 count
166 }
167
168 pub fn max_args(&self) -> Option<usize> {
170 if self
171 .params
172 .iter()
173 .any(|p| matches!(p, ParamType::Variadic(_)))
174 {
175 None
176 } else {
177 Some(
178 self.params
179 .iter()
180 .filter(|p| !matches!(p, ParamType::Signal))
181 .count(),
182 )
183 }
184 }
185}
186
187type SampleFactory<T> = Box<dyn Fn(&[f64], f32) -> Box<dyn SampleBuiltin<T>> + Send + Sync>;
189type BlockFactory<T> = Box<dyn Fn(&[f64], f32) -> Box<dyn BlockBuiltin<T>> + Send + Sync>;
190type MultichannelBlockFactory<T> =
191 Box<dyn Fn(&[f64], f32) -> Box<dyn MultichannelBlockBuiltin<T>> + Send + Sync>;
192
193enum Factory<T: Transcendental> {
194 Sample(SampleFactory<T>),
195 Block(BlockFactory<T>),
196 MultichannelBlock(MultichannelBlockFactory<T>),
197}
198
199pub struct Entry<T: Transcendental> {
201 pub sig: BuiltinSig,
203 factory: Factory<T>,
204}
205
206impl<T: Transcendental> Entry<T> {
207 pub fn build_sample(
210 &self,
211 params: &[f64],
212 sample_rate: f32,
213 ) -> Option<Box<dyn SampleBuiltin<T>>> {
214 match &self.factory {
215 Factory::Sample(f) => Some(f(params, sample_rate)),
216 Factory::Block(_) | Factory::MultichannelBlock(_) => None,
217 }
218 }
219 pub fn build_block(
221 &self,
222 params: &[f64],
223 sample_rate: f32,
224 ) -> Option<Box<dyn BlockBuiltin<T>>> {
225 match &self.factory {
226 Factory::Block(f) => Some(f(params, sample_rate)),
227 Factory::Sample(_) | Factory::MultichannelBlock(_) => None,
228 }
229 }
230 pub fn build_multichannel_block(
232 &self,
233 params: &[f64],
234 sample_rate: f32,
235 ) -> Option<Box<dyn MultichannelBlockBuiltin<T>>> {
236 match &self.factory {
237 Factory::MultichannelBlock(f) => Some(f(params, sample_rate)),
238 _ => None,
239 }
240 }
241}
242
243pub struct Registry<T: Transcendental> {
245 entries: HashMap<String, Entry<T>>,
246}
247
248impl<T: Transcendental> Default for Registry<T> {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254impl<T: Transcendental> Registry<T> {
255 pub fn new() -> Self {
257 Self {
258 entries: HashMap::new(),
259 }
260 }
261
262 pub fn register_sample(
264 &mut self,
265 sig: BuiltinSig,
266 factory: impl Fn(&[f64], f32) -> Box<dyn SampleBuiltin<T>> + Send + Sync + 'static,
267 ) {
268 debug_assert_eq!(sig.kind, BuiltinKind::Sample);
269 self.entries.insert(
270 sig.name.to_string(),
271 Entry {
272 sig,
273 factory: Factory::Sample(Box::new(factory)),
274 },
275 );
276 }
277
278 pub fn register_block(
280 &mut self,
281 sig: BuiltinSig,
282 factory: impl Fn(&[f64], f32) -> Box<dyn BlockBuiltin<T>> + Send + Sync + 'static,
283 ) {
284 debug_assert_eq!(sig.kind, BuiltinKind::Block);
285 self.entries.insert(
286 sig.name.to_string(),
287 Entry {
288 sig,
289 factory: Factory::Block(Box::new(factory)),
290 },
291 );
292 }
293
294 pub fn register_multichannel_block(
296 &mut self,
297 sig: BuiltinSig,
298 factory: impl Fn(&[f64], f32) -> Box<dyn MultichannelBlockBuiltin<T>> + Send + Sync + 'static,
299 ) {
300 debug_assert_eq!(sig.kind, BuiltinKind::Block);
301 self.entries.insert(
302 sig.name.to_string(),
303 Entry {
304 sig,
305 factory: Factory::MultichannelBlock(Box::new(factory)),
306 },
307 );
308 }
309
310 pub fn get(&self, name: &str) -> Option<&Entry<T>> {
312 self.entries.get(name)
313 }
314}
315
316pub trait SignatureSource {
318 fn builtin_sig(&self, name: &str) -> Option<&BuiltinSig>;
320}
321
322impl<T: Transcendental> SignatureSource for Registry<T> {
323 fn builtin_sig(&self, name: &str) -> Option<&BuiltinSig> {
324 self.entries.get(name).map(|e| &e.sig)
325 }
326}
327
328pub struct NoSigs;
330impl SignatureSource for NoSigs {
331 fn builtin_sig(&self, _name: &str) -> Option<&BuiltinSig> {
332 None
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 struct Gain {
341 k: f32,
342 }
343 impl SampleBuiltin<f32> for Gain {
344 fn process_sample(&mut self, inputs: &[f32]) -> f32 {
345 inputs[0] * self.k
346 }
347 fn reset(&mut self) {}
348 }
349
350 #[test]
351 fn register_and_lookup_sample() {
352 let mut reg = Registry::<f32>::new();
353 reg.register_sample(
354 BuiltinSig::simple("gain", 1, 1, 1, BuiltinKind::Sample),
355 |params, _sr| {
356 Box::new(Gain {
357 k: params[0] as f32,
358 })
359 },
360 );
361 let sig = reg.builtin_sig("gain").unwrap();
362 assert_eq!((sig.signal_ins(), sig.params.len()), (1, 2));
363 let mut inst = reg
364 .get("gain")
365 .unwrap()
366 .build_sample(&[0.5], 44100.0)
367 .unwrap();
368 assert_eq!(inst.process_sample(&[2.0]), 1.0);
369 assert!(reg.builtin_sig("missing").is_none());
370 }
371}