1use super::common::{sanitize_audio, EdgeDetector, Memo, GATE_HIGH_V, GATE_THRESHOLD_V};
4use crate::port::{GraphModule, ParamDef, ParamId, PortDef, PortSpec, PortValues, SignalKind};
5use crate::rng;
6use alloc::format;
7use alloc::vec;
8use alloc::vec::Vec;
9use core::f64::consts::TAU;
10use libm::Libm;
11
12pub struct Mixer {
16 num_channels: usize,
17 spec: PortSpec,
18}
19
20impl Mixer {
21 pub fn new(num_channels: usize) -> Self {
22 let inputs = (0..num_channels)
23 .map(|i| {
24 PortDef::new(i as u32, format!("ch{}", i), SignalKind::Audio).with_attenuverter()
25 })
26 .collect();
27
28 Self {
29 num_channels,
30 spec: PortSpec {
31 inputs,
32 outputs: vec![PortDef::new(100, "out", SignalKind::Audio)],
33 },
34 }
35 }
36}
37
38impl Default for Mixer {
39 fn default() -> Self {
40 Self::new(4)
41 }
42}
43
44impl GraphModule for Mixer {
45 fn port_spec(&self) -> &PortSpec {
46 &self.spec
47 }
48
49 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
50 let sum: f64 = (0..self.num_channels)
51 .map(|i| inputs.get_or(i as u32, 0.0))
52 .sum();
53 outputs.set(100, sum);
54 }
55
56 fn reset(&mut self) {}
57
58 fn set_sample_rate(&mut self, _: f64) {}
59
60 fn type_id(&self) -> &'static str {
61 "mixer"
62 }
63}
64
65pub struct Offset {
69 pub(crate) offset: f64,
70 spec: PortSpec,
71}
72
73impl Offset {
74 pub fn new(offset: f64) -> Self {
75 Self {
76 offset,
77 spec: PortSpec {
78 inputs: vec![PortDef::new(0, "in", SignalKind::CvBipolar)],
79 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
80 },
81 }
82 }
83
84 pub fn set_offset(&mut self, offset: f64) {
85 self.offset = offset;
86 }
87}
88
89impl Default for Offset {
90 fn default() -> Self {
91 Self::new(0.0)
92 }
93}
94
95impl GraphModule for Offset {
96 fn port_spec(&self) -> &PortSpec {
97 &self.spec
98 }
99
100 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
101 let input = inputs.get_or(0, 0.0);
102 outputs.set(10, input + self.offset);
103 }
104
105 fn reset(&mut self) {}
106
107 fn set_sample_rate(&mut self, _: f64) {}
108
109 fn type_id(&self) -> &'static str {
110 "offset"
111 }
112
113 fn params(&self) -> &[ParamDef] {
114 static PARAMS: &[ParamDef] = &[];
115 PARAMS
116 }
117
118 fn get_param(&self, id: ParamId) -> Option<f64> {
119 if id == 0 {
120 Some(self.offset)
121 } else {
122 None
123 }
124 }
125
126 fn set_param(&mut self, id: ParamId, value: f64) {
127 if id == 0 {
128 self.offset = value;
129 }
130 }
131
132 crate::impl_introspect!();
134}
135
136const NOTE_HYSTERESIS_SEMITONES: f64 = 0.3;
139
140fn hysteretic_note(
149 last: Option<f64>,
150 input_v: f64,
151 candidate_v: f64,
152 hysteresis_semitones: f64,
153) -> f64 {
154 match last {
155 None => candidate_v,
156 Some(last_v) => {
157 if candidate_v == last_v {
158 return last_v;
159 }
160 let in_s = input_v * 12.0;
161 let last_s = last_v * 12.0;
162 let cand_s = candidate_v * 12.0;
163 let boundary = (last_s + cand_s) * 0.5;
164 let commit = if cand_s > last_s {
165 in_s >= boundary + hysteresis_semitones
166 } else {
167 in_s <= boundary - hysteresis_semitones
168 };
169 if commit {
170 candidate_v
171 } else {
172 last_v
173 }
174 }
175 }
176}
177
178pub struct ScaleQuantizer {
183 last_output: Option<f64>,
185 custom_cents: Vec<f64>,
191 spec: PortSpec,
192}
193
194impl ScaleQuantizer {
195 const CHROMATIC: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
197 const MAJOR: [u8; 7] = [0, 2, 4, 5, 7, 9, 11];
198 const MINOR: [u8; 7] = [0, 2, 3, 5, 7, 8, 10];
199 const PENT_MAJOR: [u8; 5] = [0, 2, 4, 7, 9];
200 const PENT_MINOR: [u8; 5] = [0, 3, 5, 7, 10];
201 const DORIAN: [u8; 7] = [0, 2, 3, 5, 7, 9, 10];
202 const BLUES: [u8; 6] = [0, 3, 5, 6, 7, 10];
203
204 pub fn new(_sample_rate: f64) -> Self {
205 Self {
206 last_output: None,
207 custom_cents: Vec::new(),
208 spec: PortSpec {
209 inputs: vec![
210 PortDef::new(0, "in", SignalKind::VoltPerOctave),
211 PortDef::new(1, "root", SignalKind::CvUnipolar)
212 .with_default(0.0)
213 .with_attenuverter(),
214 PortDef::new(2, "scale", SignalKind::CvUnipolar)
215 .with_default(0.0)
216 .with_attenuverter(),
217 ],
218 outputs: vec![
219 PortDef::new(10, "out", SignalKind::VoltPerOctave),
220 PortDef::new(11, "trigger", SignalKind::Trigger),
221 ],
222 },
223 }
224 }
225
226 fn quantize_to_scale(note: i32, scale: &[u8]) -> i32 {
227 let octave = note.div_euclid(12);
228 let semitone = note.rem_euclid(12);
229
230 let mut closest = scale[0] as i32;
235 let mut min_dist = i32::MAX;
236
237 for &s in scale {
238 let s = s as i32;
239 let dist = (semitone - s).abs();
240 if dist < min_dist {
241 min_dist = dist;
242 closest = s;
243 }
244 let dist_wrap = (semitone - (s + 12)).abs();
245 if dist_wrap < min_dist {
246 min_dist = dist_wrap;
247 closest = s + 12;
248 }
249 }
250
251 octave * 12 + closest
252 }
253
254 pub fn has_custom_scale(&self) -> bool {
256 !self.custom_cents.is_empty()
257 }
258
259 #[cfg(feature = "alloc")]
266 pub fn set_custom_scale(&mut self, cents: &[f64]) {
267 let mut degrees: Vec<f64> = cents
268 .iter()
269 .map(|&c| {
270 let mut r = Libm::<f64>::fmod(c, 1200.0);
271 if r < 0.0 {
272 r += 1200.0;
273 }
274 r
275 })
276 .collect();
277 degrees.sort_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal));
278 degrees.dedup_by(|a, b| (*a - *b).abs() < 1e-6);
279 self.custom_cents = degrees;
280 }
281
282 #[cfg(feature = "alloc")]
284 pub fn clear_custom_scale(&mut self) {
285 self.custom_cents.clear();
286 }
287
288 #[cfg(feature = "alloc")]
296 pub fn load_scala(&mut self, source: &str) -> Result<(), crate::scala::ScalaError> {
297 let scale = crate::scala::ScalaScale::parse(source)?;
298 self.set_custom_scale(&scale.degrees_within_octave());
299 Ok(())
300 }
301
302 fn quantize_custom_cents(input_cents: f64, degrees: &[f64]) -> f64 {
309 if degrees.is_empty() {
310 return input_cents;
311 }
312 let octave = Libm::<f64>::floor(input_cents / 1200.0);
313 let within = input_cents - octave * 1200.0;
314
315 let mut closest = degrees[0];
316 let mut min_dist = f64::MAX;
317 for &d in degrees {
318 let dist = Libm::<f64>::fabs(within - d);
319 if dist < min_dist {
320 min_dist = dist;
321 closest = d;
322 }
323 let dist_wrap = Libm::<f64>::fabs(within - (d + 1200.0));
324 if dist_wrap < min_dist {
325 min_dist = dist_wrap;
326 closest = d + 1200.0;
327 }
328 }
329
330 octave * 1200.0 + closest
331 }
332}
333
334impl Default for ScaleQuantizer {
335 fn default() -> Self {
336 Self::new(44100.0)
337 }
338}
339
340impl GraphModule for ScaleQuantizer {
341 fn port_spec(&self) -> &PortSpec {
342 &self.spec
343 }
344
345 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
346 let input = inputs.get_or(0, 0.0);
347 let root_cv = inputs.get_or(1, 0.0).clamp(0.0, 1.0);
348 let scale_cv = inputs.get_or(2, 0.0).clamp(0.0, 1.0);
349
350 let root = (root_cv * 11.99) as i32;
352
353 let candidate_voct = if !self.custom_cents.is_empty() {
356 let root_cents = root as f64 * 100.0;
357 let input_cents = input * 1200.0 - root_cents;
358 let q_cents = Self::quantize_custom_cents(input_cents, &self.custom_cents);
359 (q_cents + root_cents) / 1200.0
360 } else {
361 let semitones_from_c4 = Libm::<f64>::round(input * 12.0) as i32;
363
364 let relative_note = semitones_from_c4 - root;
366
367 let scale_idx = (scale_cv * 6.99) as u8;
369 let quantized = match scale_idx {
370 0 => Self::quantize_to_scale(relative_note, &Self::CHROMATIC),
371 1 => Self::quantize_to_scale(relative_note, &Self::MAJOR),
372 2 => Self::quantize_to_scale(relative_note, &Self::MINOR),
373 3 => Self::quantize_to_scale(relative_note, &Self::PENT_MAJOR),
374 4 => Self::quantize_to_scale(relative_note, &Self::PENT_MINOR),
375 5 => Self::quantize_to_scale(relative_note, &Self::DORIAN),
376 _ => Self::quantize_to_scale(relative_note, &Self::BLUES),
377 };
378
379 (quantized + root) as f64 / 12.0
381 };
382
383 let prev = self.last_output;
387 let output_voct = hysteretic_note(prev, input, candidate_voct, NOTE_HYSTERESIS_SEMITONES);
388 let trigger = match prev {
389 Some(p) if (p - output_voct).abs() > 1e-9 => GATE_HIGH_V,
390 _ => 0.0,
391 };
392 self.last_output = Some(output_voct);
393
394 outputs.set(10, output_voct);
395 outputs.set(11, trigger);
396 }
397
398 fn reset(&mut self) {
399 self.last_output = None;
400 }
401
402 fn set_sample_rate(&mut self, _: f64) {}
403
404 fn type_id(&self) -> &'static str {
405 "scale_quantizer"
406 }
407
408 #[cfg(feature = "alloc")]
413 fn serialize_state(&self) -> Option<serde_json::Value> {
414 if self.custom_cents.is_empty() {
415 return None;
416 }
417 let cents = serde_json::to_value(&self.custom_cents).ok()?;
418 let mut map = serde_json::Map::new();
419 map.insert(alloc::string::String::from("custom_cents"), cents);
420 Some(serde_json::Value::Object(map))
421 }
422
423 #[cfg(feature = "alloc")]
427 fn deserialize_state(
428 &mut self,
429 state: &serde_json::Value,
430 ) -> Result<(), alloc::string::String> {
431 let Some(cents_val) = state.get("custom_cents") else {
432 return Ok(());
433 };
434 let cents: Vec<f64> = serde_json::from_value(cents_val.clone())
435 .map_err(|e| format!("ScaleQuantizer custom_cents: {e}"))?;
436 self.set_custom_scale(¢s);
437 Ok(())
438 }
439}
440
441pub struct Euclidean {
446 step: usize,
447 pattern: Vec<bool>,
448 last_pulses: usize,
451 clock_edge: EdgeDetector,
453 reset_edge: EdgeDetector,
455 cycle_accented: bool,
457 spec: PortSpec,
458}
459
460impl Euclidean {
461 pub fn new(_sample_rate: f64) -> Self {
462 Self {
463 step: 0,
464 pattern: vec![true; 16],
465 last_pulses: 16,
466 clock_edge: EdgeDetector::new(),
467 reset_edge: EdgeDetector::new(),
468 cycle_accented: false,
469 spec: PortSpec {
470 inputs: vec![
471 PortDef::new(0, "clock", SignalKind::Trigger),
472 PortDef::new(1, "steps", SignalKind::CvUnipolar)
473 .with_default(0.5)
474 .with_attenuverter(),
475 PortDef::new(2, "pulses", SignalKind::CvUnipolar)
476 .with_default(0.25)
477 .with_attenuverter(),
478 PortDef::new(3, "rotation", SignalKind::CvUnipolar)
479 .with_default(0.0)
480 .with_attenuverter(),
481 PortDef::new(4, "reset", SignalKind::Trigger),
482 ],
483 outputs: vec![
484 PortDef::new(10, "out", SignalKind::Trigger),
485 PortDef::new(11, "accent", SignalKind::Trigger),
486 ],
487 },
488 }
489 }
490
491 fn generate_pattern(steps: usize, pulses: usize) -> Vec<bool> {
492 if steps == 0 || pulses == 0 {
493 return vec![false; steps.max(1)];
494 }
495
496 let pulses = pulses.min(steps);
497 let mut pattern = vec![false; steps];
498
499 let mut bucket = 0;
501 for slot in pattern.iter_mut().take(steps) {
502 bucket += pulses;
503 if bucket >= steps {
504 bucket -= steps;
505 *slot = true;
506 }
507 }
508
509 pattern
510 }
511}
512
513impl Default for Euclidean {
514 fn default() -> Self {
515 Self::new(44100.0)
516 }
517}
518
519impl GraphModule for Euclidean {
520 fn port_spec(&self) -> &PortSpec {
521 &self.spec
522 }
523
524 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
525 let clock = inputs.get_or(0, 0.0);
526 let steps_cv = inputs.get_or(1, 0.5).clamp(0.0, 1.0);
527 let pulses_cv = inputs.get_or(2, 0.25).clamp(0.0, 1.0);
528 let rotation_cv = inputs.get_or(3, 0.0).clamp(0.0, 1.0);
529 let reset = inputs.get_or(4, 0.0);
530
531 let steps = 2 + (steps_cv * 14.99) as usize;
533 let pulses = (pulses_cv * steps as f64) as usize;
534
535 if self.pattern.len() != steps || self.last_pulses != pulses {
538 self.pattern = Self::generate_pattern(steps, pulses);
539 self.last_pulses = pulses;
540 }
541
542 if self.reset_edge.rising(reset) {
544 self.step = 0;
545 self.cycle_accented = false;
546 }
547
548 let trigger = self.clock_edge.rising(clock);
550
551 let mut out = 0.0;
552 let mut accent = 0.0;
553
554 if trigger {
555 let rotation = ((rotation_cv * steps as f64) as usize).min(steps - 1);
558
559 if self.step == 0 {
561 self.cycle_accented = false;
562 }
563
564 let rotated_step = (self.step + rotation) % steps;
565
566 if self.pattern[rotated_step] {
567 out = GATE_HIGH_V;
568 if !self.cycle_accented {
573 accent = GATE_HIGH_V;
574 self.cycle_accented = true;
575 }
576 }
577
578 self.step = (self.step + 1) % steps;
579 }
580
581 outputs.set(10, out);
582 outputs.set(11, accent);
583 }
584
585 fn reset(&mut self) {
586 self.step = 0;
587 self.cycle_accented = false;
588 self.clock_edge.reset();
589 self.reset_edge.reset();
590 }
591
592 fn set_sample_rate(&mut self, _: f64) {}
593
594 fn type_id(&self) -> &'static str {
595 "euclidean"
596 }
597}
598
599pub struct Crosstalk {
607 sample_rate: f64,
608 hf_state: [f64; 2],
610 spec: PortSpec,
611}
612
613impl Crosstalk {
614 pub fn new(sample_rate: f64) -> Self {
615 Self {
616 sample_rate,
617 hf_state: [0.0; 2],
618 spec: PortSpec {
619 inputs: vec![
620 PortDef::new(0, "in_a", SignalKind::Audio),
621 PortDef::new(1, "in_b", SignalKind::Audio),
622 PortDef::new(2, "amount", SignalKind::CvUnipolar).with_default(0.01),
624 PortDef::new(3, "hf_emphasis", SignalKind::CvUnipolar).with_default(0.5),
626 ],
627 outputs: vec![
628 PortDef::new(10, "out_a", SignalKind::Audio),
629 PortDef::new(11, "out_b", SignalKind::Audio),
630 ],
631 },
632 }
633 }
634}
635
636impl Default for Crosstalk {
637 fn default() -> Self {
638 Self::new(44100.0)
639 }
640}
641
642impl GraphModule for Crosstalk {
643 fn port_spec(&self) -> &PortSpec {
644 &self.spec
645 }
646
647 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
648 let in_a = sanitize_audio(inputs.get_or(0, 0.0));
649 let in_b = sanitize_audio(inputs.get_or(1, 0.0));
650 let amount = inputs.get_or(2, 0.01).clamp(0.0, 0.5);
651 let hf_emphasis = inputs.get_or(3, 0.5).clamp(0.0, 1.0);
652
653 let hf_coef = 0.1 + hf_emphasis * 0.4;
655
656 let hf_a = in_a - self.hf_state[0];
658 let hf_b = in_b - self.hf_state[1];
659 self.hf_state[0] += hf_coef * (in_a - self.hf_state[0]);
660 self.hf_state[1] += hf_coef * (in_b - self.hf_state[1]);
661
662 let crosstalk_to_a = (in_b * (1.0 - hf_emphasis) + hf_b * hf_emphasis) * amount;
664 let crosstalk_to_b = (in_a * (1.0 - hf_emphasis) + hf_a * hf_emphasis) * amount;
665
666 outputs.set(10, in_a + crosstalk_to_a);
667 outputs.set(11, in_b + crosstalk_to_b);
668 }
669
670 fn reset(&mut self) {
671 self.hf_state = [0.0; 2];
672 }
673
674 fn set_sample_rate(&mut self, sample_rate: f64) {
675 self.sample_rate = sample_rate;
676 }
677
678 fn type_id(&self) -> &'static str {
679 "crosstalk"
680 }
681}
682
683pub struct GroundLoop {
691 sample_rate: f64,
692 phase: f64,
694 pub(crate) frequency: f64,
696 thermal_state: f64,
698 spec: PortSpec,
699}
700
701impl GroundLoop {
702 pub fn new(sample_rate: f64) -> Self {
703 Self {
704 sample_rate,
705 phase: 0.0,
706 frequency: 60.0, thermal_state: 0.0,
708 spec: PortSpec {
709 inputs: vec![
710 PortDef::new(0, "in", SignalKind::Audio),
711 PortDef::new(1, "level", SignalKind::CvUnipolar).with_default(0.005),
713 PortDef::new(2, "modulation", SignalKind::CvUnipolar).with_default(0.1),
715 PortDef::new(3, "freq_select", SignalKind::CvUnipolar).with_default(1.0),
717 ],
718 outputs: vec![PortDef::new(10, "out", SignalKind::Audio)],
719 },
720 }
721 }
722
723 pub fn hz_50(sample_rate: f64) -> Self {
725 let mut gl = Self::new(sample_rate);
726 gl.frequency = 50.0;
727 gl
728 }
729
730 pub fn hz_60(sample_rate: f64) -> Self {
732 let mut gl = Self::new(sample_rate);
733 gl.frequency = 60.0;
734 gl
735 }
736}
737
738impl Default for GroundLoop {
739 fn default() -> Self {
740 Self::new(44100.0)
741 }
742}
743
744impl GraphModule for GroundLoop {
745 fn port_spec(&self) -> &PortSpec {
746 &self.spec
747 }
748
749 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
750 let input = sanitize_audio(inputs.get_or(0, 0.0));
751 let level = inputs.get_or(1, 0.005).clamp(0.0, 0.1);
752 let modulation = inputs.get_or(2, 0.1).clamp(0.0, 1.0);
753 let freq_select = inputs.get_or(3, 1.0);
754
755 let freq = if freq_select > 0.5 { 60.0 } else { 50.0 };
757
758 let signal_energy = Libm::<f64>::pow(input / 5.0, 2.0);
760 self.thermal_state += (signal_energy - self.thermal_state) * 0.0001;
761
762 let modulated_level = level * (1.0 + self.thermal_state * modulation * 10.0);
764
765 let fundamental = Libm::<f64>::sin(self.phase * TAU);
767 let second_harmonic = Libm::<f64>::sin(self.phase * 2.0 * TAU) * 0.5;
768 let third_harmonic = Libm::<f64>::sin(self.phase * 3.0 * TAU) * 0.25;
769 let hum = (fundamental + second_harmonic + third_harmonic) * modulated_level * 5.0;
770
771 let new_phase = self.phase + freq / self.sample_rate;
773 self.phase = new_phase - Libm::<f64>::floor(new_phase);
774
775 outputs.set(10, input + hum);
776 }
777
778 fn reset(&mut self) {
779 self.phase = 0.0;
780 self.thermal_state = 0.0;
781 }
782
783 fn set_sample_rate(&mut self, sample_rate: f64) {
784 self.sample_rate = sample_rate;
785 }
786
787 fn type_id(&self) -> &'static str {
788 "ground_loop"
789 }
790}
791
792pub struct StepSequencer {
796 steps: [f64; 8],
797 gates: [bool; 8],
798 current: usize,
799 prev_clock: f64,
800 prev_reset: f64,
801 spec: PortSpec,
802}
803
804impl StepSequencer {
805 pub fn new() -> Self {
806 Self {
807 steps: [0.0; 8],
808 gates: [true; 8],
809 current: 0,
810 prev_clock: 0.0,
811 prev_reset: 0.0,
812 spec: PortSpec {
813 inputs: vec![
814 PortDef::new(0, "clock", SignalKind::Clock),
815 PortDef::new(1, "reset", SignalKind::Trigger),
816 ],
817 outputs: vec![
818 PortDef::new(10, "cv", SignalKind::VoltPerOctave),
819 PortDef::new(11, "gate", SignalKind::Gate),
820 PortDef::new(12, "trig", SignalKind::Trigger),
821 ],
822 },
823 }
824 }
825
826 pub fn set_step(&mut self, index: usize, voltage: f64, gate: bool) {
827 if index < 8 {
828 self.steps[index] = voltage;
829 self.gates[index] = gate;
830 }
831 }
832
833 pub fn get_step(&self, index: usize) -> Option<(f64, bool)> {
834 if index < 8 {
835 Some((self.steps[index], self.gates[index]))
836 } else {
837 None
838 }
839 }
840}
841
842impl Default for StepSequencer {
843 fn default() -> Self {
844 Self::new()
845 }
846}
847
848impl GraphModule for StepSequencer {
849 fn port_spec(&self) -> &PortSpec {
850 &self.spec
851 }
852
853 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
854 let clock = inputs.get_or(0, 0.0);
855 let reset = inputs.get_or(1, 0.0);
856
857 let clock_rising = clock > GATE_THRESHOLD_V && self.prev_clock <= GATE_THRESHOLD_V;
858 let reset_rising = reset > GATE_THRESHOLD_V && self.prev_reset <= GATE_THRESHOLD_V;
859
860 let mut trigger = 0.0;
861
862 if reset_rising {
863 self.current = 0;
864 trigger = GATE_HIGH_V;
865 } else if clock_rising {
866 self.current = (self.current + 1) % 8;
867 trigger = GATE_HIGH_V;
868 }
869
870 self.prev_clock = clock;
871 self.prev_reset = reset;
872
873 let cv = self.steps[self.current];
874 let gate = if self.gates[self.current] && clock > GATE_THRESHOLD_V {
875 5.0
876 } else {
877 0.0
878 };
879
880 outputs.set(10, cv);
881 outputs.set(11, gate);
882 outputs.set(12, trigger);
883 }
884
885 fn reset(&mut self) {
886 self.current = 0;
887 self.prev_clock = 0.0;
888 self.prev_reset = 0.0;
889 }
890
891 fn set_sample_rate(&mut self, _: f64) {}
892
893 fn type_id(&self) -> &'static str {
894 "step_sequencer"
895 }
896
897 crate::impl_introspect!();
899}
900
901pub struct StereoOutput {
906 spec: PortSpec,
907}
908
909impl StereoOutput {
910 pub fn new() -> Self {
911 Self {
912 spec: PortSpec {
913 inputs: vec![
914 PortDef::new(0, "left", SignalKind::Audio),
915 PortDef::new(1, "right", SignalKind::Audio).normalled_to(0),
916 ],
917 outputs: vec![
918 PortDef::new(0, "left", SignalKind::Audio),
919 PortDef::new(1, "right", SignalKind::Audio),
920 ],
921 },
922 }
923 }
924}
925
926impl Default for StereoOutput {
927 fn default() -> Self {
928 Self::new()
929 }
930}
931
932impl GraphModule for StereoOutput {
933 fn port_spec(&self) -> &PortSpec {
934 &self.spec
935 }
936
937 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
938 let left = inputs.get_or(0, 0.0);
939 let right = inputs.get_or(1, left); outputs.set(0, left);
942 outputs.set(1, right);
943 }
944
945 fn reset(&mut self) {}
946
947 fn set_sample_rate(&mut self, _: f64) {}
948
949 fn type_id(&self) -> &'static str {
950 "stereo_output"
951 }
952}
953
954pub struct SampleAndHold {
958 held_value: f64,
959 trigger_edge: EdgeDetector,
960 spec: PortSpec,
961}
962
963impl SampleAndHold {
964 pub fn new() -> Self {
965 Self {
966 held_value: 0.0,
967 trigger_edge: EdgeDetector::new(),
968 spec: PortSpec {
969 inputs: vec![
970 PortDef::new(0, "in", SignalKind::CvBipolar),
971 PortDef::new(1, "trig", SignalKind::Trigger),
972 ],
973 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
974 },
975 }
976 }
977}
978
979impl Default for SampleAndHold {
980 fn default() -> Self {
981 Self::new()
982 }
983}
984
985impl GraphModule for SampleAndHold {
986 fn port_spec(&self) -> &PortSpec {
987 &self.spec
988 }
989
990 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
991 let input = inputs.get_or(0, 0.0);
992 let trigger = inputs.get_or(1, 0.0);
993
994 if self.trigger_edge.rising(trigger) {
996 self.held_value = input;
997 }
998
999 outputs.set(10, self.held_value);
1000 }
1001
1002 fn reset(&mut self) {
1003 self.held_value = 0.0;
1004 self.trigger_edge.reset();
1005 }
1006
1007 fn set_sample_rate(&mut self, _: f64) {}
1008
1009 fn type_id(&self) -> &'static str {
1010 "sample_hold"
1011 }
1012}
1013
1014pub struct SlewLimiter {
1019 current: f64,
1020 sample_rate: f64,
1021 rise_memo: Memo<2, f64>,
1023 fall_memo: Memo<2, f64>,
1025 spec: PortSpec,
1026}
1027
1028impl SlewLimiter {
1029 pub fn new(sample_rate: f64) -> Self {
1030 Self {
1031 current: 0.0,
1032 sample_rate,
1033 rise_memo: Memo::new(0.0),
1034 fall_memo: Memo::new(0.0),
1035 spec: PortSpec {
1036 inputs: vec![
1037 PortDef::new(0, "in", SignalKind::CvBipolar),
1038 PortDef::new(1, "rise", SignalKind::CvUnipolar)
1039 .with_default(0.5)
1040 .with_attenuverter(),
1041 PortDef::new(2, "fall", SignalKind::CvUnipolar)
1042 .with_default(0.5)
1043 .with_attenuverter(),
1044 ],
1045 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
1046 },
1047 }
1048 }
1049
1050 fn cv_to_rate(cv: f64, sample_rate: f64) -> f64 {
1051 let time = 0.001 + Libm::<f64>::pow(cv.clamp(0.0, 1.0), 2.0) * 10.0; 1.0 / (time * sample_rate)
1055 }
1056}
1057
1058impl Default for SlewLimiter {
1059 fn default() -> Self {
1060 Self::new(44100.0)
1061 }
1062}
1063
1064impl GraphModule for SlewLimiter {
1065 fn port_spec(&self) -> &PortSpec {
1066 &self.spec
1067 }
1068
1069 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1070 let target = inputs.get_or(0, 0.0);
1071 let rise_cv = inputs.get_or(1, 0.5);
1072 let fall_cv = inputs.get_or(2, 0.5);
1073
1074 let diff = target - self.current;
1075
1076 let sample_rate = self.sample_rate;
1079 if diff > 0.0 {
1080 let rate = self.rise_memo.get_or_compute([rise_cv, sample_rate], || {
1082 Self::cv_to_rate(rise_cv, sample_rate)
1083 });
1084 self.current += Libm::<f64>::fmin(diff, rate * 10.0); } else if diff < 0.0 {
1086 let rate = self.fall_memo.get_or_compute([fall_cv, sample_rate], || {
1088 Self::cv_to_rate(fall_cv, sample_rate)
1089 });
1090 self.current += Libm::<f64>::fmax(diff, -rate * 10.0);
1091 }
1092
1093 outputs.set(10, self.current);
1094 }
1095
1096 fn reset(&mut self) {
1097 self.current = 0.0;
1098 }
1099
1100 fn set_sample_rate(&mut self, sample_rate: f64) {
1101 self.sample_rate = sample_rate;
1102 }
1103
1104 fn type_id(&self) -> &'static str {
1105 "slew_limiter"
1106 }
1107}
1108
1109pub struct Quantizer {
1114 pub(crate) scale: Scale,
1115 last_output: Option<f64>,
1117 spec: PortSpec,
1118}
1119
1120#[derive(Debug, Clone, Copy, PartialEq)]
1122pub enum Scale {
1123 Chromatic,
1124 Major,
1125 Minor,
1126 PentatonicMajor,
1127 PentatonicMinor,
1128 Dorian,
1129 Mixolydian,
1130 Blues,
1131}
1132
1133impl Scale {
1134 fn semitones(&self) -> &'static [i32] {
1136 match self {
1137 Scale::Chromatic => &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
1138 Scale::Major => &[0, 2, 4, 5, 7, 9, 11],
1139 Scale::Minor => &[0, 2, 3, 5, 7, 8, 10],
1140 Scale::PentatonicMajor => &[0, 2, 4, 7, 9],
1141 Scale::PentatonicMinor => &[0, 3, 5, 7, 10],
1142 Scale::Dorian => &[0, 2, 3, 5, 7, 9, 10],
1143 Scale::Mixolydian => &[0, 2, 4, 5, 7, 9, 10],
1144 Scale::Blues => &[0, 3, 5, 6, 7, 10],
1145 }
1146 }
1147}
1148
1149impl Quantizer {
1150 pub fn new(scale: Scale) -> Self {
1151 Self {
1152 scale,
1153 last_output: None,
1154 spec: PortSpec {
1155 inputs: vec![PortDef::new(0, "in", SignalKind::VoltPerOctave)],
1156 outputs: vec![PortDef::new(10, "out", SignalKind::VoltPerOctave)],
1157 },
1158 }
1159 }
1160
1161 pub fn chromatic() -> Self {
1162 Self::new(Scale::Chromatic)
1163 }
1164
1165 pub fn major() -> Self {
1166 Self::new(Scale::Major)
1167 }
1168
1169 pub fn minor() -> Self {
1170 Self::new(Scale::Minor)
1171 }
1172
1173 pub fn set_scale(&mut self, scale: Scale) {
1174 self.scale = scale;
1175 }
1176
1177 fn quantize(&self, voltage: f64) -> f64 {
1178 let semitones = self.scale.semitones();
1179
1180 let total_semitones = voltage * 12.0;
1182
1183 let octave = Libm::<f64>::floor(total_semitones / 12.0);
1185 let within_octave = total_semitones - octave * 12.0;
1186
1187 let mut nearest = semitones[0];
1189 let mut min_dist = f64::MAX;
1190
1191 for &semi in semitones {
1192 let dist = (within_octave - semi as f64).abs();
1193 if dist < min_dist {
1194 min_dist = dist;
1195 nearest = semi;
1196 }
1197 let dist_wrap = (within_octave - (semi + 12) as f64).abs();
1199 if dist_wrap < min_dist {
1200 min_dist = dist_wrap;
1201 nearest = semi + 12;
1202 }
1203 }
1204
1205 (octave * 12.0 + nearest as f64) / 12.0
1207 }
1208}
1209
1210impl Default for Quantizer {
1211 fn default() -> Self {
1212 Self::chromatic()
1213 }
1214}
1215
1216impl GraphModule for Quantizer {
1217 fn port_spec(&self) -> &PortSpec {
1218 &self.spec
1219 }
1220
1221 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1222 let input = inputs.get_or(0, 0.0);
1223 let candidate = self.quantize(input);
1224 let committed = hysteretic_note(
1227 self.last_output,
1228 input,
1229 candidate,
1230 NOTE_HYSTERESIS_SEMITONES,
1231 );
1232 self.last_output = Some(committed);
1233 outputs.set(10, committed);
1234 }
1235
1236 fn reset(&mut self) {
1237 self.last_output = None;
1238 }
1239
1240 fn set_sample_rate(&mut self, _: f64) {}
1241
1242 fn type_id(&self) -> &'static str {
1243 "quantizer"
1244 }
1245
1246 crate::impl_introspect!();
1248}
1249
1250pub struct Clock {
1254 phase: f64,
1255 cycle: u64,
1258 sample_rate: f64,
1259 bpm_memo: Memo<1, f64>,
1262 spec: PortSpec,
1263}
1264
1265impl Clock {
1266 const DEFAULT_BPM_CV: f64 = 6.616_418_958_920_283;
1272
1273 pub fn new(sample_rate: f64) -> Self {
1274 Self {
1275 phase: 0.0,
1276 cycle: 0,
1277 sample_rate,
1278 bpm_memo: Memo::new(0.0),
1279 spec: PortSpec {
1280 inputs: vec![
1281 PortDef::new(0, "bpm", SignalKind::CvUnipolar)
1282 .with_default(Self::DEFAULT_BPM_CV) .with_attenuverter(),
1284 PortDef::new(1, "reset", SignalKind::Trigger),
1285 ],
1286 outputs: vec![
1287 PortDef::new(10, "out", SignalKind::Clock),
1288 PortDef::new(11, "div2", SignalKind::Clock),
1289 PortDef::new(12, "div4", SignalKind::Clock),
1290 ],
1291 },
1292 }
1293 }
1294
1295 fn cv_to_bpm(cv: f64) -> f64 {
1296 20.0 * Libm::<f64>::pow(15.0, cv / 10.0)
1298 }
1299}
1300
1301impl Default for Clock {
1302 fn default() -> Self {
1303 Self::new(44100.0)
1304 }
1305}
1306
1307impl GraphModule for Clock {
1308 fn port_spec(&self) -> &PortSpec {
1309 &self.spec
1310 }
1311
1312 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1313 let bpm_cv = inputs.get_or(0, Self::DEFAULT_BPM_CV); let reset = inputs.get_or(1, 0.0);
1315
1316 let bpm = self
1318 .bpm_memo
1319 .get_or_compute([bpm_cv], || Self::cv_to_bpm(bpm_cv));
1320 let freq = bpm / 60.0; if reset > GATE_THRESHOLD_V {
1324 self.phase = 0.0;
1325 self.cycle = 0;
1326 }
1327
1328 let pulse_width = 0.1; let in_pulse = self.phase < pulse_width;
1331 let main_out = if in_pulse { GATE_HIGH_V } else { 0.0 };
1332
1333 let div2_out = if in_pulse && (self.cycle & 1) == 0 {
1339 GATE_HIGH_V
1340 } else {
1341 0.0
1342 };
1343 let div4_out = if in_pulse && (self.cycle & 3) == 0 {
1344 GATE_HIGH_V
1345 } else {
1346 0.0
1347 };
1348
1349 outputs.set(10, main_out);
1350 outputs.set(11, div2_out);
1351 outputs.set(12, div4_out);
1352
1353 let new_phase = self.phase + freq / self.sample_rate;
1355 let wraps = Libm::<f64>::floor(new_phase);
1356 if wraps > 0.0 {
1357 self.cycle = self.cycle.wrapping_add(wraps as u64);
1358 }
1359 self.phase = new_phase - wraps;
1360 }
1361
1362 fn reset(&mut self) {
1363 self.phase = 0.0;
1364 self.cycle = 0;
1365 }
1366
1367 fn set_sample_rate(&mut self, sample_rate: f64) {
1368 self.sample_rate = sample_rate;
1369 }
1370
1371 fn type_id(&self) -> &'static str {
1372 "clock"
1373 }
1374}
1375
1376pub struct Attenuverter {
1381 spec: PortSpec,
1382}
1383
1384impl Attenuverter {
1385 pub fn new() -> Self {
1386 Self {
1387 spec: PortSpec {
1388 inputs: vec![
1389 PortDef::new(0, "in", SignalKind::CvBipolar),
1390 PortDef::new(1, "level", SignalKind::CvBipolar).with_default(5.0), ],
1392 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
1393 },
1394 }
1395 }
1396}
1397
1398impl Default for Attenuverter {
1399 fn default() -> Self {
1400 Self::new()
1401 }
1402}
1403
1404impl GraphModule for Attenuverter {
1405 fn port_spec(&self) -> &PortSpec {
1406 &self.spec
1407 }
1408
1409 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1410 let input = inputs.get_or(0, 0.0);
1411 let level = inputs.get_or(1, 5.0) / 5.0; outputs.set(10, input * level);
1414 }
1415
1416 fn reset(&mut self) {}
1417
1418 fn set_sample_rate(&mut self, _: f64) {}
1419
1420 fn type_id(&self) -> &'static str {
1421 "attenuverter"
1422 }
1423}
1424
1425pub struct Multiple {
1430 spec: PortSpec,
1431}
1432
1433impl Multiple {
1434 pub fn new() -> Self {
1435 Self {
1436 spec: PortSpec {
1437 inputs: vec![PortDef::new(0, "in", SignalKind::CvBipolar)],
1438 outputs: vec![
1439 PortDef::new(10, "out1", SignalKind::CvBipolar),
1440 PortDef::new(11, "out2", SignalKind::CvBipolar),
1441 PortDef::new(12, "out3", SignalKind::CvBipolar),
1442 PortDef::new(13, "out4", SignalKind::CvBipolar),
1443 ],
1444 },
1445 }
1446 }
1447}
1448
1449impl Default for Multiple {
1450 fn default() -> Self {
1451 Self::new()
1452 }
1453}
1454
1455impl GraphModule for Multiple {
1456 fn port_spec(&self) -> &PortSpec {
1457 &self.spec
1458 }
1459
1460 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1461 let input = inputs.get_or(0, 0.0);
1462
1463 outputs.set(10, input);
1464 outputs.set(11, input);
1465 outputs.set(12, input);
1466 outputs.set(13, input);
1467 }
1468
1469 fn reset(&mut self) {}
1470
1471 fn set_sample_rate(&mut self, _: f64) {}
1472
1473 fn type_id(&self) -> &'static str {
1474 "multiple"
1475 }
1476}
1477
1478pub struct Crossfader {
1487 spec: PortSpec,
1488}
1489
1490impl Crossfader {
1491 pub fn new() -> Self {
1492 Self {
1493 spec: PortSpec {
1494 inputs: vec![
1495 PortDef::new(0, "a", SignalKind::Audio),
1496 PortDef::new(1, "b", SignalKind::Audio),
1497 PortDef::new(2, "pos", SignalKind::CvBipolar).with_default(0.0),
1498 ],
1499 outputs: vec![
1500 PortDef::new(10, "out", SignalKind::Audio),
1501 PortDef::new(11, "left", SignalKind::Audio),
1502 PortDef::new(12, "right", SignalKind::Audio),
1503 ],
1504 },
1505 }
1506 }
1507}
1508
1509impl Default for Crossfader {
1510 fn default() -> Self {
1511 Self::new()
1512 }
1513}
1514
1515impl GraphModule for Crossfader {
1516 fn port_spec(&self) -> &PortSpec {
1517 &self.spec
1518 }
1519
1520 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1521 let a = inputs.get_or(0, 0.0);
1522 let b = inputs.get_or(1, 0.0);
1523 let pos = inputs.get_or(2, 0.0);
1524
1525 let mix = ((pos / 5.0) + 1.0) / 2.0;
1527 let mix = mix.clamp(0.0, 1.0);
1528
1529 let a_gain = Libm::<f64>::sqrt(1.0 - mix);
1531 let b_gain = Libm::<f64>::sqrt(mix);
1532
1533 let out = a * a_gain + b * b_gain;
1535 outputs.set(10, out);
1536
1537 outputs.set(11, out * a_gain); outputs.set(12, out * b_gain); }
1542
1543 fn reset(&mut self) {}
1544
1545 fn set_sample_rate(&mut self, _: f64) {}
1546
1547 fn type_id(&self) -> &'static str {
1548 "crossfader"
1549 }
1550}
1551
1552pub struct LogicAnd {
1556 spec: PortSpec,
1557}
1558
1559impl LogicAnd {
1560 pub fn new() -> Self {
1561 Self {
1562 spec: PortSpec {
1563 inputs: vec![
1564 PortDef::new(0, "a", SignalKind::Gate),
1565 PortDef::new(1, "b", SignalKind::Gate),
1566 ],
1567 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1568 },
1569 }
1570 }
1571}
1572
1573impl Default for LogicAnd {
1574 fn default() -> Self {
1575 Self::new()
1576 }
1577}
1578
1579impl GraphModule for LogicAnd {
1580 fn port_spec(&self) -> &PortSpec {
1581 &self.spec
1582 }
1583
1584 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1585 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1586 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1587
1588 outputs.set(10, if a && b { GATE_HIGH_V } else { 0.0 });
1589 }
1590
1591 fn reset(&mut self) {}
1592
1593 fn set_sample_rate(&mut self, _: f64) {}
1594
1595 fn type_id(&self) -> &'static str {
1596 "logic_and"
1597 }
1598}
1599
1600pub struct LogicOr {
1604 spec: PortSpec,
1605}
1606
1607impl LogicOr {
1608 pub fn new() -> Self {
1609 Self {
1610 spec: PortSpec {
1611 inputs: vec![
1612 PortDef::new(0, "a", SignalKind::Gate),
1613 PortDef::new(1, "b", SignalKind::Gate),
1614 ],
1615 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1616 },
1617 }
1618 }
1619}
1620
1621impl Default for LogicOr {
1622 fn default() -> Self {
1623 Self::new()
1624 }
1625}
1626
1627impl GraphModule for LogicOr {
1628 fn port_spec(&self) -> &PortSpec {
1629 &self.spec
1630 }
1631
1632 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1633 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1634 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1635
1636 outputs.set(10, if a || b { GATE_HIGH_V } else { 0.0 });
1637 }
1638
1639 fn reset(&mut self) {}
1640
1641 fn set_sample_rate(&mut self, _: f64) {}
1642
1643 fn type_id(&self) -> &'static str {
1644 "logic_or"
1645 }
1646}
1647
1648pub struct LogicXor {
1652 spec: PortSpec,
1653}
1654
1655impl LogicXor {
1656 pub fn new() -> Self {
1657 Self {
1658 spec: PortSpec {
1659 inputs: vec![
1660 PortDef::new(0, "a", SignalKind::Gate),
1661 PortDef::new(1, "b", SignalKind::Gate),
1662 ],
1663 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1664 },
1665 }
1666 }
1667}
1668
1669impl Default for LogicXor {
1670 fn default() -> Self {
1671 Self::new()
1672 }
1673}
1674
1675impl GraphModule for LogicXor {
1676 fn port_spec(&self) -> &PortSpec {
1677 &self.spec
1678 }
1679
1680 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1681 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1682 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1683
1684 outputs.set(10, if a ^ b { GATE_HIGH_V } else { 0.0 });
1685 }
1686
1687 fn reset(&mut self) {}
1688
1689 fn set_sample_rate(&mut self, _: f64) {}
1690
1691 fn type_id(&self) -> &'static str {
1692 "logic_xor"
1693 }
1694}
1695
1696pub struct LogicNot {
1700 spec: PortSpec,
1701}
1702
1703impl LogicNot {
1704 pub fn new() -> Self {
1705 Self {
1706 spec: PortSpec {
1707 inputs: vec![PortDef::new(0, "in", SignalKind::Gate)],
1708 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1709 },
1710 }
1711 }
1712}
1713
1714impl Default for LogicNot {
1715 fn default() -> Self {
1716 Self::new()
1717 }
1718}
1719
1720impl GraphModule for LogicNot {
1721 fn port_spec(&self) -> &PortSpec {
1722 &self.spec
1723 }
1724
1725 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1726 let input = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1727 outputs.set(10, if input { 0.0 } else { GATE_HIGH_V });
1728 }
1729
1730 fn reset(&mut self) {}
1731
1732 fn set_sample_rate(&mut self, _: f64) {}
1733
1734 fn type_id(&self) -> &'static str {
1735 "logic_not"
1736 }
1737}
1738
1739pub struct Comparator {
1745 state: i8,
1749 spec: PortSpec,
1750}
1751
1752impl Comparator {
1753 const DEADBAND_V: f64 = 0.01;
1755 const HYSTERESIS_V: f64 = 0.02;
1758
1759 pub fn new() -> Self {
1760 Self {
1761 state: 0,
1762 spec: PortSpec {
1763 inputs: vec![
1764 PortDef::new(0, "a", SignalKind::CvBipolar),
1765 PortDef::new(1, "b", SignalKind::CvBipolar),
1766 ],
1767 outputs: vec![
1768 PortDef::new(10, "gt", SignalKind::Gate), PortDef::new(11, "lt", SignalKind::Gate), PortDef::new(12, "eq", SignalKind::Gate), ],
1772 },
1773 }
1774 }
1775}
1776
1777impl Default for Comparator {
1778 fn default() -> Self {
1779 Self::new()
1780 }
1781}
1782
1783impl GraphModule for Comparator {
1784 fn port_spec(&self) -> &PortSpec {
1785 &self.spec
1786 }
1787
1788 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1789 let a = inputs.get_or(0, 0.0);
1790 let b = inputs.get_or(1, 0.0);
1791 let d = a - b;
1792
1793 let t = Self::DEADBAND_V;
1794 let hy = Self::HYSTERESIS_V;
1795
1796 let mut gt = self.state == 1;
1801 let mut lt = self.state == -1;
1802
1803 if gt {
1804 if d < t {
1805 gt = false;
1806 }
1807 } else if d >= t + hy {
1808 gt = true;
1809 }
1810
1811 if lt {
1812 if d > -t {
1813 lt = false;
1814 }
1815 } else if d <= -t - hy {
1816 lt = true;
1817 }
1818
1819 self.state = if gt {
1822 1
1823 } else if lt {
1824 -1
1825 } else {
1826 0
1827 };
1828
1829 outputs.set(10, if gt { GATE_HIGH_V } else { 0.0 });
1830 outputs.set(11, if lt { GATE_HIGH_V } else { 0.0 });
1831 outputs.set(12, if self.state == 0 { GATE_HIGH_V } else { 0.0 });
1832 }
1833
1834 fn reset(&mut self) {
1835 self.state = 0;
1836 }
1837
1838 fn set_sample_rate(&mut self, _: f64) {}
1839
1840 fn type_id(&self) -> &'static str {
1841 "comparator"
1842 }
1843}
1844
1845pub struct Rectifier {
1850 spec: PortSpec,
1851}
1852
1853impl Rectifier {
1854 pub fn new() -> Self {
1855 Self {
1856 spec: PortSpec {
1857 inputs: vec![PortDef::new(0, "in", SignalKind::Audio)],
1858 outputs: vec![
1859 PortDef::new(10, "full", SignalKind::Audio), PortDef::new(11, "half_pos", SignalKind::Audio), PortDef::new(12, "half_neg", SignalKind::Audio), PortDef::new(13, "abs", SignalKind::CvUnipolar), ],
1864 },
1865 }
1866 }
1867}
1868
1869impl Default for Rectifier {
1870 fn default() -> Self {
1871 Self::new()
1872 }
1873}
1874
1875impl GraphModule for Rectifier {
1876 fn port_spec(&self) -> &PortSpec {
1877 &self.spec
1878 }
1879
1880 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1881 let input = inputs.get_or(0, 0.0);
1882
1883 outputs.set(10, Libm::<f64>::fabs(input));
1885
1886 outputs.set(11, Libm::<f64>::fmax(input, 0.0));
1888
1889 outputs.set(12, Libm::<f64>::fmax(-input, 0.0));
1891
1892 outputs.set(13, Libm::<f64>::fabs(input) * 2.0);
1894 }
1895
1896 fn reset(&mut self) {}
1897
1898 fn set_sample_rate(&mut self, _: f64) {}
1899
1900 fn type_id(&self) -> &'static str {
1901 "rectifier"
1902 }
1903}
1904
1905pub struct PrecisionAdder {
1911 spec: PortSpec,
1912}
1913
1914impl PrecisionAdder {
1915 pub fn new() -> Self {
1916 Self {
1917 spec: PortSpec {
1918 inputs: vec![
1919 PortDef::new(0, "in1", SignalKind::VoltPerOctave),
1920 PortDef::new(1, "in2", SignalKind::VoltPerOctave),
1921 PortDef::new(2, "in3", SignalKind::CvBipolar),
1922 PortDef::new(3, "in4", SignalKind::CvBipolar),
1923 ],
1924 outputs: vec![
1925 PortDef::new(10, "sum", SignalKind::VoltPerOctave),
1926 PortDef::new(11, "inv", SignalKind::VoltPerOctave), ],
1928 },
1929 }
1930 }
1931}
1932
1933impl Default for PrecisionAdder {
1934 fn default() -> Self {
1935 Self::new()
1936 }
1937}
1938
1939impl GraphModule for PrecisionAdder {
1940 fn port_spec(&self) -> &PortSpec {
1941 &self.spec
1942 }
1943
1944 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1945 let sum = inputs.get_or(0, 0.0)
1946 + inputs.get_or(1, 0.0)
1947 + inputs.get_or(2, 0.0)
1948 + inputs.get_or(3, 0.0);
1949
1950 outputs.set(10, sum);
1951 outputs.set(11, -sum);
1952 }
1953
1954 fn reset(&mut self) {}
1955
1956 fn set_sample_rate(&mut self, _: f64) {}
1957
1958 fn type_id(&self) -> &'static str {
1959 "precision_adder"
1960 }
1961}
1962
1963pub struct VcSwitch {
1969 spec: PortSpec,
1970}
1971
1972impl VcSwitch {
1973 pub fn new() -> Self {
1974 Self {
1975 spec: PortSpec {
1976 inputs: vec![
1977 PortDef::new(0, "a", SignalKind::Audio),
1978 PortDef::new(1, "b", SignalKind::Audio),
1979 PortDef::new(2, "cv", SignalKind::Gate).with_default(0.0),
1980 ],
1981 outputs: vec![
1982 PortDef::new(10, "out", SignalKind::Audio), PortDef::new(11, "a_out", SignalKind::Audio), PortDef::new(12, "b_out", SignalKind::Audio), ],
1986 },
1987 }
1988 }
1989}
1990
1991impl Default for VcSwitch {
1992 fn default() -> Self {
1993 Self::new()
1994 }
1995}
1996
1997impl GraphModule for VcSwitch {
1998 fn port_spec(&self) -> &PortSpec {
1999 &self.spec
2000 }
2001
2002 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2003 let a = inputs.get_or(0, 0.0);
2004 let b = inputs.get_or(1, 0.0);
2005 let cv = inputs.get_or(2, 0.0);
2006
2007 let select_b = cv > GATE_THRESHOLD_V;
2008
2009 if select_b {
2010 outputs.set(10, b);
2011 outputs.set(11, 0.0);
2012 outputs.set(12, b);
2013 } else {
2014 outputs.set(10, a);
2015 outputs.set(11, a);
2016 outputs.set(12, 0.0);
2017 }
2018 }
2019
2020 fn reset(&mut self) {}
2021
2022 fn set_sample_rate(&mut self, _: f64) {}
2023
2024 fn type_id(&self) -> &'static str {
2025 "vc_switch"
2026 }
2027}
2028
2029pub struct BernoulliGate {
2035 prev_trigger: f64,
2036 gate_a: f64,
2039 gate_b: f64,
2041 spec: PortSpec,
2042}
2043
2044impl BernoulliGate {
2045 pub fn new() -> Self {
2046 Self {
2047 prev_trigger: 0.0,
2048 gate_a: 0.0,
2049 gate_b: 0.0,
2050 spec: PortSpec {
2051 inputs: vec![
2052 PortDef::new(0, "trig", SignalKind::Trigger),
2053 PortDef::new(1, "prob", SignalKind::CvUnipolar).with_default(5.0), ],
2055 outputs: vec![
2056 PortDef::new(10, "a", SignalKind::Trigger), PortDef::new(11, "b", SignalKind::Trigger), PortDef::new(12, "gate_a", SignalKind::Gate), PortDef::new(13, "gate_b", SignalKind::Gate), ],
2061 },
2062 }
2063 }
2064}
2065
2066impl Default for BernoulliGate {
2067 fn default() -> Self {
2068 Self::new()
2069 }
2070}
2071
2072impl GraphModule for BernoulliGate {
2073 fn port_spec(&self) -> &PortSpec {
2074 &self.spec
2075 }
2076
2077 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2078 let trigger = inputs.get_or(0, 0.0);
2079 let prob = (inputs.get_or(1, 5.0) / 10.0).clamp(0.0, 1.0); let rising_edge = trigger > GATE_THRESHOLD_V && self.prev_trigger <= GATE_THRESHOLD_V;
2082 self.prev_trigger = trigger;
2083
2084 let mut trig_a = 0.0;
2086 let mut trig_b = 0.0;
2087
2088 if rising_edge {
2089 let rand_val: f64 = rng::random();
2091 if rand_val < prob {
2092 trig_a = GATE_HIGH_V;
2093 } else {
2094 trig_b = GATE_HIGH_V;
2095 }
2096 }
2097
2098 outputs.set(10, trig_a);
2100 outputs.set(11, trig_b);
2101
2102 if trig_a > 0.0 {
2106 self.gate_a = GATE_HIGH_V;
2107 self.gate_b = 0.0;
2108 } else if trig_b > 0.0 {
2109 self.gate_a = 0.0;
2110 self.gate_b = GATE_HIGH_V;
2111 }
2112
2113 outputs.set(12, self.gate_a);
2114 outputs.set(13, self.gate_b);
2115 }
2116
2117 fn reset(&mut self) {
2118 self.prev_trigger = 0.0;
2119 self.gate_a = 0.0;
2120 self.gate_b = 0.0;
2121 }
2122
2123 fn set_sample_rate(&mut self, _: f64) {}
2124
2125 fn type_id(&self) -> &'static str {
2126 "bernoulli_gate"
2127 }
2128}
2129
2130pub struct Min {
2134 spec: PortSpec,
2135}
2136
2137impl Min {
2138 pub fn new() -> Self {
2139 Self {
2140 spec: PortSpec {
2141 inputs: vec![
2142 PortDef::new(0, "a", SignalKind::CvBipolar),
2143 PortDef::new(1, "b", SignalKind::CvBipolar),
2144 ],
2145 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
2146 },
2147 }
2148 }
2149}
2150
2151impl Default for Min {
2152 fn default() -> Self {
2153 Self::new()
2154 }
2155}
2156
2157impl GraphModule for Min {
2158 fn port_spec(&self) -> &PortSpec {
2159 &self.spec
2160 }
2161
2162 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2163 let a = inputs.get_or(0, 0.0);
2164 let b = inputs.get_or(1, 0.0);
2165 outputs.set(10, Libm::<f64>::fmin(a, b));
2166 }
2167
2168 fn reset(&mut self) {}
2169
2170 fn set_sample_rate(&mut self, _: f64) {}
2171
2172 fn type_id(&self) -> &'static str {
2173 "min"
2174 }
2175}
2176
2177pub struct Max {
2181 spec: PortSpec,
2182}
2183
2184impl Max {
2185 pub fn new() -> Self {
2186 Self {
2187 spec: PortSpec {
2188 inputs: vec![
2189 PortDef::new(0, "a", SignalKind::CvBipolar),
2190 PortDef::new(1, "b", SignalKind::CvBipolar),
2191 ],
2192 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
2193 },
2194 }
2195 }
2196}
2197
2198impl Default for Max {
2199 fn default() -> Self {
2200 Self::new()
2201 }
2202}
2203
2204impl GraphModule for Max {
2205 fn port_spec(&self) -> &PortSpec {
2206 &self.spec
2207 }
2208
2209 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2210 let a = inputs.get_or(0, 0.0);
2211 let b = inputs.get_or(1, 0.0);
2212 outputs.set(10, Libm::<f64>::fmax(a, b));
2213 }
2214
2215 fn reset(&mut self) {}
2216
2217 fn set_sample_rate(&mut self, _: f64) {}
2218
2219 fn type_id(&self) -> &'static str {
2220 "max"
2221 }
2222}
2223
2224#[derive(Debug, Clone, Copy, PartialEq)]
2230pub enum ChordType {
2231 Major,
2232 Minor,
2233 Seventh,
2234 MajorSeventh,
2235 MinorSeventh,
2236 Diminished,
2237 Augmented,
2238 Sus2,
2239 Sus4,
2240}
2241
2242impl ChordType {
2243 fn intervals(&self) -> &'static [i32] {
2245 match self {
2246 ChordType::Major => &[0, 4, 7],
2247 ChordType::Minor => &[0, 3, 7],
2248 ChordType::Seventh => &[0, 4, 7, 10],
2249 ChordType::MajorSeventh => &[0, 4, 7, 11],
2250 ChordType::MinorSeventh => &[0, 3, 7, 10],
2251 ChordType::Diminished => &[0, 3, 6],
2252 ChordType::Augmented => &[0, 4, 8],
2253 ChordType::Sus2 => &[0, 2, 7],
2254 ChordType::Sus4 => &[0, 5, 7],
2255 }
2256 }
2257
2258 fn from_cv(cv: f64) -> Self {
2260 match (cv * 8.99) as u8 {
2261 0 => ChordType::Major,
2262 1 => ChordType::Minor,
2263 2 => ChordType::Seventh,
2264 3 => ChordType::MajorSeventh,
2265 4 => ChordType::MinorSeventh,
2266 5 => ChordType::Diminished,
2267 6 => ChordType::Augmented,
2268 7 => ChordType::Sus2,
2269 _ => ChordType::Sus4,
2270 }
2271 }
2272}
2273
2274pub struct ChordMemory {
2286 spec: PortSpec,
2287}
2288
2289impl ChordMemory {
2290 pub fn new() -> Self {
2291 Self {
2292 spec: PortSpec {
2293 inputs: vec![
2294 PortDef::new(0, "root", SignalKind::VoltPerOctave),
2295 PortDef::new(1, "chord", SignalKind::CvUnipolar)
2296 .with_default(0.0)
2297 .with_attenuverter(),
2298 PortDef::new(2, "inversion", SignalKind::CvUnipolar)
2299 .with_default(0.0)
2300 .with_attenuverter(),
2301 PortDef::new(3, "spread", SignalKind::CvUnipolar)
2302 .with_default(0.0)
2303 .with_attenuverter(),
2304 ],
2305 outputs: vec![
2306 PortDef::new(10, "voice1", SignalKind::VoltPerOctave),
2307 PortDef::new(11, "voice2", SignalKind::VoltPerOctave),
2308 PortDef::new(12, "voice3", SignalKind::VoltPerOctave),
2309 PortDef::new(13, "voice4", SignalKind::VoltPerOctave),
2310 ],
2311 },
2312 }
2313 }
2314}
2315
2316impl Default for ChordMemory {
2317 fn default() -> Self {
2318 Self::new()
2319 }
2320}
2321
2322impl GraphModule for ChordMemory {
2323 fn port_spec(&self) -> &PortSpec {
2324 &self.spec
2325 }
2326
2327 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2328 let root = inputs.get_or(0, 0.0);
2329 let chord_cv = inputs.get_or(1, 0.0).clamp(0.0, 1.0);
2330 let inversion_cv = inputs.get_or(2, 0.0).clamp(0.0, 1.0);
2331 let spread = inputs.get_or(3, 0.0).clamp(0.0, 1.0);
2332
2333 let chord_type = ChordType::from_cv(chord_cv);
2334 let intervals = chord_type.intervals();
2335 let num_notes = intervals.len();
2336
2337 let inversion = ((inversion_cv * num_notes as f64) as usize) % num_notes;
2339
2340 let mut voices = [0.0f64; 4];
2342 for (i, voice) in voices.iter_mut().enumerate() {
2343 if i < num_notes {
2344 let interval_idx = (i + inversion) % num_notes;
2345 let semitones = intervals[interval_idx];
2346
2347 let octave_offset = if i + inversion >= num_notes { 1.0 } else { 0.0 };
2349
2350 let spread_offset = spread * (i as f64 / 3.0);
2352
2353 *voice = root + semitones as f64 / 12.0 + octave_offset + spread_offset;
2355 } else {
2356 let spread_offset = spread * (i as f64 / 3.0);
2359 *voice = root + 1.0 + spread_offset;
2360 }
2361 }
2362
2363 outputs.set(10, voices[0]);
2364 outputs.set(11, voices[1]);
2365 outputs.set(12, voices[2]);
2366 outputs.set(13, voices[3]);
2367 }
2368
2369 fn reset(&mut self) {}
2370
2371 fn set_sample_rate(&mut self, _: f64) {}
2372
2373 fn type_id(&self) -> &'static str {
2374 "chord_memory"
2375 }
2376}
2377
2378#[derive(Debug, Clone, Copy, PartialEq)]
2384pub enum ArpPattern {
2385 Up,
2387 Down,
2389 UpDown,
2391 Random,
2393}
2394
2395impl ArpPattern {
2396 fn from_cv(cv: f64) -> Self {
2398 let cv = cv.clamp(0.0, 1.0);
2399 if cv < 0.25 {
2400 ArpPattern::Up
2401 } else if cv < 0.5 {
2402 ArpPattern::Down
2403 } else if cv < 0.75 {
2404 ArpPattern::UpDown
2405 } else {
2406 ArpPattern::Random
2407 }
2408 }
2409}
2410
2411pub struct Arpeggiator {
2427 held_notes: [f64; 8],
2429 num_notes: usize,
2431 current_step: usize,
2433 direction_up: bool,
2435 prev_gate: f64,
2437 captured_note: Option<f64>,
2440 prev_clock: f64,
2442 prev_reset: f64,
2444 rng: crate::rng::Rng,
2446 gate_out: f64,
2448 trigger_countdown: usize,
2450 sample_rate: f64,
2451 spec: PortSpec,
2452}
2453
2454impl Arpeggiator {
2455 const TRIGGER_MS: f64 = 1.0;
2457
2458 pub fn new(sample_rate: f64) -> Self {
2459 let spec = PortSpec {
2460 inputs: vec![
2461 PortDef::new(0, "v_oct", SignalKind::VoltPerOctave).with_default(0.0),
2462 PortDef::new(1, "gate", SignalKind::Gate).with_default(0.0),
2463 PortDef::new(2, "clock", SignalKind::Clock).with_default(0.0),
2464 PortDef::new(3, "pattern", SignalKind::CvUnipolar).with_default(0.0),
2465 PortDef::new(4, "octaves", SignalKind::CvUnipolar).with_default(0.0),
2466 PortDef::new(5, "reset", SignalKind::Gate).with_default(0.0),
2467 ],
2468 outputs: vec![
2469 PortDef::new(10, "v_oct_out", SignalKind::VoltPerOctave),
2470 PortDef::new(11, "gate_out", SignalKind::Gate),
2471 PortDef::new(12, "trigger", SignalKind::Trigger),
2472 ],
2473 };
2474
2475 Self {
2476 held_notes: [0.0; 8],
2477 num_notes: 0,
2478 current_step: 0,
2479 direction_up: true,
2480 prev_gate: 0.0,
2481 captured_note: None,
2482 prev_clock: 0.0,
2483 prev_reset: 0.0,
2484 rng: crate::rng::Rng::from_seed(42),
2485 gate_out: 0.0,
2486 trigger_countdown: 0,
2487 sample_rate,
2488 spec,
2489 }
2490 }
2491
2492 fn add_note(&mut self, note: f64) {
2494 if self.num_notes >= 8 {
2495 return;
2496 }
2497
2498 let mut insert_pos = self.num_notes;
2500 for i in 0..self.num_notes {
2501 if note < self.held_notes[i] {
2502 insert_pos = i;
2503 break;
2504 }
2505 }
2506
2507 for i in (insert_pos..self.num_notes).rev() {
2509 self.held_notes[i + 1] = self.held_notes[i];
2510 }
2511
2512 self.held_notes[insert_pos] = note;
2513 self.num_notes += 1;
2514 }
2515
2516 pub fn remove_note(&mut self, note: f64) {
2518 let mut found_idx = None;
2520 for i in 0..self.num_notes {
2521 if (self.held_notes[i] - note).abs() < 0.001 {
2522 found_idx = Some(i);
2523 break;
2524 }
2525 }
2526
2527 if let Some(idx) = found_idx {
2528 for i in idx..self.num_notes - 1 {
2530 self.held_notes[i] = self.held_notes[i + 1];
2531 }
2532 self.num_notes -= 1;
2533 }
2534 }
2535
2536 fn get_current_note(&mut self, pattern: ArpPattern, octaves: usize) -> f64 {
2538 if self.num_notes == 0 {
2539 return 0.0;
2540 }
2541
2542 let total_steps = self.num_notes * octaves;
2543 let step = self.current_step % total_steps;
2544
2545 let note_idx = match pattern {
2546 ArpPattern::Up => step % self.num_notes,
2547 ArpPattern::Down => (self.num_notes - 1) - (step % self.num_notes),
2548 ArpPattern::UpDown => {
2549 let cycle_len = if self.num_notes > 1 {
2551 (self.num_notes - 1) * 2
2552 } else {
2553 1
2554 };
2555 let pos = step % cycle_len;
2556 if pos < self.num_notes {
2557 pos
2558 } else {
2559 (self.num_notes - 1) * 2 - pos
2560 }
2561 }
2562 ArpPattern::Random => (self.rng.next_u64() as usize) % self.num_notes,
2563 };
2564
2565 let octave = step / self.num_notes;
2566 let base_note = self.held_notes[note_idx % self.num_notes];
2567
2568 base_note + octave as f64 }
2570}
2571
2572impl Default for Arpeggiator {
2573 fn default() -> Self {
2574 Self::new(44100.0)
2575 }
2576}
2577
2578impl GraphModule for Arpeggiator {
2579 fn port_spec(&self) -> &PortSpec {
2580 &self.spec
2581 }
2582
2583 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2584 let v_oct = inputs.get_or(0, 0.0);
2585 let gate = inputs.get_or(1, 0.0);
2586 let clock = inputs.get_or(2, 0.0);
2587 let pattern_cv = inputs.get_or(3, 0.0);
2588 let octaves_cv = inputs.get_or(4, 0.0);
2589 let reset = inputs.get_or(5, 0.0);
2590
2591 let pattern = ArpPattern::from_cv(pattern_cv);
2592 let octaves = (1.0 + octaves_cv.clamp(0.0, 1.0) * 3.0) as usize; if gate > GATE_THRESHOLD_V && self.prev_gate <= GATE_THRESHOLD_V {
2599 self.add_note(v_oct);
2601 self.captured_note = Some(v_oct);
2602 } else if gate <= GATE_THRESHOLD_V && self.prev_gate > GATE_THRESHOLD_V {
2603 if let Some(note) = self.captured_note.take() {
2605 self.remove_note(note);
2606 }
2607 }
2608 self.prev_gate = gate;
2609
2610 if reset > GATE_THRESHOLD_V && self.prev_reset <= GATE_THRESHOLD_V {
2612 self.current_step = 0;
2613 self.direction_up = true;
2614 self.held_notes = [0.0; 8];
2615 self.num_notes = 0;
2616 self.captured_note = None;
2617 }
2618 self.prev_reset = reset;
2619
2620 let mut trigger_out = 0.0;
2622 let clock_rising =
2623 clock > GATE_THRESHOLD_V && self.prev_clock <= GATE_THRESHOLD_V && self.num_notes > 0;
2624
2625 if clock_rising {
2626 self.gate_out = GATE_HIGH_V;
2627 self.trigger_countdown = (Self::TRIGGER_MS * self.sample_rate / 1000.0) as usize;
2629 trigger_out = GATE_HIGH_V;
2630 }
2631 self.prev_clock = clock;
2632
2633 if self.trigger_countdown > 0 {
2635 self.trigger_countdown -= 1;
2636 trigger_out = GATE_HIGH_V;
2637 }
2638
2639 if clock <= GATE_THRESHOLD_V {
2641 self.gate_out = 0.0;
2642 }
2643
2644 let v_oct_out = if self.num_notes > 0 {
2646 self.get_current_note(pattern, octaves)
2647 } else {
2648 0.0
2649 };
2650
2651 if clock_rising {
2653 self.current_step += 1;
2654 }
2655
2656 outputs.set(10, v_oct_out);
2657 outputs.set(
2658 11,
2659 if self.num_notes > 0 {
2660 self.gate_out
2661 } else {
2662 0.0
2663 },
2664 );
2665 outputs.set(12, trigger_out);
2666 }
2667
2668 fn reset(&mut self) {
2669 self.held_notes = [0.0; 8];
2670 self.num_notes = 0;
2671 self.captured_note = None;
2672 self.current_step = 0;
2673 self.direction_up = true;
2674 self.prev_gate = 0.0;
2675 self.prev_clock = 0.0;
2676 self.prev_reset = 0.0;
2677 self.gate_out = 0.0;
2678 self.trigger_countdown = 0;
2679 }
2680
2681 fn set_sample_rate(&mut self, sample_rate: f64) {
2682 self.sample_rate = sample_rate;
2683 }
2684
2685 fn type_id(&self) -> &'static str {
2686 "arpeggiator"
2687 }
2688}
2689
2690#[cfg(test)]
2695mod tests {
2696 use super::*;
2697 use crate::modules::common::SAFE_AUDIO_LIMIT;
2698
2699 #[test]
2700 fn test_mixer() {
2701 let mut mixer = Mixer::new(4);
2702 let mut inputs = PortValues::new();
2703 let mut outputs = PortValues::new();
2704
2705 inputs.set(0, 1.0);
2706 inputs.set(1, 2.0);
2707 inputs.set(2, 3.0);
2708 inputs.set(3, 4.0);
2709
2710 mixer.tick(&inputs, &mut outputs);
2711
2712 let out = outputs.get(100).unwrap();
2713 assert!((out - 10.0).abs() < 0.01);
2714 }
2715 #[test]
2716 fn test_step_sequencer() {
2717 let mut seq = StepSequencer::new();
2718 seq.set_step(0, 0.0, true);
2719 seq.set_step(1, 0.5, true);
2720 seq.set_step(2, 1.0, true);
2721
2722 let mut inputs = PortValues::new();
2723 let mut outputs = PortValues::new();
2724
2725 seq.tick(&inputs, &mut outputs);
2727 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2728
2729 inputs.set(0, 5.0);
2731 seq.tick(&inputs, &mut outputs);
2732 assert!((outputs.get(10).unwrap() - 0.5).abs() < 0.01);
2733
2734 inputs.set(0, 0.0);
2736 seq.tick(&inputs, &mut outputs);
2737 inputs.set(0, 5.0);
2738 seq.tick(&inputs, &mut outputs);
2739 assert!((outputs.get(10).unwrap() - 1.0).abs() < 0.01);
2740 }
2741 #[test]
2742 fn test_sample_and_hold() {
2743 let mut sh = SampleAndHold::new();
2744 let mut inputs = PortValues::new();
2745 let mut outputs = PortValues::new();
2746
2747 inputs.set(0, 3.0);
2749 inputs.set(1, 0.0);
2750 sh.tick(&inputs, &mut outputs);
2751 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2753
2754 inputs.set(1, 5.0);
2756 sh.tick(&inputs, &mut outputs);
2757 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
2758
2759 inputs.set(0, 7.0);
2761 sh.tick(&inputs, &mut outputs);
2762 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
2763
2764 inputs.set(1, 0.0);
2766 sh.tick(&inputs, &mut outputs);
2767 inputs.set(1, 5.0);
2768 sh.tick(&inputs, &mut outputs);
2769 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
2770 }
2771 #[test]
2772 fn test_slew_limiter() {
2773 let mut slew = SlewLimiter::new(1000.0); let mut inputs = PortValues::new();
2775 let mut outputs = PortValues::new();
2776
2777 inputs.set(1, 0.5); inputs.set(2, 0.5); inputs.set(0, 5.0);
2783 slew.tick(&inputs, &mut outputs);
2784 let first = outputs.get(10).unwrap();
2785
2786 assert!(first > 0.0);
2788 assert!(first < 5.0);
2789
2790 for _ in 0..100 {
2792 slew.tick(&inputs, &mut outputs);
2793 }
2794 let after_100 = outputs.get(10).unwrap();
2796 assert!(after_100 > first);
2797 }
2798 #[test]
2799 fn test_quantizer_chromatic() {
2800 let mut quant = Quantizer::new(Scale::Chromatic);
2801 let mut inputs = PortValues::new();
2802 let mut outputs = PortValues::new();
2803
2804 inputs.set(0, 0.0); quant.tick(&inputs, &mut outputs);
2807 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2808
2809 inputs.set(0, 0.04); quant.tick(&inputs, &mut outputs);
2812 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2814
2815 inputs.set(0, 0.07);
2817 quant.tick(&inputs, &mut outputs);
2818 let expected_csharp = 1.0 / 12.0;
2820 assert!((outputs.get(10).unwrap() - expected_csharp).abs() < 0.01);
2821 }
2822 #[test]
2823 fn test_quantizer_major_scale() {
2824 let mut quant = Quantizer::new(Scale::Major);
2825 let mut inputs = PortValues::new();
2826 let mut outputs = PortValues::new();
2827
2828 inputs.set(0, 1.0 / 12.0); quant.tick(&inputs, &mut outputs);
2831 let out = outputs.get(10).unwrap();
2832 assert!(out.abs() < 0.01 || (out - 2.0 / 12.0).abs() < 0.01);
2834 }
2835 #[test]
2836 fn test_clock() {
2837 let mut clock = Clock::new(1000.0); let mut inputs = PortValues::new();
2839 let mut outputs = PortValues::new();
2840
2841 inputs.set(0, 10.0); let mut trigger_count = 0;
2845 let mut last_trigger = 0.0;
2846
2847 for _ in 0..1000 {
2848 clock.tick(&inputs, &mut outputs);
2849 let trigger = outputs.get(10).unwrap(); if trigger > 2.5 && last_trigger <= 2.5 {
2851 trigger_count += 1;
2852 }
2853 last_trigger = trigger;
2854 }
2855
2856 assert!(trigger_count >= 3);
2859 }
2860 #[test]
2861 fn test_attenuverter() {
2862 let mut att = Attenuverter::new();
2863 let mut inputs = PortValues::new();
2864 let mut outputs = PortValues::new();
2865
2866 inputs.set(0, 5.0); inputs.set(1, 5.0); att.tick(&inputs, &mut outputs);
2870 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.1);
2871
2872 inputs.set(1, 2.5);
2874 att.tick(&inputs, &mut outputs);
2875 assert!((outputs.get(10).unwrap() - 2.5).abs() < 0.1);
2876
2877 inputs.set(1, 0.0);
2879 att.tick(&inputs, &mut outputs);
2880 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.1);
2881 }
2882 #[test]
2883 fn test_multiple() {
2884 let mut mult = Multiple::new();
2885 let mut inputs = PortValues::new();
2886 let mut outputs = PortValues::new();
2887
2888 inputs.set(0, 3.5);
2889 mult.tick(&inputs, &mut outputs);
2890
2891 assert!((outputs.get(10).unwrap() - 3.5).abs() < 0.0001);
2893 assert!((outputs.get(11).unwrap() - 3.5).abs() < 0.0001);
2894 assert!((outputs.get(12).unwrap() - 3.5).abs() < 0.0001);
2895 assert!((outputs.get(13).unwrap() - 3.5).abs() < 0.0001);
2896 }
2897 #[test]
2898 fn test_crossfader() {
2899 let mut xf = Crossfader::new();
2900 let mut inputs = PortValues::new();
2901 let mut outputs = PortValues::new();
2902
2903 inputs.set(0, 5.0); inputs.set(1, -5.0); inputs.set(2, -5.0);
2908 xf.tick(&inputs, &mut outputs);
2909 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.1);
2910
2911 inputs.set(2, 5.0);
2913 xf.tick(&inputs, &mut outputs);
2914 assert!((outputs.get(10).unwrap() - (-5.0)).abs() < 0.1);
2915
2916 inputs.set(2, 0.0);
2918 xf.tick(&inputs, &mut outputs);
2919 let out = outputs.get(10).unwrap();
2921 assert!(out.abs() < 1.0); }
2923 #[test]
2924 fn test_logic_and() {
2925 let mut gate = LogicAnd::new();
2926 let mut inputs = PortValues::new();
2927 let mut outputs = PortValues::new();
2928
2929 inputs.set(0, 0.0);
2931 inputs.set(1, 0.0);
2932 gate.tick(&inputs, &mut outputs);
2933 assert!(outputs.get(10).unwrap() < 2.5);
2934
2935 inputs.set(0, 5.0);
2937 inputs.set(1, 0.0);
2938 gate.tick(&inputs, &mut outputs);
2939 assert!(outputs.get(10).unwrap() < 2.5);
2940
2941 inputs.set(0, 5.0);
2943 inputs.set(1, 5.0);
2944 gate.tick(&inputs, &mut outputs);
2945 assert!(outputs.get(10).unwrap() > 2.5);
2946 }
2947 #[test]
2948 fn test_logic_or() {
2949 let mut gate = LogicOr::new();
2950 let mut inputs = PortValues::new();
2951 let mut outputs = PortValues::new();
2952
2953 inputs.set(0, 0.0);
2955 inputs.set(1, 0.0);
2956 gate.tick(&inputs, &mut outputs);
2957 assert!(outputs.get(10).unwrap() < 2.5);
2958
2959 inputs.set(0, 5.0);
2961 inputs.set(1, 0.0);
2962 gate.tick(&inputs, &mut outputs);
2963 assert!(outputs.get(10).unwrap() > 2.5);
2964
2965 inputs.set(0, 5.0);
2967 inputs.set(1, 5.0);
2968 gate.tick(&inputs, &mut outputs);
2969 assert!(outputs.get(10).unwrap() > 2.5);
2970 }
2971 #[test]
2972 fn test_logic_xor() {
2973 let mut gate = LogicXor::new();
2974 let mut inputs = PortValues::new();
2975 let mut outputs = PortValues::new();
2976
2977 inputs.set(0, 0.0);
2979 inputs.set(1, 0.0);
2980 gate.tick(&inputs, &mut outputs);
2981 assert!(outputs.get(10).unwrap() < 2.5);
2982
2983 inputs.set(0, 5.0);
2985 inputs.set(1, 0.0);
2986 gate.tick(&inputs, &mut outputs);
2987 assert!(outputs.get(10).unwrap() > 2.5);
2988
2989 inputs.set(0, 5.0);
2991 inputs.set(1, 5.0);
2992 gate.tick(&inputs, &mut outputs);
2993 assert!(outputs.get(10).unwrap() < 2.5);
2994 }
2995 #[test]
2996 fn test_logic_not() {
2997 let mut gate = LogicNot::new();
2998 let mut inputs = PortValues::new();
2999 let mut outputs = PortValues::new();
3000
3001 inputs.set(0, 0.0);
3003 gate.tick(&inputs, &mut outputs);
3004 assert!(outputs.get(10).unwrap() > 2.5);
3005
3006 inputs.set(0, 5.0);
3008 gate.tick(&inputs, &mut outputs);
3009 assert!(outputs.get(10).unwrap() < 2.5);
3010 }
3011 #[test]
3012 fn test_comparator() {
3013 let mut cmp = Comparator::new();
3014 let mut inputs = PortValues::new();
3015 let mut outputs = PortValues::new();
3016
3017 inputs.set(0, 3.0);
3019 inputs.set(1, 1.0);
3020 cmp.tick(&inputs, &mut outputs);
3021 assert!(outputs.get(10).unwrap() > 2.5); assert!(outputs.get(11).unwrap() < 2.5); assert!(outputs.get(12).unwrap() < 2.5); inputs.set(0, 1.0);
3027 inputs.set(1, 3.0);
3028 cmp.tick(&inputs, &mut outputs);
3029 assert!(outputs.get(10).unwrap() < 2.5); assert!(outputs.get(11).unwrap() > 2.5); assert!(outputs.get(12).unwrap() < 2.5); inputs.set(0, 2.0);
3035 inputs.set(1, 2.0);
3036 cmp.tick(&inputs, &mut outputs);
3037 assert!(outputs.get(10).unwrap() < 2.5); assert!(outputs.get(11).unwrap() < 2.5); assert!(outputs.get(12).unwrap() > 2.5); }
3041 #[test]
3042 fn test_rectifier() {
3043 let mut rect = Rectifier::new();
3044 let mut inputs = PortValues::new();
3045 let mut outputs = PortValues::new();
3046
3047 inputs.set(0, 3.0);
3049 rect.tick(&inputs, &mut outputs);
3050 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01); assert!((outputs.get(11).unwrap() - 3.0).abs() < 0.01); assert!((outputs.get(12).unwrap()).abs() < 0.01); inputs.set(0, -3.0);
3056 rect.tick(&inputs, &mut outputs);
3057 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01); assert!((outputs.get(11).unwrap()).abs() < 0.01); assert!((outputs.get(12).unwrap() - 3.0).abs() < 0.01); }
3061 #[test]
3062 fn test_precision_adder() {
3063 let mut adder = PrecisionAdder::new();
3064 let mut inputs = PortValues::new();
3065 let mut outputs = PortValues::new();
3066
3067 inputs.set(0, 1.0);
3068 inputs.set(1, 2.0);
3069 inputs.set(2, 0.5);
3070 inputs.set(3, -0.5);
3071 adder.tick(&inputs, &mut outputs);
3072
3073 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01); assert!((outputs.get(11).unwrap() - (-3.0)).abs() < 0.01); }
3076 #[test]
3077 fn test_vc_switch() {
3078 let mut sw = VcSwitch::new();
3079 let mut inputs = PortValues::new();
3080 let mut outputs = PortValues::new();
3081
3082 inputs.set(0, 3.0); inputs.set(1, 7.0); inputs.set(2, 0.0);
3087 sw.tick(&inputs, &mut outputs);
3088 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
3089 assert!((outputs.get(11).unwrap() - 3.0).abs() < 0.01);
3090 assert!((outputs.get(12).unwrap()).abs() < 0.01);
3091
3092 inputs.set(2, 5.0);
3094 sw.tick(&inputs, &mut outputs);
3095 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
3096 assert!((outputs.get(11).unwrap()).abs() < 0.01);
3097 assert!((outputs.get(12).unwrap() - 7.0).abs() < 0.01);
3098 }
3099 #[test]
3100 fn test_bernoulli_gate() {
3101 let mut bg = BernoulliGate::new();
3102 let mut inputs = PortValues::new();
3103 let mut outputs = PortValues::new();
3104
3105 inputs.set(1, 10.0);
3107
3108 inputs.set(0, 0.0);
3110 bg.tick(&inputs, &mut outputs);
3111 inputs.set(0, 5.0);
3112 bg.tick(&inputs, &mut outputs);
3113
3114 assert!(outputs.get(10).unwrap() > 2.5); assert!(outputs.get(11).unwrap() < 2.5); bg.reset();
3120 inputs.set(1, 0.0);
3121 inputs.set(0, 0.0);
3122 bg.tick(&inputs, &mut outputs);
3123 inputs.set(0, 5.0);
3124 bg.tick(&inputs, &mut outputs);
3125
3126 assert!(outputs.get(10).unwrap() < 2.5); assert!(outputs.get(11).unwrap() > 2.5); }
3130 #[test]
3131 fn test_min() {
3132 let mut m = Min::new();
3133 let mut inputs = PortValues::new();
3134 let mut outputs = PortValues::new();
3135
3136 inputs.set(0, 3.0);
3137 inputs.set(1, 5.0);
3138 m.tick(&inputs, &mut outputs);
3139 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
3140
3141 inputs.set(0, 7.0);
3142 inputs.set(1, 2.0);
3143 m.tick(&inputs, &mut outputs);
3144 assert!((outputs.get(10).unwrap() - 2.0).abs() < 0.01);
3145 }
3146 #[test]
3147 fn test_max() {
3148 let mut m = Max::new();
3149 let mut inputs = PortValues::new();
3150 let mut outputs = PortValues::new();
3151
3152 inputs.set(0, 3.0);
3153 inputs.set(1, 5.0);
3154 m.tick(&inputs, &mut outputs);
3155 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.01);
3156
3157 inputs.set(0, 7.0);
3158 inputs.set(1, 2.0);
3159 m.tick(&inputs, &mut outputs);
3160 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
3161 }
3162 #[test]
3163 fn test_mixer_default_reset_sample_rate() {
3164 let mut mixer = Mixer::default();
3165 mixer.reset();
3166 mixer.set_sample_rate(48000.0);
3167 assert_eq!(mixer.type_id(), "mixer");
3168 }
3169 #[test]
3170 fn test_stereo_output_default_reset_sample_rate() {
3171 let mut stereo = StereoOutput::default();
3172 stereo.reset();
3173 stereo.set_sample_rate(48000.0);
3174 assert_eq!(stereo.type_id(), "stereo_output");
3175 }
3176 #[test]
3177 fn test_offset_default_reset_sample_rate() {
3178 let mut offset = Offset::default();
3179 offset.reset();
3180 offset.set_sample_rate(48000.0);
3181 assert_eq!(offset.type_id(), "offset");
3182 }
3183 #[test]
3184 fn test_scale_enum_semitones() {
3185 let scale = Scale::Chromatic;
3186 assert!(scale.semitones().len() == 12);
3187
3188 let scale = Scale::Major;
3189 assert!(scale.semitones().len() == 7);
3190
3191 let scale = Scale::PentatonicMajor;
3192 assert!(scale.semitones().len() == 5);
3193 }
3194 #[test]
3195 fn test_step_sequencer_default_reset_sample_rate() {
3196 let mut seq = StepSequencer::default();
3197 seq.set_step(0, 1.0, true);
3198 let mut inputs = PortValues::new();
3199 let mut outputs = PortValues::new();
3200 inputs.set(0, 5.0);
3201 seq.tick(&inputs, &mut outputs);
3202
3203 seq.reset();
3204 assert!(seq.current == 0);
3205 assert!(seq.prev_clock == 0.0);
3206
3207 seq.set_sample_rate(48000.0);
3208 assert_eq!(seq.type_id(), "step_sequencer");
3209 }
3210 #[test]
3211 fn test_sample_and_hold_default_reset_sample_rate() {
3212 let mut sh = SampleAndHold::default();
3213 let mut inputs = PortValues::new();
3214 let mut outputs = PortValues::new();
3215 inputs.set(0, 5.0);
3216 inputs.set(1, 5.0);
3217 sh.tick(&inputs, &mut outputs);
3218
3219 sh.reset();
3220 assert!(sh.held_value == 0.0);
3221
3222 sh.set_sample_rate(48000.0);
3223 assert_eq!(sh.type_id(), "sample_hold");
3224 }
3225 #[test]
3226 fn test_slew_limiter_default_reset_sample_rate() {
3227 let mut slew = SlewLimiter::default();
3228 assert!(slew.sample_rate == 44100.0);
3229
3230 let mut inputs = PortValues::new();
3231 let mut outputs = PortValues::new();
3232 inputs.set(0, 5.0);
3233 slew.tick(&inputs, &mut outputs);
3234
3235 slew.reset();
3236 assert!(slew.current == 0.0);
3237
3238 slew.set_sample_rate(48000.0);
3239 assert!(slew.sample_rate == 48000.0);
3240
3241 assert_eq!(slew.type_id(), "slew_limiter");
3242 }
3243 #[test]
3244 fn test_quantizer_default_reset_sample_rate() {
3245 let mut quant = Quantizer::default();
3246 quant.reset();
3247 quant.set_sample_rate(48000.0);
3248 assert_eq!(quant.type_id(), "quantizer");
3249 }
3250 #[test]
3251 fn test_clock_default_reset_sample_rate() {
3252 let mut clock = Clock::default();
3253 assert!(clock.sample_rate == 44100.0);
3254
3255 let inputs = PortValues::new();
3256 let mut outputs = PortValues::new();
3257 for _ in 0..100 {
3258 clock.tick(&inputs, &mut outputs);
3259 }
3260
3261 clock.reset();
3262 assert!(clock.phase == 0.0);
3263
3264 clock.set_sample_rate(48000.0);
3265 assert!(clock.sample_rate == 48000.0);
3266
3267 assert_eq!(clock.type_id(), "clock");
3268 }
3269 #[test]
3270 fn test_attenuverter_default_reset_sample_rate() {
3271 let mut att = Attenuverter::default();
3272 att.reset();
3273 att.set_sample_rate(48000.0);
3274 assert_eq!(att.type_id(), "attenuverter");
3275 }
3276 #[test]
3277 fn test_multiple_default_reset_sample_rate() {
3278 let mut mult = Multiple::default();
3279 mult.reset();
3280 mult.set_sample_rate(48000.0);
3281 assert_eq!(mult.type_id(), "multiple");
3282 }
3283 #[test]
3284 fn test_crossfader_default_reset_sample_rate() {
3285 let mut xf = Crossfader::default();
3286 xf.reset();
3287 xf.set_sample_rate(48000.0);
3288 assert_eq!(xf.type_id(), "crossfader");
3289 }
3290 #[test]
3291 fn test_logic_and_default_reset_sample_rate() {
3292 let mut gate = LogicAnd::default();
3293 gate.reset();
3294 gate.set_sample_rate(48000.0);
3295 assert_eq!(gate.type_id(), "logic_and");
3296 }
3297 #[test]
3298 fn test_logic_or_default_reset_sample_rate() {
3299 let mut gate = LogicOr::default();
3300 gate.reset();
3301 gate.set_sample_rate(48000.0);
3302 assert_eq!(gate.type_id(), "logic_or");
3303 }
3304 #[test]
3305 fn test_logic_xor_default_reset_sample_rate() {
3306 let mut gate = LogicXor::default();
3307 gate.reset();
3308 gate.set_sample_rate(48000.0);
3309 assert_eq!(gate.type_id(), "logic_xor");
3310 }
3311 #[test]
3312 fn test_logic_not_default_reset_sample_rate() {
3313 let mut gate = LogicNot::default();
3314 gate.reset();
3315 gate.set_sample_rate(48000.0);
3316 assert_eq!(gate.type_id(), "logic_not");
3317 }
3318 #[test]
3319 fn test_comparator_default_reset_sample_rate() {
3320 let mut cmp = Comparator::default();
3321 cmp.reset();
3322 cmp.set_sample_rate(48000.0);
3323 assert_eq!(cmp.type_id(), "comparator");
3324 }
3325 #[test]
3326 fn test_rectifier_default_reset_sample_rate() {
3327 let mut rect = Rectifier::default();
3328 rect.reset();
3329 rect.set_sample_rate(48000.0);
3330 assert_eq!(rect.type_id(), "rectifier");
3331 }
3332 #[test]
3333 fn test_precision_adder_default_reset_sample_rate() {
3334 let mut adder = PrecisionAdder::default();
3335 adder.reset();
3336 adder.set_sample_rate(48000.0);
3337 assert_eq!(adder.type_id(), "precision_adder");
3338 }
3339 #[test]
3340 fn test_vc_switch_default_reset_sample_rate() {
3341 let mut sw = VcSwitch::default();
3342 sw.reset();
3343 sw.set_sample_rate(48000.0);
3344 assert_eq!(sw.type_id(), "vc_switch");
3345 }
3346 #[test]
3347 fn test_bernoulli_gate_default_reset_sample_rate() {
3348 let mut bg = BernoulliGate::default();
3349 let mut inputs = PortValues::new();
3350 let mut outputs = PortValues::new();
3351 inputs.set(0, 5.0);
3352 bg.tick(&inputs, &mut outputs);
3353
3354 bg.reset();
3355 assert!(bg.prev_trigger == 0.0);
3356
3357 bg.set_sample_rate(48000.0);
3358 assert_eq!(bg.type_id(), "bernoulli_gate");
3359 }
3360 #[test]
3361 fn test_min_default_reset_sample_rate() {
3362 let mut m = Min::default();
3363 m.reset();
3364 m.set_sample_rate(48000.0);
3365 assert_eq!(m.type_id(), "min");
3366 }
3367 #[test]
3368 fn test_max_default_reset_sample_rate() {
3369 let mut m = Max::default();
3370 m.reset();
3371 m.set_sample_rate(48000.0);
3372 assert_eq!(m.type_id(), "max");
3373 }
3374 #[test]
3375 fn test_step_sequencer_skip_disabled() {
3376 let mut seq = StepSequencer::new();
3377 seq.set_step(0, 1.0, true);
3378 seq.set_step(1, 2.0, false); seq.set_step(2, 3.0, true);
3380
3381 let mut inputs = PortValues::new();
3382 let mut outputs = PortValues::new();
3383
3384 seq.tick(&inputs, &mut outputs);
3386 let _out = outputs.get(10).unwrap_or(0.0);
3387
3388 inputs.set(0, 5.0);
3390 seq.tick(&inputs, &mut outputs);
3391 }
3392 #[test]
3393 fn test_quantizer_pentatonic_scale() {
3394 let mut quant = Quantizer::new(Scale::PentatonicMajor);
3395 let mut inputs = PortValues::new();
3396 let mut outputs = PortValues::new();
3397
3398 inputs.set(0, 0.0);
3400 quant.tick(&inputs, &mut outputs);
3401 assert!(outputs.get(10).unwrap().abs() < 0.01);
3402 }
3403 #[test]
3404 fn test_quantizer_blues_scale() {
3405 let mut quant = Quantizer::new(Scale::Blues);
3406 let mut inputs = PortValues::new();
3407 let mut outputs = PortValues::new();
3408
3409 inputs.set(0, 0.0);
3410 quant.tick(&inputs, &mut outputs);
3411 assert!(outputs.get(10).is_some());
3412 }
3413 #[test]
3414 fn test_slew_limiter_falling() {
3415 let mut slew = SlewLimiter::new(1000.0);
3416 let mut inputs = PortValues::new();
3417 let mut outputs = PortValues::new();
3418
3419 inputs.set(0, 5.0);
3421 inputs.set(1, 10.0); inputs.set(2, 0.5); for _ in 0..1000 {
3424 slew.tick(&inputs, &mut outputs);
3425 }
3426
3427 inputs.set(0, 0.0);
3429 slew.tick(&inputs, &mut outputs);
3430 let falling = outputs.get(10).unwrap();
3431 assert!(falling < 5.0);
3432 assert!(falling > 0.0);
3433 }
3434 #[test]
3435 fn test_scale_dorian_and_mixolydian() {
3436 let scale = Scale::Dorian;
3437 assert!(scale.semitones().len() == 7);
3438
3439 let scale = Scale::Mixolydian;
3440 assert!(scale.semitones().len() == 7);
3441 }
3442 #[test]
3443 fn test_clock_subdivisions() {
3444 let mut clock = Clock::new(1000.0);
3445 let mut inputs = PortValues::new();
3446 let mut outputs = PortValues::new();
3447
3448 inputs.set(0, 5.0); for _ in 0..1000 {
3452 clock.tick(&inputs, &mut outputs);
3453 }
3454
3455 assert!(outputs.get(10).is_some()); assert!(outputs.get(11).is_some()); assert!(outputs.get(12).is_some()); }
3460 #[test]
3461 fn test_chord_memory_major() {
3462 let mut cm = ChordMemory::new();
3463 let mut inputs = PortValues::new();
3464 let mut outputs = PortValues::new();
3465
3466 inputs.set(0, 0.0);
3468 inputs.set(1, 0.0); inputs.set(2, 0.0); inputs.set(3, 0.0); cm.tick(&inputs, &mut outputs);
3473
3474 let voice1 = outputs.get(10).unwrap();
3476 let voice2 = outputs.get(11).unwrap();
3477 let voice3 = outputs.get(12).unwrap();
3478 let voice4 = outputs.get(13).unwrap();
3479
3480 assert!((voice1 - 0.0).abs() < 0.01); assert!((voice2 - 4.0 / 12.0).abs() < 0.01); assert!((voice3 - 7.0 / 12.0).abs() < 0.01); assert!((voice4 - 1.0).abs() < 0.01); }
3485 #[test]
3486 fn test_chord_memory_minor() {
3487 let mut cm = ChordMemory::new();
3488 let mut inputs = PortValues::new();
3489 let mut outputs = PortValues::new();
3490
3491 inputs.set(0, 0.0);
3492 inputs.set(1, 0.15); cm.tick(&inputs, &mut outputs);
3495
3496 let voice2 = outputs.get(11).unwrap();
3498 assert!((voice2 - 3.0 / 12.0).abs() < 0.01); }
3500 #[test]
3501 fn test_chord_memory_seventh() {
3502 let mut cm = ChordMemory::new();
3503 let mut inputs = PortValues::new();
3504 let mut outputs = PortValues::new();
3505
3506 inputs.set(0, 0.0);
3507 inputs.set(1, 0.26); cm.tick(&inputs, &mut outputs);
3510
3511 let voice4 = outputs.get(13).unwrap();
3513 assert!((voice4 - 10.0 / 12.0).abs() < 0.01); }
3515 #[test]
3516 fn test_chord_memory_inversion() {
3517 let mut cm = ChordMemory::new();
3518 let mut inputs = PortValues::new();
3519 let mut outputs = PortValues::new();
3520
3521 inputs.set(0, 0.0);
3522 inputs.set(1, 0.0); inputs.set(2, 0.4); cm.tick(&inputs, &mut outputs);
3526
3527 let voice1 = outputs.get(10).unwrap();
3529 let voice2 = outputs.get(11).unwrap();
3530 let voice3 = outputs.get(12).unwrap();
3531
3532 assert!((voice1 - 4.0 / 12.0).abs() < 0.01);
3534 assert!((voice2 - 7.0 / 12.0).abs() < 0.01);
3536 assert!((voice3 - 1.0).abs() < 0.01);
3538 }
3539 #[test]
3540 fn test_chord_memory_spread() {
3541 let mut cm = ChordMemory::new();
3542 let mut inputs = PortValues::new();
3543 let mut outputs = PortValues::new();
3544
3545 inputs.set(0, 0.0);
3546 inputs.set(1, 0.0); inputs.set(2, 0.0); inputs.set(3, 1.0); cm.tick(&inputs, &mut outputs);
3551
3552 let voice1 = outputs.get(10).unwrap();
3553 let voice2 = outputs.get(11).unwrap();
3554 let voice3 = outputs.get(12).unwrap();
3555 let voice4 = outputs.get(13).unwrap();
3556
3557 assert!(voice1 < voice2);
3563 assert!(voice2 < voice3);
3564 assert!(voice3 < voice4);
3565 }
3566 #[test]
3567 fn test_chord_memory_all_chord_types() {
3568 let mut cm = ChordMemory::new();
3569 let mut inputs = PortValues::new();
3570 let mut outputs = PortValues::new();
3571
3572 for i in 0..9 {
3574 let chord_cv = i as f64 / 9.0;
3575 inputs.set(0, 0.0);
3576 inputs.set(1, chord_cv);
3577
3578 cm.tick(&inputs, &mut outputs);
3579
3580 assert!(outputs.get(10).is_some());
3582 assert!(outputs.get(11).is_some());
3583 assert!(outputs.get(12).is_some());
3584 assert!(outputs.get(13).is_some());
3585 }
3586 }
3587 #[test]
3588 fn test_chord_memory_default_reset_sample_rate() {
3589 let mut cm = ChordMemory::default();
3590 cm.reset();
3591 cm.set_sample_rate(48000.0);
3592 assert_eq!(cm.type_id(), "chord_memory");
3593
3594 assert_eq!(cm.port_spec().inputs.len(), 4);
3596 assert_eq!(cm.port_spec().outputs.len(), 4);
3597 }
3598 #[test]
3599 fn test_chord_type_intervals() {
3600 assert_eq!(ChordType::Major.intervals(), &[0, 4, 7]);
3602 assert_eq!(ChordType::Minor.intervals(), &[0, 3, 7]);
3603 assert_eq!(ChordType::Seventh.intervals(), &[0, 4, 7, 10]);
3604 assert_eq!(ChordType::MajorSeventh.intervals(), &[0, 4, 7, 11]);
3605 assert_eq!(ChordType::MinorSeventh.intervals(), &[0, 3, 7, 10]);
3606 assert_eq!(ChordType::Diminished.intervals(), &[0, 3, 6]);
3607 assert_eq!(ChordType::Augmented.intervals(), &[0, 4, 8]);
3608 assert_eq!(ChordType::Sus2.intervals(), &[0, 2, 7]);
3609 assert_eq!(ChordType::Sus4.intervals(), &[0, 5, 7]);
3610 }
3611 #[test]
3612 fn test_chord_type_from_cv() {
3613 assert_eq!(ChordType::from_cv(0.0), ChordType::Major);
3614 assert_eq!(ChordType::from_cv(0.12), ChordType::Minor);
3615 assert_eq!(ChordType::from_cv(0.23), ChordType::Seventh);
3616 assert_eq!(ChordType::from_cv(1.0), ChordType::Sus4);
3617 }
3618 #[test]
3619 fn test_arp_pattern_from_cv() {
3620 assert_eq!(ArpPattern::from_cv(0.0), ArpPattern::Up);
3621 assert_eq!(ArpPattern::from_cv(0.1), ArpPattern::Up);
3622 assert_eq!(ArpPattern::from_cv(0.3), ArpPattern::Down);
3623 assert_eq!(ArpPattern::from_cv(0.6), ArpPattern::UpDown);
3624 assert_eq!(ArpPattern::from_cv(0.9), ArpPattern::Random);
3625 assert_eq!(ArpPattern::from_cv(1.0), ArpPattern::Random);
3626 }
3627 #[test]
3628 fn test_arpeggiator_default_reset_sample_rate() {
3629 let mut arp = Arpeggiator::default();
3630 assert_eq!(arp.sample_rate, 44100.0);
3631
3632 arp.add_note(0.0);
3634 assert_eq!(arp.num_notes, 1);
3635
3636 arp.reset();
3638 assert_eq!(arp.num_notes, 0);
3639 assert_eq!(arp.current_step, 0);
3640
3641 arp.set_sample_rate(48000.0);
3643 assert_eq!(arp.sample_rate, 48000.0);
3644
3645 assert_eq!(arp.type_id(), "arpeggiator");
3646 assert_eq!(arp.port_spec().inputs.len(), 6);
3647 assert_eq!(arp.port_spec().outputs.len(), 3);
3648 }
3649 #[test]
3650 fn test_arpeggiator_add_remove_notes() {
3651 let mut arp = Arpeggiator::new(44100.0);
3652
3653 arp.add_note(0.0); arp.add_note(0.5); arp.add_note(0.25); assert_eq!(arp.num_notes, 3);
3659 assert_eq!(arp.held_notes[0], 0.0);
3661 assert_eq!(arp.held_notes[1], 0.25);
3662 assert_eq!(arp.held_notes[2], 0.5);
3663
3664 arp.remove_note(0.25);
3666 assert_eq!(arp.num_notes, 2);
3667 assert_eq!(arp.held_notes[0], 0.0);
3668 assert_eq!(arp.held_notes[1], 0.5);
3669 }
3670 #[test]
3671 fn test_arpeggiator_up_pattern() {
3672 let mut arp = Arpeggiator::new(44100.0);
3673 let mut inputs = PortValues::new();
3674 let mut outputs = PortValues::new();
3675
3676 arp.add_note(0.0); arp.add_note(0.333); arp.add_note(0.583); inputs.set(1, 0.0); assert_eq!(arp.num_notes, 3);
3685
3686 inputs.set(3, 0.0); let mut notes_out = Vec::new();
3689
3690 for _ in 0..6 {
3691 inputs.set(2, 5.0); arp.tick(&inputs, &mut outputs);
3693 notes_out.push(outputs.get(10).unwrap());
3694
3695 inputs.set(2, 0.0); arp.tick(&inputs, &mut outputs);
3697 }
3698
3699 assert!(notes_out[0] < notes_out[1]);
3701 assert!(notes_out[1] < notes_out[2]);
3702 assert!((notes_out[3] - notes_out[0]).abs() < 0.01);
3704 }
3705 #[test]
3706 fn test_arpeggiator_trigger_output() {
3707 let mut arp = Arpeggiator::new(44100.0);
3708 let mut inputs = PortValues::new();
3709 let mut outputs = PortValues::new();
3710
3711 inputs.set(0, 0.0);
3713 inputs.set(1, 5.0);
3714 arp.tick(&inputs, &mut outputs);
3715
3716 inputs.set(2, 5.0);
3718 arp.tick(&inputs, &mut outputs);
3719 let trigger = outputs.get(12).unwrap();
3720 assert!(trigger > 0.0, "Should output trigger on clock");
3721
3722 inputs.set(2, 0.0);
3724 arp.tick(&inputs, &mut outputs);
3725 let trigger2 = outputs.get(12).unwrap();
3726 assert!(trigger2 > 0.0, "Trigger should persist briefly");
3727 }
3728 #[test]
3729 fn test_arpeggiator_reset_input() {
3730 let mut arp = Arpeggiator::new(44100.0);
3731 let mut inputs = PortValues::new();
3732 let mut outputs = PortValues::new();
3733
3734 inputs.set(0, 0.0);
3736 inputs.set(1, 5.0);
3737 arp.tick(&inputs, &mut outputs);
3738
3739 for _ in 0..5 {
3740 inputs.set(2, 5.0);
3741 arp.tick(&inputs, &mut outputs);
3742 inputs.set(2, 0.0);
3743 arp.tick(&inputs, &mut outputs);
3744 }
3745
3746 let step_before = arp.current_step;
3747 assert!(step_before > 0);
3748
3749 inputs.set(5, 5.0);
3751 arp.tick(&inputs, &mut outputs);
3752
3753 assert_eq!(arp.current_step, 0, "Reset should clear step");
3754 }
3755 #[test]
3756 fn test_arpeggiator_octaves() {
3757 let mut arp = Arpeggiator::new(44100.0);
3758
3759 arp.add_note(0.0); let note1 = arp.get_current_note(ArpPattern::Up, 2);
3764 arp.current_step = 1;
3765 let note2 = arp.get_current_note(ArpPattern::Up, 2);
3766
3767 assert!(
3768 (note2 - note1 - 1.0).abs() < 0.01,
3769 "Second note should be 1 octave higher"
3770 );
3771 }
3772 #[test]
3773 fn test_mixer_summation_bounded() {
3774 let mut mixer = Mixer::new(4);
3776 let mut inputs = PortValues::new();
3777 let mut outputs = PortValues::new();
3778
3779 for i in 0..4 {
3781 inputs.set(i as u32, 5.0);
3782 }
3783
3784 mixer.tick(&inputs, &mut outputs);
3785 let out = outputs.get(100).unwrap_or(0.0);
3786
3787 assert!(
3790 out.abs() <= SAFE_AUDIO_LIMIT * 2.0,
3791 "Mixer output {} is very high - consider adding limiting",
3792 out
3793 );
3794 }
3795
3796 #[test]
3799 fn test_scale_quantizer_octave_wrap_minor_11() {
3800 assert_eq!(
3802 ScaleQuantizer::quantize_to_scale(11, &ScaleQuantizer::MINOR),
3803 12
3804 );
3805 }
3806
3807 #[test]
3808 fn test_scale_quantizer_monotonic_sweep() {
3809 for scale in [
3813 &ScaleQuantizer::MINOR[..],
3814 &ScaleQuantizer::PENT_MAJOR[..],
3815 &ScaleQuantizer::BLUES[..],
3816 ] {
3817 let mut prev = i32::MIN;
3818 for note in 0..=24 {
3819 let q = ScaleQuantizer::quantize_to_scale(note, scale);
3820 assert!(
3821 q >= prev,
3822 "non-monotonic: note {} -> {} after {}",
3823 note,
3824 q,
3825 prev
3826 );
3827 prev = q;
3828 }
3829 }
3830 }
3831
3832 #[test]
3835 fn test_quantizer_hysteresis_no_chatter() {
3836 let mut quant = Quantizer::new(Scale::Chromatic);
3840 let mut inputs = PortValues::new();
3841 let mut outputs = PortValues::new();
3842
3843 let n = 4000;
3844 let mut changes = 0;
3845 let mut prev: Option<f64> = None;
3846 for i in 0..=n {
3847 let base = 2.0 * i as f64 / n as f64; let dither = if i % 2 == 0 { 0.1 } else { -0.1 };
3849 inputs.set(0, (base + dither) / 12.0);
3850 quant.tick(&inputs, &mut outputs);
3851 let out = outputs.get(10).unwrap();
3852 if let Some(p) = prev {
3853 if (out - p).abs() > 1e-9 {
3854 changes += 1;
3855 }
3856 }
3857 prev = Some(out);
3858 }
3859 assert_eq!(changes, 2, "expected exactly two boundary crossings");
3860 }
3861
3862 #[test]
3863 fn test_scale_quantizer_trigger_once_per_boundary() {
3864 let mut sq = ScaleQuantizer::new(44100.0);
3867 let mut inputs = PortValues::new();
3868 let mut outputs = PortValues::new();
3869 inputs.set(2, 0.0); let n = 4000;
3872 let mut triggers = 0;
3873 for i in 0..=n {
3874 let base = 2.0 * i as f64 / n as f64;
3875 let dither = if i % 2 == 0 { 0.1 } else { -0.1 };
3876 inputs.set(0, (base + dither) / 12.0);
3877 sq.tick(&inputs, &mut outputs);
3878 if outputs.get(11).unwrap() > 2.5 {
3879 triggers += 1;
3880 }
3881 }
3882 assert_eq!(triggers, 2, "trigger should fire once per boundary");
3883 }
3884
3885 #[test]
3886 fn test_comparator_hysteresis_no_chatter() {
3887 let mut cmp = Comparator::new();
3890 let mut inputs = PortValues::new();
3891 let mut outputs = PortValues::new();
3892 inputs.set(1, 0.0); inputs.set(0, 0.0);
3896 cmp.tick(&inputs, &mut outputs);
3897
3898 let mut gt_high = 0;
3899 let mut lt_high = 0;
3900 for i in 0..200 {
3901 let a = if i % 2 == 0 { 0.02 } else { -0.02 };
3902 inputs.set(0, a);
3903 cmp.tick(&inputs, &mut outputs);
3904 if outputs.get(10).unwrap() > 2.5 {
3905 gt_high += 1;
3906 }
3907 if outputs.get(11).unwrap() > 2.5 {
3908 lt_high += 1;
3909 }
3910 assert!(outputs.get(12).unwrap() > 2.5, "should stay equal");
3911 }
3912 assert_eq!(gt_high, 0, "gt should never fire on sub-band dither");
3913 assert_eq!(lt_high, 0, "lt should never fire on sub-band dither");
3914 }
3915
3916 #[test]
3917 fn test_comparator_still_compares() {
3918 let mut cmp = Comparator::new();
3920 let mut inputs = PortValues::new();
3921 let mut outputs = PortValues::new();
3922
3923 inputs.set(0, 3.0);
3924 inputs.set(1, 1.0);
3925 cmp.tick(&inputs, &mut outputs);
3926 assert!(outputs.get(10).unwrap() > 2.5);
3927
3928 inputs.set(0, 1.0);
3929 inputs.set(1, 3.0);
3930 cmp.tick(&inputs, &mut outputs);
3931 assert!(outputs.get(11).unwrap() > 2.5);
3932
3933 inputs.set(0, 2.0);
3934 inputs.set(1, 2.0);
3935 cmp.tick(&inputs, &mut outputs);
3936 assert!(outputs.get(12).unwrap() > 2.5);
3937 }
3938
3939 #[test]
3942 fn test_clock_divided_outputs() {
3943 let mut clock = Clock::new(1000.0);
3945 let mut inputs = PortValues::new();
3946 let mut outputs = PortValues::new();
3947 inputs.set(0, 5.0); let (mut main_c, mut div2_c, mut div4_c) = (0, 0, 0);
3950 let (mut pm, mut p2, mut p4) = (0.0, 0.0, 0.0);
3951 for _ in 0..100_000 {
3952 clock.tick(&inputs, &mut outputs);
3953 let m = outputs.get(10).unwrap();
3954 let d2 = outputs.get(11).unwrap();
3955 let d4 = outputs.get(12).unwrap();
3956 let m_rise = m > 2.5 && pm <= 2.5;
3957 if m_rise {
3958 main_c += 1;
3959 }
3960 if d2 > 2.5 && p2 <= 2.5 {
3961 div2_c += 1;
3962 }
3963 if d4 > 2.5 && p4 <= 2.5 {
3964 div4_c += 1;
3965 }
3966 pm = m;
3967 p2 = d2;
3968 p4 = d4;
3969 if m_rise && main_c == 8 {
3970 break;
3971 }
3972 }
3973 assert_eq!(main_c, 8, "main should pulse every cycle");
3974 assert_eq!(div2_c, 4, "div2 should pulse at half rate");
3975 assert_eq!(div4_c, 2, "div4 should pulse at quarter rate");
3976 }
3977
3978 #[test]
3979 fn test_clock_default_tempo_120_bpm() {
3980 let mut clock = Clock::default();
3982 let inputs = PortValues::new(); let mut outputs = PortValues::new();
3984
3985 let mut edges = Vec::new();
3986 let mut prev = 0.0;
3987 for i in 0..100_000 {
3988 clock.tick(&inputs, &mut outputs);
3989 let m = outputs.get(10).unwrap();
3990 if m > 2.5 && prev <= 2.5 {
3991 edges.push(i);
3992 }
3993 prev = m;
3994 if edges.len() >= 2 {
3995 break;
3996 }
3997 }
3998 assert!(edges.len() >= 2, "expected at least two clock pulses");
3999 let period = (edges[1] - edges[0]) as f64;
4000 let bpm = 60.0 * 44100.0 / period;
4001 assert!(
4002 (bpm - 120.0).abs() < 1.0,
4003 "default tempo {} BPM should be ~120",
4004 bpm
4005 );
4006 }
4007
4008 #[test]
4011 fn test_bernoulli_gate_latches() {
4012 let mut bg = BernoulliGate::new();
4013 let mut inputs = PortValues::new();
4014
4015 inputs.set(1, 10.0);
4017
4018 let tick = |bg: &mut BernoulliGate, inputs: &PortValues| {
4021 let mut o = PortValues::new();
4022 bg.tick(inputs, &mut o);
4023 o
4024 };
4025
4026 inputs.set(0, 0.0);
4027 tick(&mut bg, &inputs);
4028 inputs.set(0, 5.0);
4029 let o = tick(&mut bg, &inputs); assert!(o.get(12).unwrap() > 2.5, "gate_a should latch high");
4031 assert!(o.get(13).unwrap() < 2.5);
4032
4033 inputs.set(0, 0.0);
4035 for _ in 0..20 {
4036 let o = tick(&mut bg, &inputs);
4037 assert!(o.get(12).unwrap() > 2.5, "gate_a must stay latched");
4038 assert!(o.get(13).unwrap() < 2.5);
4039 }
4040
4041 inputs.set(1, 0.0);
4043 inputs.set(0, 0.0);
4044 tick(&mut bg, &inputs);
4045 inputs.set(0, 5.0);
4046 let o = tick(&mut bg, &inputs); assert!(o.get(12).unwrap() < 2.5, "gate_a should release");
4048 assert!(o.get(13).unwrap() > 2.5, "gate_b should latch high");
4049 }
4050
4051 #[test]
4054 fn test_euclidean_pulses_control_live() {
4055 let mut euc = Euclidean::new(44100.0);
4057 let mut inputs = PortValues::new();
4058 let mut outputs = PortValues::new();
4059 inputs.set(1, 0.5); inputs.set(2, 0.25); euc.tick(&inputs, &mut outputs);
4063 let active_low = euc.pattern.iter().filter(|&&x| x).count();
4064 assert_eq!(active_low, 2);
4065
4066 inputs.set(2, 0.75); euc.tick(&inputs, &mut outputs);
4068 let active_high = euc.pattern.iter().filter(|&&x| x).count();
4069 assert_eq!(active_high, 6);
4070 assert_ne!(active_low, active_high, "pulses control must be live");
4071 }
4072
4073 #[test]
4074 fn test_euclidean_accent_on_rotated_pulse() {
4075 let mut euc = Euclidean::new(44100.0);
4078 let mut inputs = PortValues::new();
4079 let mut outputs = PortValues::new();
4080 inputs.set(1, 0.4003); inputs.set(2, 0.5); inputs.set(3, 0.3); let mut accents = 0;
4085 for _ in 0..8 {
4086 inputs.set(0, 5.0); euc.tick(&inputs, &mut outputs);
4088 let out = outputs.get(10).unwrap();
4089 let accent = outputs.get(11).unwrap();
4090 if accent > 2.5 {
4091 accents += 1;
4092 assert!(out > 2.5, "accent must coincide with a pulse");
4093 }
4094 inputs.set(0, 0.0); euc.tick(&inputs, &mut outputs);
4096 }
4097 assert_eq!(accents, 1, "exactly one accent per cycle");
4098 }
4099
4100 #[test]
4101 fn test_euclidean_gate_threshold() {
4102 let mut euc = Euclidean::new(44100.0);
4105 let mut inputs = PortValues::new();
4106 let mut outputs = PortValues::new();
4107 inputs.set(1, 0.4003); inputs.set(2, 1.0); let mut low_pulses = 0;
4112 for _ in 0..8 {
4113 inputs.set(0, 1.0);
4114 euc.tick(&inputs, &mut outputs);
4115 if outputs.get(10).unwrap() > 2.5 {
4116 low_pulses += 1;
4117 }
4118 inputs.set(0, 0.0);
4119 euc.tick(&inputs, &mut outputs);
4120 }
4121 assert_eq!(low_pulses, 0, "1.0V clock must not trigger");
4122
4123 let mut high_pulses = 0;
4125 for _ in 0..8 {
4126 inputs.set(0, 5.0);
4127 euc.tick(&inputs, &mut outputs);
4128 if outputs.get(10).unwrap() > 2.5 {
4129 high_pulses += 1;
4130 }
4131 inputs.set(0, 0.0);
4132 euc.tick(&inputs, &mut outputs);
4133 }
4134 assert!(high_pulses > 0, "5V clock must trigger");
4135 }
4136
4137 #[test]
4140 fn test_arpeggiator_releases_notes() {
4141 let mut arp = Arpeggiator::new(44100.0);
4142 let mut inputs = PortValues::new();
4143 let mut outputs = PortValues::new();
4144
4145 inputs.set(0, 0.25);
4147 inputs.set(1, 5.0);
4148 arp.tick(&inputs, &mut outputs);
4149 assert_eq!(arp.num_notes, 1);
4150
4151 for _ in 0..5 {
4153 arp.tick(&inputs, &mut outputs);
4154 }
4155 assert_eq!(arp.num_notes, 1);
4156
4157 inputs.set(1, 0.0);
4159 arp.tick(&inputs, &mut outputs);
4160 assert_eq!(arp.num_notes, 0, "release must remove the held note");
4161
4162 inputs.set(0, 0.5);
4164 inputs.set(1, 5.0);
4165 arp.tick(&inputs, &mut outputs);
4166 assert_eq!(arp.num_notes, 1);
4167 inputs.set(1, 0.0);
4168 arp.tick(&inputs, &mut outputs);
4169 assert_eq!(arp.num_notes, 0);
4170 }
4171
4172 #[test]
4173 fn test_arpeggiator_reset_clears_held_notes() {
4174 let mut arp = Arpeggiator::new(44100.0);
4175 let mut inputs = PortValues::new();
4176 let mut outputs = PortValues::new();
4177
4178 inputs.set(0, 0.0);
4180 inputs.set(1, 5.0);
4181 arp.tick(&inputs, &mut outputs);
4182 assert_eq!(arp.num_notes, 1);
4183
4184 inputs.set(5, 5.0);
4186 arp.tick(&inputs, &mut outputs);
4187 assert_eq!(arp.num_notes, 0, "reset must clear held notes");
4188 }
4189
4190 #[cfg(feature = "alloc")]
4195 #[test]
4196 fn test_custom_scale_snaps_to_degrees() {
4197 let mut sq = ScaleQuantizer::new(44100.0);
4199 assert!(!sq.has_custom_scale());
4200 sq.set_custom_scale(&[0.0, 200.0, 400.0, 600.0, 800.0, 1000.0]);
4201 assert!(sq.has_custom_scale());
4202
4203 let mut inputs = PortValues::new();
4204 let mut outputs = PortValues::new();
4205
4206 inputs.set(0, 0.175);
4210 for _ in 0..4 {
4212 sq.tick(&inputs, &mut outputs);
4213 }
4214 let out_v = outputs.get(10).unwrap();
4215 assert!(
4217 (out_v - 200.0 / 1200.0).abs() < 1e-6,
4218 "custom scale should snap to 200 cents, got {} cents",
4219 out_v * 1200.0
4220 );
4221 }
4222
4223 #[cfg(feature = "alloc")]
4224 #[test]
4225 fn test_load_scala_and_quantize() {
4226 let scl = "\
4227whole tone
42286
4229200.0
4230400.0
4231600.0
4232800.0
42331000.0
42341200.0
4235";
4236 let mut sq = ScaleQuantizer::new(44100.0);
4237 sq.load_scala(scl).unwrap();
4238 assert!(sq.has_custom_scale());
4239
4240 let mut inputs = PortValues::new();
4241 let mut outputs = PortValues::new();
4242 inputs.set(0, 390.0 / 1200.0);
4244 for _ in 0..4 {
4245 sq.tick(&inputs, &mut outputs);
4246 }
4247 let out_v = outputs.get(10).unwrap();
4248 assert!(
4249 (out_v - 400.0 / 1200.0).abs() < 1e-6,
4250 "expected 400 cents, got {} cents",
4251 out_v * 1200.0
4252 );
4253 }
4254
4255 #[cfg(feature = "alloc")]
4256 #[test]
4257 fn test_clear_custom_scale_restores_enum() {
4258 let mut sq = ScaleQuantizer::new(44100.0);
4259 sq.set_custom_scale(&[0.0, 200.0, 400.0]);
4260 assert!(sq.has_custom_scale());
4261 sq.clear_custom_scale();
4262 assert!(!sq.has_custom_scale());
4263 }
4264
4265 #[cfg(feature = "alloc")]
4266 #[test]
4267 fn test_load_scala_malformed_leaves_scale_unchanged() {
4268 let mut sq = ScaleQuantizer::new(44100.0);
4269 sq.set_custom_scale(&[0.0, 500.0]);
4270 let err = sq.load_scala("bad\n3\n100.0\n");
4272 assert!(err.is_err());
4273 assert!(sq.has_custom_scale());
4275 }
4276
4277 #[test]
4280 fn test_quantizer_negative_voct_chromatic() {
4281 let cases = [
4283 (-0.5, -0.5), (-1.0, -1.0), (-13.0 / 12.0, -13.0 / 12.0), (-1.0 / 24.0, 0.0), ];
4288 for (input, expected) in cases {
4289 let mut q = Quantizer::new(Scale::Chromatic);
4290 let mut inputs = PortValues::new();
4291 let mut outputs = PortValues::new();
4292 inputs.set(0, input);
4293 q.tick(&inputs, &mut outputs);
4294 let out = outputs.get(10).unwrap();
4295 assert!(
4296 (out - expected).abs() < 1e-9,
4297 "chromatic {input}V -> {out}V, expected {expected}V"
4298 );
4299 }
4300 }
4301
4302 #[test]
4303 fn test_quantizer_negative_voct_major_scale() {
4304 let mut q = Quantizer::new(Scale::Major);
4307 let mut inputs = PortValues::new();
4308 let mut outputs = PortValues::new();
4309 inputs.set(0, -0.5);
4310 q.tick(&inputs, &mut outputs);
4311 let out = outputs.get(10).unwrap();
4312 assert!(
4313 (out - (-7.0 / 12.0)).abs() < 1e-9,
4314 "major -0.5V should snap to F3 (-7/12 V), got {out}V"
4315 );
4316
4317 let mut q2 = Quantizer::new(Scale::Major);
4319 inputs.set(0, -1.0);
4320 q2.tick(&inputs, &mut outputs);
4321 assert!((outputs.get(10).unwrap() - (-1.0)).abs() < 1e-9);
4322 }
4323
4324 #[test]
4325 fn test_scale_quantizer_negative_voct() {
4326 let cases = [(-1.0, -1.0), (-13.0 / 12.0, -13.0 / 12.0)];
4328 for (input, expected) in cases {
4329 let mut sq = ScaleQuantizer::new(44100.0);
4330 let mut inputs = PortValues::new();
4331 let mut outputs = PortValues::new();
4332 inputs.set(0, input);
4333 inputs.set(2, 0.0); sq.tick(&inputs, &mut outputs);
4335 let out = outputs.get(10).unwrap();
4336 assert!(
4337 (out - expected).abs() < 1e-9,
4338 "scale-quantizer chromatic {input}V -> {out}V, expected {expected}V"
4339 );
4340 }
4341
4342 let mut sq = ScaleQuantizer::new(44100.0);
4344 let mut inputs = PortValues::new();
4345 let mut outputs = PortValues::new();
4346 inputs.set(0, -0.5);
4347 inputs.set(2, 0.3); sq.tick(&inputs, &mut outputs);
4349 let out = outputs.get(10).unwrap();
4350 assert!(
4351 (out - (-7.0 / 12.0)).abs() < 1e-9,
4352 "minor -0.5V should snap to F3 (-7/12 V), got {out}V"
4353 );
4354 }
4355
4356 #[test]
4359 fn test_euclidean_reset_and_sample_rate() {
4360 let mut euc = Euclidean::new(44100.0);
4361 assert_eq!(euc.type_id(), "euclidean");
4362 let mut inputs = PortValues::new();
4363 let mut outputs = PortValues::new();
4364 for _ in 0..3 {
4366 inputs.set(0, 5.0);
4367 euc.tick(&inputs, &mut outputs);
4368 inputs.set(0, 0.0);
4369 euc.tick(&inputs, &mut outputs);
4370 }
4371 assert!(euc.step != 0, "clock pulses should advance the step");
4372 euc.reset();
4373 assert_eq!(euc.step, 0);
4374 assert!(!euc.cycle_accented);
4375 euc.set_sample_rate(48000.0);
4377 inputs.set(0, 5.0);
4378 euc.tick(&inputs, &mut outputs);
4379 assert!(outputs.get(10).unwrap().is_finite());
4380 }
4381
4382 #[test]
4383 fn test_scale_quantizer_reset_and_sample_rate() {
4384 let mut sq = ScaleQuantizer::new(44100.0);
4385 assert_eq!(sq.type_id(), "scale_quantizer");
4386 let mut inputs = PortValues::new();
4387 let mut outputs = PortValues::new();
4388 inputs.set(0, 0.25); sq.tick(&inputs, &mut outputs);
4390 assert!(sq.last_output.is_some());
4391 sq.reset();
4392 assert!(sq.last_output.is_none());
4393 sq.set_sample_rate(48000.0);
4394 sq.tick(&inputs, &mut outputs);
4395 assert!(outputs.get(10).unwrap().is_finite());
4396 }
4397
4398 #[test]
4406 fn test_utilities_memos_bit_identical() {
4407 let mut slew_m = SlewLimiter::new(44100.0);
4408 let mut slew_f = SlewLimiter::new(44100.0);
4409 let mut clk_m = Clock::new(44100.0);
4410 let mut clk_f = Clock::new(44100.0);
4411 let mut inputs = PortValues::new();
4412 let mut out_m = PortValues::new();
4413 let mut out_f = PortValues::new();
4414
4415 for n in 0..20_000u32 {
4416 let t = n as f64;
4417 let sweep = if n < 10_000 {
4418 0.5
4419 } else {
4420 0.5 + 0.3 * Libm::<f64>::sin(t * 0.002)
4421 };
4422
4423 inputs.set(0, if (n / 500) % 2 == 0 { 4.0 } else { -4.0 });
4425 inputs.set(1, sweep);
4426 inputs.set(2, 0.3);
4427 slew_m.tick(&inputs, &mut out_m);
4428 slew_f.rise_memo.invalidate();
4429 slew_f.fall_memo.invalidate();
4430 slew_f.tick(&inputs, &mut out_f);
4431 assert_eq!(
4432 out_m.get(10).unwrap().to_bits(),
4433 out_f.get(10).unwrap().to_bits(),
4434 "SlewLimiter diverged at sample {n}"
4435 );
4436
4437 inputs.set(0, 5.0 + sweep);
4439 clk_m.tick(&inputs, &mut out_m);
4440 clk_f.bpm_memo.invalidate();
4441 clk_f.tick(&inputs, &mut out_f);
4442 for &id in &[10u32, 11, 12] {
4443 assert_eq!(
4444 out_m.get(id).unwrap().to_bits(),
4445 out_f.get(id).unwrap().to_bits(),
4446 "Clock output {id} diverged at sample {n}"
4447 );
4448 }
4449 }
4450 assert!(clk_m.bpm_memo.recompute_count() <= 10_001);
4451 }
4452}