1use super::common::{sanitize_audio, EdgeDetector, 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 spec: PortSpec,
1022}
1023
1024impl SlewLimiter {
1025 pub fn new(sample_rate: f64) -> Self {
1026 Self {
1027 current: 0.0,
1028 sample_rate,
1029 spec: PortSpec {
1030 inputs: vec![
1031 PortDef::new(0, "in", SignalKind::CvBipolar),
1032 PortDef::new(1, "rise", SignalKind::CvUnipolar)
1033 .with_default(0.5)
1034 .with_attenuverter(),
1035 PortDef::new(2, "fall", SignalKind::CvUnipolar)
1036 .with_default(0.5)
1037 .with_attenuverter(),
1038 ],
1039 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
1040 },
1041 }
1042 }
1043
1044 fn cv_to_rate(&self, cv: f64) -> f64 {
1045 let time = 0.001 + Libm::<f64>::pow(cv.clamp(0.0, 1.0), 2.0) * 10.0; 1.0 / (time * self.sample_rate)
1049 }
1050}
1051
1052impl Default for SlewLimiter {
1053 fn default() -> Self {
1054 Self::new(44100.0)
1055 }
1056}
1057
1058impl GraphModule for SlewLimiter {
1059 fn port_spec(&self) -> &PortSpec {
1060 &self.spec
1061 }
1062
1063 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1064 let target = inputs.get_or(0, 0.0);
1065 let rise_cv = inputs.get_or(1, 0.5);
1066 let fall_cv = inputs.get_or(2, 0.5);
1067
1068 let diff = target - self.current;
1069
1070 if diff > 0.0 {
1071 let rate = self.cv_to_rate(rise_cv);
1073 self.current += Libm::<f64>::fmin(diff, rate * 10.0); } else if diff < 0.0 {
1075 let rate = self.cv_to_rate(fall_cv);
1077 self.current += Libm::<f64>::fmax(diff, -rate * 10.0);
1078 }
1079
1080 outputs.set(10, self.current);
1081 }
1082
1083 fn reset(&mut self) {
1084 self.current = 0.0;
1085 }
1086
1087 fn set_sample_rate(&mut self, sample_rate: f64) {
1088 self.sample_rate = sample_rate;
1089 }
1090
1091 fn type_id(&self) -> &'static str {
1092 "slew_limiter"
1093 }
1094}
1095
1096pub struct Quantizer {
1101 pub(crate) scale: Scale,
1102 last_output: Option<f64>,
1104 spec: PortSpec,
1105}
1106
1107#[derive(Debug, Clone, Copy, PartialEq)]
1109pub enum Scale {
1110 Chromatic,
1111 Major,
1112 Minor,
1113 PentatonicMajor,
1114 PentatonicMinor,
1115 Dorian,
1116 Mixolydian,
1117 Blues,
1118}
1119
1120impl Scale {
1121 fn semitones(&self) -> &'static [i32] {
1123 match self {
1124 Scale::Chromatic => &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
1125 Scale::Major => &[0, 2, 4, 5, 7, 9, 11],
1126 Scale::Minor => &[0, 2, 3, 5, 7, 8, 10],
1127 Scale::PentatonicMajor => &[0, 2, 4, 7, 9],
1128 Scale::PentatonicMinor => &[0, 3, 5, 7, 10],
1129 Scale::Dorian => &[0, 2, 3, 5, 7, 9, 10],
1130 Scale::Mixolydian => &[0, 2, 4, 5, 7, 9, 10],
1131 Scale::Blues => &[0, 3, 5, 6, 7, 10],
1132 }
1133 }
1134}
1135
1136impl Quantizer {
1137 pub fn new(scale: Scale) -> Self {
1138 Self {
1139 scale,
1140 last_output: None,
1141 spec: PortSpec {
1142 inputs: vec![PortDef::new(0, "in", SignalKind::VoltPerOctave)],
1143 outputs: vec![PortDef::new(10, "out", SignalKind::VoltPerOctave)],
1144 },
1145 }
1146 }
1147
1148 pub fn chromatic() -> Self {
1149 Self::new(Scale::Chromatic)
1150 }
1151
1152 pub fn major() -> Self {
1153 Self::new(Scale::Major)
1154 }
1155
1156 pub fn minor() -> Self {
1157 Self::new(Scale::Minor)
1158 }
1159
1160 pub fn set_scale(&mut self, scale: Scale) {
1161 self.scale = scale;
1162 }
1163
1164 fn quantize(&self, voltage: f64) -> f64 {
1165 let semitones = self.scale.semitones();
1166
1167 let total_semitones = voltage * 12.0;
1169
1170 let octave = Libm::<f64>::floor(total_semitones / 12.0);
1172 let within_octave = total_semitones - octave * 12.0;
1173
1174 let mut nearest = semitones[0];
1176 let mut min_dist = f64::MAX;
1177
1178 for &semi in semitones {
1179 let dist = (within_octave - semi as f64).abs();
1180 if dist < min_dist {
1181 min_dist = dist;
1182 nearest = semi;
1183 }
1184 let dist_wrap = (within_octave - (semi + 12) as f64).abs();
1186 if dist_wrap < min_dist {
1187 min_dist = dist_wrap;
1188 nearest = semi + 12;
1189 }
1190 }
1191
1192 (octave * 12.0 + nearest as f64) / 12.0
1194 }
1195}
1196
1197impl Default for Quantizer {
1198 fn default() -> Self {
1199 Self::chromatic()
1200 }
1201}
1202
1203impl GraphModule for Quantizer {
1204 fn port_spec(&self) -> &PortSpec {
1205 &self.spec
1206 }
1207
1208 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1209 let input = inputs.get_or(0, 0.0);
1210 let candidate = self.quantize(input);
1211 let committed = hysteretic_note(
1214 self.last_output,
1215 input,
1216 candidate,
1217 NOTE_HYSTERESIS_SEMITONES,
1218 );
1219 self.last_output = Some(committed);
1220 outputs.set(10, committed);
1221 }
1222
1223 fn reset(&mut self) {
1224 self.last_output = None;
1225 }
1226
1227 fn set_sample_rate(&mut self, _: f64) {}
1228
1229 fn type_id(&self) -> &'static str {
1230 "quantizer"
1231 }
1232
1233 crate::impl_introspect!();
1235}
1236
1237pub struct Clock {
1241 phase: f64,
1242 cycle: u64,
1245 sample_rate: f64,
1246 spec: PortSpec,
1247}
1248
1249impl Clock {
1250 const DEFAULT_BPM_CV: f64 = 6.616_418_958_920_283;
1256
1257 pub fn new(sample_rate: f64) -> Self {
1258 Self {
1259 phase: 0.0,
1260 cycle: 0,
1261 sample_rate,
1262 spec: PortSpec {
1263 inputs: vec![
1264 PortDef::new(0, "bpm", SignalKind::CvUnipolar)
1265 .with_default(Self::DEFAULT_BPM_CV) .with_attenuverter(),
1267 PortDef::new(1, "reset", SignalKind::Trigger),
1268 ],
1269 outputs: vec![
1270 PortDef::new(10, "out", SignalKind::Clock),
1271 PortDef::new(11, "div2", SignalKind::Clock),
1272 PortDef::new(12, "div4", SignalKind::Clock),
1273 ],
1274 },
1275 }
1276 }
1277
1278 fn cv_to_bpm(cv: f64) -> f64 {
1279 20.0 * Libm::<f64>::pow(15.0, cv / 10.0)
1281 }
1282}
1283
1284impl Default for Clock {
1285 fn default() -> Self {
1286 Self::new(44100.0)
1287 }
1288}
1289
1290impl GraphModule for Clock {
1291 fn port_spec(&self) -> &PortSpec {
1292 &self.spec
1293 }
1294
1295 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1296 let bpm_cv = inputs.get_or(0, Self::DEFAULT_BPM_CV); let reset = inputs.get_or(1, 0.0);
1298
1299 let bpm = Self::cv_to_bpm(bpm_cv);
1300 let freq = bpm / 60.0; if reset > GATE_THRESHOLD_V {
1304 self.phase = 0.0;
1305 self.cycle = 0;
1306 }
1307
1308 let pulse_width = 0.1; let in_pulse = self.phase < pulse_width;
1311 let main_out = if in_pulse { GATE_HIGH_V } else { 0.0 };
1312
1313 let div2_out = if in_pulse && (self.cycle & 1) == 0 {
1319 GATE_HIGH_V
1320 } else {
1321 0.0
1322 };
1323 let div4_out = if in_pulse && (self.cycle & 3) == 0 {
1324 GATE_HIGH_V
1325 } else {
1326 0.0
1327 };
1328
1329 outputs.set(10, main_out);
1330 outputs.set(11, div2_out);
1331 outputs.set(12, div4_out);
1332
1333 let new_phase = self.phase + freq / self.sample_rate;
1335 let wraps = Libm::<f64>::floor(new_phase);
1336 if wraps > 0.0 {
1337 self.cycle = self.cycle.wrapping_add(wraps as u64);
1338 }
1339 self.phase = new_phase - wraps;
1340 }
1341
1342 fn reset(&mut self) {
1343 self.phase = 0.0;
1344 self.cycle = 0;
1345 }
1346
1347 fn set_sample_rate(&mut self, sample_rate: f64) {
1348 self.sample_rate = sample_rate;
1349 }
1350
1351 fn type_id(&self) -> &'static str {
1352 "clock"
1353 }
1354}
1355
1356pub struct Attenuverter {
1361 spec: PortSpec,
1362}
1363
1364impl Attenuverter {
1365 pub fn new() -> Self {
1366 Self {
1367 spec: PortSpec {
1368 inputs: vec![
1369 PortDef::new(0, "in", SignalKind::CvBipolar),
1370 PortDef::new(1, "level", SignalKind::CvBipolar).with_default(5.0), ],
1372 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
1373 },
1374 }
1375 }
1376}
1377
1378impl Default for Attenuverter {
1379 fn default() -> Self {
1380 Self::new()
1381 }
1382}
1383
1384impl GraphModule for Attenuverter {
1385 fn port_spec(&self) -> &PortSpec {
1386 &self.spec
1387 }
1388
1389 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1390 let input = inputs.get_or(0, 0.0);
1391 let level = inputs.get_or(1, 5.0) / 5.0; outputs.set(10, input * level);
1394 }
1395
1396 fn reset(&mut self) {}
1397
1398 fn set_sample_rate(&mut self, _: f64) {}
1399
1400 fn type_id(&self) -> &'static str {
1401 "attenuverter"
1402 }
1403}
1404
1405pub struct Multiple {
1410 spec: PortSpec,
1411}
1412
1413impl Multiple {
1414 pub fn new() -> Self {
1415 Self {
1416 spec: PortSpec {
1417 inputs: vec![PortDef::new(0, "in", SignalKind::CvBipolar)],
1418 outputs: vec![
1419 PortDef::new(10, "out1", SignalKind::CvBipolar),
1420 PortDef::new(11, "out2", SignalKind::CvBipolar),
1421 PortDef::new(12, "out3", SignalKind::CvBipolar),
1422 PortDef::new(13, "out4", SignalKind::CvBipolar),
1423 ],
1424 },
1425 }
1426 }
1427}
1428
1429impl Default for Multiple {
1430 fn default() -> Self {
1431 Self::new()
1432 }
1433}
1434
1435impl GraphModule for Multiple {
1436 fn port_spec(&self) -> &PortSpec {
1437 &self.spec
1438 }
1439
1440 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1441 let input = inputs.get_or(0, 0.0);
1442
1443 outputs.set(10, input);
1444 outputs.set(11, input);
1445 outputs.set(12, input);
1446 outputs.set(13, input);
1447 }
1448
1449 fn reset(&mut self) {}
1450
1451 fn set_sample_rate(&mut self, _: f64) {}
1452
1453 fn type_id(&self) -> &'static str {
1454 "multiple"
1455 }
1456}
1457
1458pub struct Crossfader {
1467 spec: PortSpec,
1468}
1469
1470impl Crossfader {
1471 pub fn new() -> Self {
1472 Self {
1473 spec: PortSpec {
1474 inputs: vec![
1475 PortDef::new(0, "a", SignalKind::Audio),
1476 PortDef::new(1, "b", SignalKind::Audio),
1477 PortDef::new(2, "pos", SignalKind::CvBipolar).with_default(0.0),
1478 ],
1479 outputs: vec![
1480 PortDef::new(10, "out", SignalKind::Audio),
1481 PortDef::new(11, "left", SignalKind::Audio),
1482 PortDef::new(12, "right", SignalKind::Audio),
1483 ],
1484 },
1485 }
1486 }
1487}
1488
1489impl Default for Crossfader {
1490 fn default() -> Self {
1491 Self::new()
1492 }
1493}
1494
1495impl GraphModule for Crossfader {
1496 fn port_spec(&self) -> &PortSpec {
1497 &self.spec
1498 }
1499
1500 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1501 let a = inputs.get_or(0, 0.0);
1502 let b = inputs.get_or(1, 0.0);
1503 let pos = inputs.get_or(2, 0.0);
1504
1505 let mix = ((pos / 5.0) + 1.0) / 2.0;
1507 let mix = mix.clamp(0.0, 1.0);
1508
1509 let a_gain = Libm::<f64>::sqrt(1.0 - mix);
1511 let b_gain = Libm::<f64>::sqrt(mix);
1512
1513 let out = a * a_gain + b * b_gain;
1515 outputs.set(10, out);
1516
1517 outputs.set(11, out * a_gain); outputs.set(12, out * b_gain); }
1522
1523 fn reset(&mut self) {}
1524
1525 fn set_sample_rate(&mut self, _: f64) {}
1526
1527 fn type_id(&self) -> &'static str {
1528 "crossfader"
1529 }
1530}
1531
1532pub struct LogicAnd {
1536 spec: PortSpec,
1537}
1538
1539impl LogicAnd {
1540 pub fn new() -> Self {
1541 Self {
1542 spec: PortSpec {
1543 inputs: vec![
1544 PortDef::new(0, "a", SignalKind::Gate),
1545 PortDef::new(1, "b", SignalKind::Gate),
1546 ],
1547 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1548 },
1549 }
1550 }
1551}
1552
1553impl Default for LogicAnd {
1554 fn default() -> Self {
1555 Self::new()
1556 }
1557}
1558
1559impl GraphModule for LogicAnd {
1560 fn port_spec(&self) -> &PortSpec {
1561 &self.spec
1562 }
1563
1564 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1565 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1566 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1567
1568 outputs.set(10, if a && b { GATE_HIGH_V } else { 0.0 });
1569 }
1570
1571 fn reset(&mut self) {}
1572
1573 fn set_sample_rate(&mut self, _: f64) {}
1574
1575 fn type_id(&self) -> &'static str {
1576 "logic_and"
1577 }
1578}
1579
1580pub struct LogicOr {
1584 spec: PortSpec,
1585}
1586
1587impl LogicOr {
1588 pub fn new() -> Self {
1589 Self {
1590 spec: PortSpec {
1591 inputs: vec![
1592 PortDef::new(0, "a", SignalKind::Gate),
1593 PortDef::new(1, "b", SignalKind::Gate),
1594 ],
1595 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1596 },
1597 }
1598 }
1599}
1600
1601impl Default for LogicOr {
1602 fn default() -> Self {
1603 Self::new()
1604 }
1605}
1606
1607impl GraphModule for LogicOr {
1608 fn port_spec(&self) -> &PortSpec {
1609 &self.spec
1610 }
1611
1612 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1613 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1614 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1615
1616 outputs.set(10, if a || b { GATE_HIGH_V } else { 0.0 });
1617 }
1618
1619 fn reset(&mut self) {}
1620
1621 fn set_sample_rate(&mut self, _: f64) {}
1622
1623 fn type_id(&self) -> &'static str {
1624 "logic_or"
1625 }
1626}
1627
1628pub struct LogicXor {
1632 spec: PortSpec,
1633}
1634
1635impl LogicXor {
1636 pub fn new() -> Self {
1637 Self {
1638 spec: PortSpec {
1639 inputs: vec![
1640 PortDef::new(0, "a", SignalKind::Gate),
1641 PortDef::new(1, "b", SignalKind::Gate),
1642 ],
1643 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1644 },
1645 }
1646 }
1647}
1648
1649impl Default for LogicXor {
1650 fn default() -> Self {
1651 Self::new()
1652 }
1653}
1654
1655impl GraphModule for LogicXor {
1656 fn port_spec(&self) -> &PortSpec {
1657 &self.spec
1658 }
1659
1660 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1661 let a = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1662 let b = inputs.get_or(1, 0.0) > GATE_THRESHOLD_V;
1663
1664 outputs.set(10, if a ^ b { GATE_HIGH_V } else { 0.0 });
1665 }
1666
1667 fn reset(&mut self) {}
1668
1669 fn set_sample_rate(&mut self, _: f64) {}
1670
1671 fn type_id(&self) -> &'static str {
1672 "logic_xor"
1673 }
1674}
1675
1676pub struct LogicNot {
1680 spec: PortSpec,
1681}
1682
1683impl LogicNot {
1684 pub fn new() -> Self {
1685 Self {
1686 spec: PortSpec {
1687 inputs: vec![PortDef::new(0, "in", SignalKind::Gate)],
1688 outputs: vec![PortDef::new(10, "out", SignalKind::Gate)],
1689 },
1690 }
1691 }
1692}
1693
1694impl Default for LogicNot {
1695 fn default() -> Self {
1696 Self::new()
1697 }
1698}
1699
1700impl GraphModule for LogicNot {
1701 fn port_spec(&self) -> &PortSpec {
1702 &self.spec
1703 }
1704
1705 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1706 let input = inputs.get_or(0, 0.0) > GATE_THRESHOLD_V;
1707 outputs.set(10, if input { 0.0 } else { GATE_HIGH_V });
1708 }
1709
1710 fn reset(&mut self) {}
1711
1712 fn set_sample_rate(&mut self, _: f64) {}
1713
1714 fn type_id(&self) -> &'static str {
1715 "logic_not"
1716 }
1717}
1718
1719pub struct Comparator {
1725 state: i8,
1729 spec: PortSpec,
1730}
1731
1732impl Comparator {
1733 const DEADBAND_V: f64 = 0.01;
1735 const HYSTERESIS_V: f64 = 0.02;
1738
1739 pub fn new() -> Self {
1740 Self {
1741 state: 0,
1742 spec: PortSpec {
1743 inputs: vec![
1744 PortDef::new(0, "a", SignalKind::CvBipolar),
1745 PortDef::new(1, "b", SignalKind::CvBipolar),
1746 ],
1747 outputs: vec![
1748 PortDef::new(10, "gt", SignalKind::Gate), PortDef::new(11, "lt", SignalKind::Gate), PortDef::new(12, "eq", SignalKind::Gate), ],
1752 },
1753 }
1754 }
1755}
1756
1757impl Default for Comparator {
1758 fn default() -> Self {
1759 Self::new()
1760 }
1761}
1762
1763impl GraphModule for Comparator {
1764 fn port_spec(&self) -> &PortSpec {
1765 &self.spec
1766 }
1767
1768 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1769 let a = inputs.get_or(0, 0.0);
1770 let b = inputs.get_or(1, 0.0);
1771 let d = a - b;
1772
1773 let t = Self::DEADBAND_V;
1774 let hy = Self::HYSTERESIS_V;
1775
1776 let mut gt = self.state == 1;
1781 let mut lt = self.state == -1;
1782
1783 if gt {
1784 if d < t {
1785 gt = false;
1786 }
1787 } else if d >= t + hy {
1788 gt = true;
1789 }
1790
1791 if lt {
1792 if d > -t {
1793 lt = false;
1794 }
1795 } else if d <= -t - hy {
1796 lt = true;
1797 }
1798
1799 self.state = if gt {
1802 1
1803 } else if lt {
1804 -1
1805 } else {
1806 0
1807 };
1808
1809 outputs.set(10, if gt { GATE_HIGH_V } else { 0.0 });
1810 outputs.set(11, if lt { GATE_HIGH_V } else { 0.0 });
1811 outputs.set(12, if self.state == 0 { GATE_HIGH_V } else { 0.0 });
1812 }
1813
1814 fn reset(&mut self) {
1815 self.state = 0;
1816 }
1817
1818 fn set_sample_rate(&mut self, _: f64) {}
1819
1820 fn type_id(&self) -> &'static str {
1821 "comparator"
1822 }
1823}
1824
1825pub struct Rectifier {
1830 spec: PortSpec,
1831}
1832
1833impl Rectifier {
1834 pub fn new() -> Self {
1835 Self {
1836 spec: PortSpec {
1837 inputs: vec![PortDef::new(0, "in", SignalKind::Audio)],
1838 outputs: vec![
1839 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), ],
1844 },
1845 }
1846 }
1847}
1848
1849impl Default for Rectifier {
1850 fn default() -> Self {
1851 Self::new()
1852 }
1853}
1854
1855impl GraphModule for Rectifier {
1856 fn port_spec(&self) -> &PortSpec {
1857 &self.spec
1858 }
1859
1860 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1861 let input = inputs.get_or(0, 0.0);
1862
1863 outputs.set(10, Libm::<f64>::fabs(input));
1865
1866 outputs.set(11, Libm::<f64>::fmax(input, 0.0));
1868
1869 outputs.set(12, Libm::<f64>::fmax(-input, 0.0));
1871
1872 outputs.set(13, Libm::<f64>::fabs(input) * 2.0);
1874 }
1875
1876 fn reset(&mut self) {}
1877
1878 fn set_sample_rate(&mut self, _: f64) {}
1879
1880 fn type_id(&self) -> &'static str {
1881 "rectifier"
1882 }
1883}
1884
1885pub struct PrecisionAdder {
1891 spec: PortSpec,
1892}
1893
1894impl PrecisionAdder {
1895 pub fn new() -> Self {
1896 Self {
1897 spec: PortSpec {
1898 inputs: vec![
1899 PortDef::new(0, "in1", SignalKind::VoltPerOctave),
1900 PortDef::new(1, "in2", SignalKind::VoltPerOctave),
1901 PortDef::new(2, "in3", SignalKind::CvBipolar),
1902 PortDef::new(3, "in4", SignalKind::CvBipolar),
1903 ],
1904 outputs: vec![
1905 PortDef::new(10, "sum", SignalKind::VoltPerOctave),
1906 PortDef::new(11, "inv", SignalKind::VoltPerOctave), ],
1908 },
1909 }
1910 }
1911}
1912
1913impl Default for PrecisionAdder {
1914 fn default() -> Self {
1915 Self::new()
1916 }
1917}
1918
1919impl GraphModule for PrecisionAdder {
1920 fn port_spec(&self) -> &PortSpec {
1921 &self.spec
1922 }
1923
1924 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1925 let sum = inputs.get_or(0, 0.0)
1926 + inputs.get_or(1, 0.0)
1927 + inputs.get_or(2, 0.0)
1928 + inputs.get_or(3, 0.0);
1929
1930 outputs.set(10, sum);
1931 outputs.set(11, -sum);
1932 }
1933
1934 fn reset(&mut self) {}
1935
1936 fn set_sample_rate(&mut self, _: f64) {}
1937
1938 fn type_id(&self) -> &'static str {
1939 "precision_adder"
1940 }
1941}
1942
1943pub struct VcSwitch {
1949 spec: PortSpec,
1950}
1951
1952impl VcSwitch {
1953 pub fn new() -> Self {
1954 Self {
1955 spec: PortSpec {
1956 inputs: vec![
1957 PortDef::new(0, "a", SignalKind::Audio),
1958 PortDef::new(1, "b", SignalKind::Audio),
1959 PortDef::new(2, "cv", SignalKind::Gate).with_default(0.0),
1960 ],
1961 outputs: vec![
1962 PortDef::new(10, "out", SignalKind::Audio), PortDef::new(11, "a_out", SignalKind::Audio), PortDef::new(12, "b_out", SignalKind::Audio), ],
1966 },
1967 }
1968 }
1969}
1970
1971impl Default for VcSwitch {
1972 fn default() -> Self {
1973 Self::new()
1974 }
1975}
1976
1977impl GraphModule for VcSwitch {
1978 fn port_spec(&self) -> &PortSpec {
1979 &self.spec
1980 }
1981
1982 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1983 let a = inputs.get_or(0, 0.0);
1984 let b = inputs.get_or(1, 0.0);
1985 let cv = inputs.get_or(2, 0.0);
1986
1987 let select_b = cv > GATE_THRESHOLD_V;
1988
1989 if select_b {
1990 outputs.set(10, b);
1991 outputs.set(11, 0.0);
1992 outputs.set(12, b);
1993 } else {
1994 outputs.set(10, a);
1995 outputs.set(11, a);
1996 outputs.set(12, 0.0);
1997 }
1998 }
1999
2000 fn reset(&mut self) {}
2001
2002 fn set_sample_rate(&mut self, _: f64) {}
2003
2004 fn type_id(&self) -> &'static str {
2005 "vc_switch"
2006 }
2007}
2008
2009pub struct BernoulliGate {
2015 prev_trigger: f64,
2016 gate_a: f64,
2019 gate_b: f64,
2021 spec: PortSpec,
2022}
2023
2024impl BernoulliGate {
2025 pub fn new() -> Self {
2026 Self {
2027 prev_trigger: 0.0,
2028 gate_a: 0.0,
2029 gate_b: 0.0,
2030 spec: PortSpec {
2031 inputs: vec![
2032 PortDef::new(0, "trig", SignalKind::Trigger),
2033 PortDef::new(1, "prob", SignalKind::CvUnipolar).with_default(5.0), ],
2035 outputs: vec![
2036 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), ],
2041 },
2042 }
2043 }
2044}
2045
2046impl Default for BernoulliGate {
2047 fn default() -> Self {
2048 Self::new()
2049 }
2050}
2051
2052impl GraphModule for BernoulliGate {
2053 fn port_spec(&self) -> &PortSpec {
2054 &self.spec
2055 }
2056
2057 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2058 let trigger = inputs.get_or(0, 0.0);
2059 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;
2062 self.prev_trigger = trigger;
2063
2064 let mut trig_a = 0.0;
2066 let mut trig_b = 0.0;
2067
2068 if rising_edge {
2069 let rand_val: f64 = rng::random();
2071 if rand_val < prob {
2072 trig_a = GATE_HIGH_V;
2073 } else {
2074 trig_b = GATE_HIGH_V;
2075 }
2076 }
2077
2078 outputs.set(10, trig_a);
2080 outputs.set(11, trig_b);
2081
2082 if trig_a > 0.0 {
2086 self.gate_a = GATE_HIGH_V;
2087 self.gate_b = 0.0;
2088 } else if trig_b > 0.0 {
2089 self.gate_a = 0.0;
2090 self.gate_b = GATE_HIGH_V;
2091 }
2092
2093 outputs.set(12, self.gate_a);
2094 outputs.set(13, self.gate_b);
2095 }
2096
2097 fn reset(&mut self) {
2098 self.prev_trigger = 0.0;
2099 self.gate_a = 0.0;
2100 self.gate_b = 0.0;
2101 }
2102
2103 fn set_sample_rate(&mut self, _: f64) {}
2104
2105 fn type_id(&self) -> &'static str {
2106 "bernoulli_gate"
2107 }
2108}
2109
2110pub struct Min {
2114 spec: PortSpec,
2115}
2116
2117impl Min {
2118 pub fn new() -> Self {
2119 Self {
2120 spec: PortSpec {
2121 inputs: vec![
2122 PortDef::new(0, "a", SignalKind::CvBipolar),
2123 PortDef::new(1, "b", SignalKind::CvBipolar),
2124 ],
2125 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
2126 },
2127 }
2128 }
2129}
2130
2131impl Default for Min {
2132 fn default() -> Self {
2133 Self::new()
2134 }
2135}
2136
2137impl GraphModule for Min {
2138 fn port_spec(&self) -> &PortSpec {
2139 &self.spec
2140 }
2141
2142 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2143 let a = inputs.get_or(0, 0.0);
2144 let b = inputs.get_or(1, 0.0);
2145 outputs.set(10, Libm::<f64>::fmin(a, b));
2146 }
2147
2148 fn reset(&mut self) {}
2149
2150 fn set_sample_rate(&mut self, _: f64) {}
2151
2152 fn type_id(&self) -> &'static str {
2153 "min"
2154 }
2155}
2156
2157pub struct Max {
2161 spec: PortSpec,
2162}
2163
2164impl Max {
2165 pub fn new() -> Self {
2166 Self {
2167 spec: PortSpec {
2168 inputs: vec![
2169 PortDef::new(0, "a", SignalKind::CvBipolar),
2170 PortDef::new(1, "b", SignalKind::CvBipolar),
2171 ],
2172 outputs: vec![PortDef::new(10, "out", SignalKind::CvBipolar)],
2173 },
2174 }
2175 }
2176}
2177
2178impl Default for Max {
2179 fn default() -> Self {
2180 Self::new()
2181 }
2182}
2183
2184impl GraphModule for Max {
2185 fn port_spec(&self) -> &PortSpec {
2186 &self.spec
2187 }
2188
2189 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2190 let a = inputs.get_or(0, 0.0);
2191 let b = inputs.get_or(1, 0.0);
2192 outputs.set(10, Libm::<f64>::fmax(a, b));
2193 }
2194
2195 fn reset(&mut self) {}
2196
2197 fn set_sample_rate(&mut self, _: f64) {}
2198
2199 fn type_id(&self) -> &'static str {
2200 "max"
2201 }
2202}
2203
2204#[derive(Debug, Clone, Copy, PartialEq)]
2210pub enum ChordType {
2211 Major,
2212 Minor,
2213 Seventh,
2214 MajorSeventh,
2215 MinorSeventh,
2216 Diminished,
2217 Augmented,
2218 Sus2,
2219 Sus4,
2220}
2221
2222impl ChordType {
2223 fn intervals(&self) -> &'static [i32] {
2225 match self {
2226 ChordType::Major => &[0, 4, 7],
2227 ChordType::Minor => &[0, 3, 7],
2228 ChordType::Seventh => &[0, 4, 7, 10],
2229 ChordType::MajorSeventh => &[0, 4, 7, 11],
2230 ChordType::MinorSeventh => &[0, 3, 7, 10],
2231 ChordType::Diminished => &[0, 3, 6],
2232 ChordType::Augmented => &[0, 4, 8],
2233 ChordType::Sus2 => &[0, 2, 7],
2234 ChordType::Sus4 => &[0, 5, 7],
2235 }
2236 }
2237
2238 fn from_cv(cv: f64) -> Self {
2240 match (cv * 8.99) as u8 {
2241 0 => ChordType::Major,
2242 1 => ChordType::Minor,
2243 2 => ChordType::Seventh,
2244 3 => ChordType::MajorSeventh,
2245 4 => ChordType::MinorSeventh,
2246 5 => ChordType::Diminished,
2247 6 => ChordType::Augmented,
2248 7 => ChordType::Sus2,
2249 _ => ChordType::Sus4,
2250 }
2251 }
2252}
2253
2254pub struct ChordMemory {
2266 spec: PortSpec,
2267}
2268
2269impl ChordMemory {
2270 pub fn new() -> Self {
2271 Self {
2272 spec: PortSpec {
2273 inputs: vec![
2274 PortDef::new(0, "root", SignalKind::VoltPerOctave),
2275 PortDef::new(1, "chord", SignalKind::CvUnipolar)
2276 .with_default(0.0)
2277 .with_attenuverter(),
2278 PortDef::new(2, "inversion", SignalKind::CvUnipolar)
2279 .with_default(0.0)
2280 .with_attenuverter(),
2281 PortDef::new(3, "spread", SignalKind::CvUnipolar)
2282 .with_default(0.0)
2283 .with_attenuverter(),
2284 ],
2285 outputs: vec![
2286 PortDef::new(10, "voice1", SignalKind::VoltPerOctave),
2287 PortDef::new(11, "voice2", SignalKind::VoltPerOctave),
2288 PortDef::new(12, "voice3", SignalKind::VoltPerOctave),
2289 PortDef::new(13, "voice4", SignalKind::VoltPerOctave),
2290 ],
2291 },
2292 }
2293 }
2294}
2295
2296impl Default for ChordMemory {
2297 fn default() -> Self {
2298 Self::new()
2299 }
2300}
2301
2302impl GraphModule for ChordMemory {
2303 fn port_spec(&self) -> &PortSpec {
2304 &self.spec
2305 }
2306
2307 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2308 let root = inputs.get_or(0, 0.0);
2309 let chord_cv = inputs.get_or(1, 0.0).clamp(0.0, 1.0);
2310 let inversion_cv = inputs.get_or(2, 0.0).clamp(0.0, 1.0);
2311 let spread = inputs.get_or(3, 0.0).clamp(0.0, 1.0);
2312
2313 let chord_type = ChordType::from_cv(chord_cv);
2314 let intervals = chord_type.intervals();
2315 let num_notes = intervals.len();
2316
2317 let inversion = ((inversion_cv * num_notes as f64) as usize) % num_notes;
2319
2320 let mut voices = [0.0f64; 4];
2322 for (i, voice) in voices.iter_mut().enumerate() {
2323 if i < num_notes {
2324 let interval_idx = (i + inversion) % num_notes;
2325 let semitones = intervals[interval_idx];
2326
2327 let octave_offset = if i + inversion >= num_notes { 1.0 } else { 0.0 };
2329
2330 let spread_offset = spread * (i as f64 / 3.0);
2332
2333 *voice = root + semitones as f64 / 12.0 + octave_offset + spread_offset;
2335 } else {
2336 let spread_offset = spread * (i as f64 / 3.0);
2339 *voice = root + 1.0 + spread_offset;
2340 }
2341 }
2342
2343 outputs.set(10, voices[0]);
2344 outputs.set(11, voices[1]);
2345 outputs.set(12, voices[2]);
2346 outputs.set(13, voices[3]);
2347 }
2348
2349 fn reset(&mut self) {}
2350
2351 fn set_sample_rate(&mut self, _: f64) {}
2352
2353 fn type_id(&self) -> &'static str {
2354 "chord_memory"
2355 }
2356}
2357
2358#[derive(Debug, Clone, Copy, PartialEq)]
2364pub enum ArpPattern {
2365 Up,
2367 Down,
2369 UpDown,
2371 Random,
2373}
2374
2375impl ArpPattern {
2376 fn from_cv(cv: f64) -> Self {
2378 let cv = cv.clamp(0.0, 1.0);
2379 if cv < 0.25 {
2380 ArpPattern::Up
2381 } else if cv < 0.5 {
2382 ArpPattern::Down
2383 } else if cv < 0.75 {
2384 ArpPattern::UpDown
2385 } else {
2386 ArpPattern::Random
2387 }
2388 }
2389}
2390
2391pub struct Arpeggiator {
2407 held_notes: [f64; 8],
2409 num_notes: usize,
2411 current_step: usize,
2413 direction_up: bool,
2415 prev_gate: f64,
2417 captured_note: Option<f64>,
2420 prev_clock: f64,
2422 prev_reset: f64,
2424 rng: crate::rng::Rng,
2426 gate_out: f64,
2428 trigger_countdown: usize,
2430 sample_rate: f64,
2431 spec: PortSpec,
2432}
2433
2434impl Arpeggiator {
2435 const TRIGGER_MS: f64 = 1.0;
2437
2438 pub fn new(sample_rate: f64) -> Self {
2439 let spec = PortSpec {
2440 inputs: vec![
2441 PortDef::new(0, "v_oct", SignalKind::VoltPerOctave).with_default(0.0),
2442 PortDef::new(1, "gate", SignalKind::Gate).with_default(0.0),
2443 PortDef::new(2, "clock", SignalKind::Clock).with_default(0.0),
2444 PortDef::new(3, "pattern", SignalKind::CvUnipolar).with_default(0.0),
2445 PortDef::new(4, "octaves", SignalKind::CvUnipolar).with_default(0.0),
2446 PortDef::new(5, "reset", SignalKind::Gate).with_default(0.0),
2447 ],
2448 outputs: vec![
2449 PortDef::new(10, "v_oct_out", SignalKind::VoltPerOctave),
2450 PortDef::new(11, "gate_out", SignalKind::Gate),
2451 PortDef::new(12, "trigger", SignalKind::Trigger),
2452 ],
2453 };
2454
2455 Self {
2456 held_notes: [0.0; 8],
2457 num_notes: 0,
2458 current_step: 0,
2459 direction_up: true,
2460 prev_gate: 0.0,
2461 captured_note: None,
2462 prev_clock: 0.0,
2463 prev_reset: 0.0,
2464 rng: crate::rng::Rng::from_seed(42),
2465 gate_out: 0.0,
2466 trigger_countdown: 0,
2467 sample_rate,
2468 spec,
2469 }
2470 }
2471
2472 fn add_note(&mut self, note: f64) {
2474 if self.num_notes >= 8 {
2475 return;
2476 }
2477
2478 let mut insert_pos = self.num_notes;
2480 for i in 0..self.num_notes {
2481 if note < self.held_notes[i] {
2482 insert_pos = i;
2483 break;
2484 }
2485 }
2486
2487 for i in (insert_pos..self.num_notes).rev() {
2489 self.held_notes[i + 1] = self.held_notes[i];
2490 }
2491
2492 self.held_notes[insert_pos] = note;
2493 self.num_notes += 1;
2494 }
2495
2496 pub fn remove_note(&mut self, note: f64) {
2498 let mut found_idx = None;
2500 for i in 0..self.num_notes {
2501 if (self.held_notes[i] - note).abs() < 0.001 {
2502 found_idx = Some(i);
2503 break;
2504 }
2505 }
2506
2507 if let Some(idx) = found_idx {
2508 for i in idx..self.num_notes - 1 {
2510 self.held_notes[i] = self.held_notes[i + 1];
2511 }
2512 self.num_notes -= 1;
2513 }
2514 }
2515
2516 fn get_current_note(&mut self, pattern: ArpPattern, octaves: usize) -> f64 {
2518 if self.num_notes == 0 {
2519 return 0.0;
2520 }
2521
2522 let total_steps = self.num_notes * octaves;
2523 let step = self.current_step % total_steps;
2524
2525 let note_idx = match pattern {
2526 ArpPattern::Up => step % self.num_notes,
2527 ArpPattern::Down => (self.num_notes - 1) - (step % self.num_notes),
2528 ArpPattern::UpDown => {
2529 let cycle_len = if self.num_notes > 1 {
2531 (self.num_notes - 1) * 2
2532 } else {
2533 1
2534 };
2535 let pos = step % cycle_len;
2536 if pos < self.num_notes {
2537 pos
2538 } else {
2539 (self.num_notes - 1) * 2 - pos
2540 }
2541 }
2542 ArpPattern::Random => (self.rng.next_u64() as usize) % self.num_notes,
2543 };
2544
2545 let octave = step / self.num_notes;
2546 let base_note = self.held_notes[note_idx % self.num_notes];
2547
2548 base_note + octave as f64 }
2550}
2551
2552impl Default for Arpeggiator {
2553 fn default() -> Self {
2554 Self::new(44100.0)
2555 }
2556}
2557
2558impl GraphModule for Arpeggiator {
2559 fn port_spec(&self) -> &PortSpec {
2560 &self.spec
2561 }
2562
2563 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
2564 let v_oct = inputs.get_or(0, 0.0);
2565 let gate = inputs.get_or(1, 0.0);
2566 let clock = inputs.get_or(2, 0.0);
2567 let pattern_cv = inputs.get_or(3, 0.0);
2568 let octaves_cv = inputs.get_or(4, 0.0);
2569 let reset = inputs.get_or(5, 0.0);
2570
2571 let pattern = ArpPattern::from_cv(pattern_cv);
2572 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 {
2579 self.add_note(v_oct);
2581 self.captured_note = Some(v_oct);
2582 } else if gate <= GATE_THRESHOLD_V && self.prev_gate > GATE_THRESHOLD_V {
2583 if let Some(note) = self.captured_note.take() {
2585 self.remove_note(note);
2586 }
2587 }
2588 self.prev_gate = gate;
2589
2590 if reset > GATE_THRESHOLD_V && self.prev_reset <= GATE_THRESHOLD_V {
2592 self.current_step = 0;
2593 self.direction_up = true;
2594 self.held_notes = [0.0; 8];
2595 self.num_notes = 0;
2596 self.captured_note = None;
2597 }
2598 self.prev_reset = reset;
2599
2600 let mut trigger_out = 0.0;
2602 let clock_rising =
2603 clock > GATE_THRESHOLD_V && self.prev_clock <= GATE_THRESHOLD_V && self.num_notes > 0;
2604
2605 if clock_rising {
2606 self.gate_out = GATE_HIGH_V;
2607 self.trigger_countdown = (Self::TRIGGER_MS * self.sample_rate / 1000.0) as usize;
2609 trigger_out = GATE_HIGH_V;
2610 }
2611 self.prev_clock = clock;
2612
2613 if self.trigger_countdown > 0 {
2615 self.trigger_countdown -= 1;
2616 trigger_out = GATE_HIGH_V;
2617 }
2618
2619 if clock <= GATE_THRESHOLD_V {
2621 self.gate_out = 0.0;
2622 }
2623
2624 let v_oct_out = if self.num_notes > 0 {
2626 self.get_current_note(pattern, octaves)
2627 } else {
2628 0.0
2629 };
2630
2631 if clock_rising {
2633 self.current_step += 1;
2634 }
2635
2636 outputs.set(10, v_oct_out);
2637 outputs.set(
2638 11,
2639 if self.num_notes > 0 {
2640 self.gate_out
2641 } else {
2642 0.0
2643 },
2644 );
2645 outputs.set(12, trigger_out);
2646 }
2647
2648 fn reset(&mut self) {
2649 self.held_notes = [0.0; 8];
2650 self.num_notes = 0;
2651 self.captured_note = None;
2652 self.current_step = 0;
2653 self.direction_up = true;
2654 self.prev_gate = 0.0;
2655 self.prev_clock = 0.0;
2656 self.prev_reset = 0.0;
2657 self.gate_out = 0.0;
2658 self.trigger_countdown = 0;
2659 }
2660
2661 fn set_sample_rate(&mut self, sample_rate: f64) {
2662 self.sample_rate = sample_rate;
2663 }
2664
2665 fn type_id(&self) -> &'static str {
2666 "arpeggiator"
2667 }
2668}
2669
2670#[cfg(test)]
2675mod tests {
2676 use super::*;
2677 use crate::modules::common::SAFE_AUDIO_LIMIT;
2678
2679 #[test]
2680 fn test_mixer() {
2681 let mut mixer = Mixer::new(4);
2682 let mut inputs = PortValues::new();
2683 let mut outputs = PortValues::new();
2684
2685 inputs.set(0, 1.0);
2686 inputs.set(1, 2.0);
2687 inputs.set(2, 3.0);
2688 inputs.set(3, 4.0);
2689
2690 mixer.tick(&inputs, &mut outputs);
2691
2692 let out = outputs.get(100).unwrap();
2693 assert!((out - 10.0).abs() < 0.01);
2694 }
2695 #[test]
2696 fn test_step_sequencer() {
2697 let mut seq = StepSequencer::new();
2698 seq.set_step(0, 0.0, true);
2699 seq.set_step(1, 0.5, true);
2700 seq.set_step(2, 1.0, true);
2701
2702 let mut inputs = PortValues::new();
2703 let mut outputs = PortValues::new();
2704
2705 seq.tick(&inputs, &mut outputs);
2707 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2708
2709 inputs.set(0, 5.0);
2711 seq.tick(&inputs, &mut outputs);
2712 assert!((outputs.get(10).unwrap() - 0.5).abs() < 0.01);
2713
2714 inputs.set(0, 0.0);
2716 seq.tick(&inputs, &mut outputs);
2717 inputs.set(0, 5.0);
2718 seq.tick(&inputs, &mut outputs);
2719 assert!((outputs.get(10).unwrap() - 1.0).abs() < 0.01);
2720 }
2721 #[test]
2722 fn test_sample_and_hold() {
2723 let mut sh = SampleAndHold::new();
2724 let mut inputs = PortValues::new();
2725 let mut outputs = PortValues::new();
2726
2727 inputs.set(0, 3.0);
2729 inputs.set(1, 0.0);
2730 sh.tick(&inputs, &mut outputs);
2731 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2733
2734 inputs.set(1, 5.0);
2736 sh.tick(&inputs, &mut outputs);
2737 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
2738
2739 inputs.set(0, 7.0);
2741 sh.tick(&inputs, &mut outputs);
2742 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
2743
2744 inputs.set(1, 0.0);
2746 sh.tick(&inputs, &mut outputs);
2747 inputs.set(1, 5.0);
2748 sh.tick(&inputs, &mut outputs);
2749 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
2750 }
2751 #[test]
2752 fn test_slew_limiter() {
2753 let mut slew = SlewLimiter::new(1000.0); let mut inputs = PortValues::new();
2755 let mut outputs = PortValues::new();
2756
2757 inputs.set(1, 0.5); inputs.set(2, 0.5); inputs.set(0, 5.0);
2763 slew.tick(&inputs, &mut outputs);
2764 let first = outputs.get(10).unwrap();
2765
2766 assert!(first > 0.0);
2768 assert!(first < 5.0);
2769
2770 for _ in 0..100 {
2772 slew.tick(&inputs, &mut outputs);
2773 }
2774 let after_100 = outputs.get(10).unwrap();
2776 assert!(after_100 > first);
2777 }
2778 #[test]
2779 fn test_quantizer_chromatic() {
2780 let mut quant = Quantizer::new(Scale::Chromatic);
2781 let mut inputs = PortValues::new();
2782 let mut outputs = PortValues::new();
2783
2784 inputs.set(0, 0.0); quant.tick(&inputs, &mut outputs);
2787 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2788
2789 inputs.set(0, 0.04); quant.tick(&inputs, &mut outputs);
2792 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.01);
2794
2795 inputs.set(0, 0.07);
2797 quant.tick(&inputs, &mut outputs);
2798 let expected_csharp = 1.0 / 12.0;
2800 assert!((outputs.get(10).unwrap() - expected_csharp).abs() < 0.01);
2801 }
2802 #[test]
2803 fn test_quantizer_major_scale() {
2804 let mut quant = Quantizer::new(Scale::Major);
2805 let mut inputs = PortValues::new();
2806 let mut outputs = PortValues::new();
2807
2808 inputs.set(0, 1.0 / 12.0); quant.tick(&inputs, &mut outputs);
2811 let out = outputs.get(10).unwrap();
2812 assert!(out.abs() < 0.01 || (out - 2.0 / 12.0).abs() < 0.01);
2814 }
2815 #[test]
2816 fn test_clock() {
2817 let mut clock = Clock::new(1000.0); let mut inputs = PortValues::new();
2819 let mut outputs = PortValues::new();
2820
2821 inputs.set(0, 10.0); let mut trigger_count = 0;
2825 let mut last_trigger = 0.0;
2826
2827 for _ in 0..1000 {
2828 clock.tick(&inputs, &mut outputs);
2829 let trigger = outputs.get(10).unwrap(); if trigger > 2.5 && last_trigger <= 2.5 {
2831 trigger_count += 1;
2832 }
2833 last_trigger = trigger;
2834 }
2835
2836 assert!(trigger_count >= 3);
2839 }
2840 #[test]
2841 fn test_attenuverter() {
2842 let mut att = Attenuverter::new();
2843 let mut inputs = PortValues::new();
2844 let mut outputs = PortValues::new();
2845
2846 inputs.set(0, 5.0); inputs.set(1, 5.0); att.tick(&inputs, &mut outputs);
2850 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.1);
2851
2852 inputs.set(1, 2.5);
2854 att.tick(&inputs, &mut outputs);
2855 assert!((outputs.get(10).unwrap() - 2.5).abs() < 0.1);
2856
2857 inputs.set(1, 0.0);
2859 att.tick(&inputs, &mut outputs);
2860 assert!((outputs.get(10).unwrap() - 0.0).abs() < 0.1);
2861 }
2862 #[test]
2863 fn test_multiple() {
2864 let mut mult = Multiple::new();
2865 let mut inputs = PortValues::new();
2866 let mut outputs = PortValues::new();
2867
2868 inputs.set(0, 3.5);
2869 mult.tick(&inputs, &mut outputs);
2870
2871 assert!((outputs.get(10).unwrap() - 3.5).abs() < 0.0001);
2873 assert!((outputs.get(11).unwrap() - 3.5).abs() < 0.0001);
2874 assert!((outputs.get(12).unwrap() - 3.5).abs() < 0.0001);
2875 assert!((outputs.get(13).unwrap() - 3.5).abs() < 0.0001);
2876 }
2877 #[test]
2878 fn test_crossfader() {
2879 let mut xf = Crossfader::new();
2880 let mut inputs = PortValues::new();
2881 let mut outputs = PortValues::new();
2882
2883 inputs.set(0, 5.0); inputs.set(1, -5.0); inputs.set(2, -5.0);
2888 xf.tick(&inputs, &mut outputs);
2889 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.1);
2890
2891 inputs.set(2, 5.0);
2893 xf.tick(&inputs, &mut outputs);
2894 assert!((outputs.get(10).unwrap() - (-5.0)).abs() < 0.1);
2895
2896 inputs.set(2, 0.0);
2898 xf.tick(&inputs, &mut outputs);
2899 let out = outputs.get(10).unwrap();
2901 assert!(out.abs() < 1.0); }
2903 #[test]
2904 fn test_logic_and() {
2905 let mut gate = LogicAnd::new();
2906 let mut inputs = PortValues::new();
2907 let mut outputs = PortValues::new();
2908
2909 inputs.set(0, 0.0);
2911 inputs.set(1, 0.0);
2912 gate.tick(&inputs, &mut outputs);
2913 assert!(outputs.get(10).unwrap() < 2.5);
2914
2915 inputs.set(0, 5.0);
2917 inputs.set(1, 0.0);
2918 gate.tick(&inputs, &mut outputs);
2919 assert!(outputs.get(10).unwrap() < 2.5);
2920
2921 inputs.set(0, 5.0);
2923 inputs.set(1, 5.0);
2924 gate.tick(&inputs, &mut outputs);
2925 assert!(outputs.get(10).unwrap() > 2.5);
2926 }
2927 #[test]
2928 fn test_logic_or() {
2929 let mut gate = LogicOr::new();
2930 let mut inputs = PortValues::new();
2931 let mut outputs = PortValues::new();
2932
2933 inputs.set(0, 0.0);
2935 inputs.set(1, 0.0);
2936 gate.tick(&inputs, &mut outputs);
2937 assert!(outputs.get(10).unwrap() < 2.5);
2938
2939 inputs.set(0, 5.0);
2941 inputs.set(1, 0.0);
2942 gate.tick(&inputs, &mut outputs);
2943 assert!(outputs.get(10).unwrap() > 2.5);
2944
2945 inputs.set(0, 5.0);
2947 inputs.set(1, 5.0);
2948 gate.tick(&inputs, &mut outputs);
2949 assert!(outputs.get(10).unwrap() > 2.5);
2950 }
2951 #[test]
2952 fn test_logic_xor() {
2953 let mut gate = LogicXor::new();
2954 let mut inputs = PortValues::new();
2955 let mut outputs = PortValues::new();
2956
2957 inputs.set(0, 0.0);
2959 inputs.set(1, 0.0);
2960 gate.tick(&inputs, &mut outputs);
2961 assert!(outputs.get(10).unwrap() < 2.5);
2962
2963 inputs.set(0, 5.0);
2965 inputs.set(1, 0.0);
2966 gate.tick(&inputs, &mut outputs);
2967 assert!(outputs.get(10).unwrap() > 2.5);
2968
2969 inputs.set(0, 5.0);
2971 inputs.set(1, 5.0);
2972 gate.tick(&inputs, &mut outputs);
2973 assert!(outputs.get(10).unwrap() < 2.5);
2974 }
2975 #[test]
2976 fn test_logic_not() {
2977 let mut gate = LogicNot::new();
2978 let mut inputs = PortValues::new();
2979 let mut outputs = PortValues::new();
2980
2981 inputs.set(0, 0.0);
2983 gate.tick(&inputs, &mut outputs);
2984 assert!(outputs.get(10).unwrap() > 2.5);
2985
2986 inputs.set(0, 5.0);
2988 gate.tick(&inputs, &mut outputs);
2989 assert!(outputs.get(10).unwrap() < 2.5);
2990 }
2991 #[test]
2992 fn test_comparator() {
2993 let mut cmp = Comparator::new();
2994 let mut inputs = PortValues::new();
2995 let mut outputs = PortValues::new();
2996
2997 inputs.set(0, 3.0);
2999 inputs.set(1, 1.0);
3000 cmp.tick(&inputs, &mut outputs);
3001 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);
3007 inputs.set(1, 3.0);
3008 cmp.tick(&inputs, &mut outputs);
3009 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);
3015 inputs.set(1, 2.0);
3016 cmp.tick(&inputs, &mut outputs);
3017 assert!(outputs.get(10).unwrap() < 2.5); assert!(outputs.get(11).unwrap() < 2.5); assert!(outputs.get(12).unwrap() > 2.5); }
3021 #[test]
3022 fn test_rectifier() {
3023 let mut rect = Rectifier::new();
3024 let mut inputs = PortValues::new();
3025 let mut outputs = PortValues::new();
3026
3027 inputs.set(0, 3.0);
3029 rect.tick(&inputs, &mut outputs);
3030 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);
3036 rect.tick(&inputs, &mut outputs);
3037 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); }
3041 #[test]
3042 fn test_precision_adder() {
3043 let mut adder = PrecisionAdder::new();
3044 let mut inputs = PortValues::new();
3045 let mut outputs = PortValues::new();
3046
3047 inputs.set(0, 1.0);
3048 inputs.set(1, 2.0);
3049 inputs.set(2, 0.5);
3050 inputs.set(3, -0.5);
3051 adder.tick(&inputs, &mut outputs);
3052
3053 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01); assert!((outputs.get(11).unwrap() - (-3.0)).abs() < 0.01); }
3056 #[test]
3057 fn test_vc_switch() {
3058 let mut sw = VcSwitch::new();
3059 let mut inputs = PortValues::new();
3060 let mut outputs = PortValues::new();
3061
3062 inputs.set(0, 3.0); inputs.set(1, 7.0); inputs.set(2, 0.0);
3067 sw.tick(&inputs, &mut outputs);
3068 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
3069 assert!((outputs.get(11).unwrap() - 3.0).abs() < 0.01);
3070 assert!((outputs.get(12).unwrap()).abs() < 0.01);
3071
3072 inputs.set(2, 5.0);
3074 sw.tick(&inputs, &mut outputs);
3075 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
3076 assert!((outputs.get(11).unwrap()).abs() < 0.01);
3077 assert!((outputs.get(12).unwrap() - 7.0).abs() < 0.01);
3078 }
3079 #[test]
3080 fn test_bernoulli_gate() {
3081 let mut bg = BernoulliGate::new();
3082 let mut inputs = PortValues::new();
3083 let mut outputs = PortValues::new();
3084
3085 inputs.set(1, 10.0);
3087
3088 inputs.set(0, 0.0);
3090 bg.tick(&inputs, &mut outputs);
3091 inputs.set(0, 5.0);
3092 bg.tick(&inputs, &mut outputs);
3093
3094 assert!(outputs.get(10).unwrap() > 2.5); assert!(outputs.get(11).unwrap() < 2.5); bg.reset();
3100 inputs.set(1, 0.0);
3101 inputs.set(0, 0.0);
3102 bg.tick(&inputs, &mut outputs);
3103 inputs.set(0, 5.0);
3104 bg.tick(&inputs, &mut outputs);
3105
3106 assert!(outputs.get(10).unwrap() < 2.5); assert!(outputs.get(11).unwrap() > 2.5); }
3110 #[test]
3111 fn test_min() {
3112 let mut m = Min::new();
3113 let mut inputs = PortValues::new();
3114 let mut outputs = PortValues::new();
3115
3116 inputs.set(0, 3.0);
3117 inputs.set(1, 5.0);
3118 m.tick(&inputs, &mut outputs);
3119 assert!((outputs.get(10).unwrap() - 3.0).abs() < 0.01);
3120
3121 inputs.set(0, 7.0);
3122 inputs.set(1, 2.0);
3123 m.tick(&inputs, &mut outputs);
3124 assert!((outputs.get(10).unwrap() - 2.0).abs() < 0.01);
3125 }
3126 #[test]
3127 fn test_max() {
3128 let mut m = Max::new();
3129 let mut inputs = PortValues::new();
3130 let mut outputs = PortValues::new();
3131
3132 inputs.set(0, 3.0);
3133 inputs.set(1, 5.0);
3134 m.tick(&inputs, &mut outputs);
3135 assert!((outputs.get(10).unwrap() - 5.0).abs() < 0.01);
3136
3137 inputs.set(0, 7.0);
3138 inputs.set(1, 2.0);
3139 m.tick(&inputs, &mut outputs);
3140 assert!((outputs.get(10).unwrap() - 7.0).abs() < 0.01);
3141 }
3142 #[test]
3143 fn test_mixer_default_reset_sample_rate() {
3144 let mut mixer = Mixer::default();
3145 mixer.reset();
3146 mixer.set_sample_rate(48000.0);
3147 assert_eq!(mixer.type_id(), "mixer");
3148 }
3149 #[test]
3150 fn test_stereo_output_default_reset_sample_rate() {
3151 let mut stereo = StereoOutput::default();
3152 stereo.reset();
3153 stereo.set_sample_rate(48000.0);
3154 assert_eq!(stereo.type_id(), "stereo_output");
3155 }
3156 #[test]
3157 fn test_offset_default_reset_sample_rate() {
3158 let mut offset = Offset::default();
3159 offset.reset();
3160 offset.set_sample_rate(48000.0);
3161 assert_eq!(offset.type_id(), "offset");
3162 }
3163 #[test]
3164 fn test_scale_enum_semitones() {
3165 let scale = Scale::Chromatic;
3166 assert!(scale.semitones().len() == 12);
3167
3168 let scale = Scale::Major;
3169 assert!(scale.semitones().len() == 7);
3170
3171 let scale = Scale::PentatonicMajor;
3172 assert!(scale.semitones().len() == 5);
3173 }
3174 #[test]
3175 fn test_step_sequencer_default_reset_sample_rate() {
3176 let mut seq = StepSequencer::default();
3177 seq.set_step(0, 1.0, true);
3178 let mut inputs = PortValues::new();
3179 let mut outputs = PortValues::new();
3180 inputs.set(0, 5.0);
3181 seq.tick(&inputs, &mut outputs);
3182
3183 seq.reset();
3184 assert!(seq.current == 0);
3185 assert!(seq.prev_clock == 0.0);
3186
3187 seq.set_sample_rate(48000.0);
3188 assert_eq!(seq.type_id(), "step_sequencer");
3189 }
3190 #[test]
3191 fn test_sample_and_hold_default_reset_sample_rate() {
3192 let mut sh = SampleAndHold::default();
3193 let mut inputs = PortValues::new();
3194 let mut outputs = PortValues::new();
3195 inputs.set(0, 5.0);
3196 inputs.set(1, 5.0);
3197 sh.tick(&inputs, &mut outputs);
3198
3199 sh.reset();
3200 assert!(sh.held_value == 0.0);
3201
3202 sh.set_sample_rate(48000.0);
3203 assert_eq!(sh.type_id(), "sample_hold");
3204 }
3205 #[test]
3206 fn test_slew_limiter_default_reset_sample_rate() {
3207 let mut slew = SlewLimiter::default();
3208 assert!(slew.sample_rate == 44100.0);
3209
3210 let mut inputs = PortValues::new();
3211 let mut outputs = PortValues::new();
3212 inputs.set(0, 5.0);
3213 slew.tick(&inputs, &mut outputs);
3214
3215 slew.reset();
3216 assert!(slew.current == 0.0);
3217
3218 slew.set_sample_rate(48000.0);
3219 assert!(slew.sample_rate == 48000.0);
3220
3221 assert_eq!(slew.type_id(), "slew_limiter");
3222 }
3223 #[test]
3224 fn test_quantizer_default_reset_sample_rate() {
3225 let mut quant = Quantizer::default();
3226 quant.reset();
3227 quant.set_sample_rate(48000.0);
3228 assert_eq!(quant.type_id(), "quantizer");
3229 }
3230 #[test]
3231 fn test_clock_default_reset_sample_rate() {
3232 let mut clock = Clock::default();
3233 assert!(clock.sample_rate == 44100.0);
3234
3235 let inputs = PortValues::new();
3236 let mut outputs = PortValues::new();
3237 for _ in 0..100 {
3238 clock.tick(&inputs, &mut outputs);
3239 }
3240
3241 clock.reset();
3242 assert!(clock.phase == 0.0);
3243
3244 clock.set_sample_rate(48000.0);
3245 assert!(clock.sample_rate == 48000.0);
3246
3247 assert_eq!(clock.type_id(), "clock");
3248 }
3249 #[test]
3250 fn test_attenuverter_default_reset_sample_rate() {
3251 let mut att = Attenuverter::default();
3252 att.reset();
3253 att.set_sample_rate(48000.0);
3254 assert_eq!(att.type_id(), "attenuverter");
3255 }
3256 #[test]
3257 fn test_multiple_default_reset_sample_rate() {
3258 let mut mult = Multiple::default();
3259 mult.reset();
3260 mult.set_sample_rate(48000.0);
3261 assert_eq!(mult.type_id(), "multiple");
3262 }
3263 #[test]
3264 fn test_crossfader_default_reset_sample_rate() {
3265 let mut xf = Crossfader::default();
3266 xf.reset();
3267 xf.set_sample_rate(48000.0);
3268 assert_eq!(xf.type_id(), "crossfader");
3269 }
3270 #[test]
3271 fn test_logic_and_default_reset_sample_rate() {
3272 let mut gate = LogicAnd::default();
3273 gate.reset();
3274 gate.set_sample_rate(48000.0);
3275 assert_eq!(gate.type_id(), "logic_and");
3276 }
3277 #[test]
3278 fn test_logic_or_default_reset_sample_rate() {
3279 let mut gate = LogicOr::default();
3280 gate.reset();
3281 gate.set_sample_rate(48000.0);
3282 assert_eq!(gate.type_id(), "logic_or");
3283 }
3284 #[test]
3285 fn test_logic_xor_default_reset_sample_rate() {
3286 let mut gate = LogicXor::default();
3287 gate.reset();
3288 gate.set_sample_rate(48000.0);
3289 assert_eq!(gate.type_id(), "logic_xor");
3290 }
3291 #[test]
3292 fn test_logic_not_default_reset_sample_rate() {
3293 let mut gate = LogicNot::default();
3294 gate.reset();
3295 gate.set_sample_rate(48000.0);
3296 assert_eq!(gate.type_id(), "logic_not");
3297 }
3298 #[test]
3299 fn test_comparator_default_reset_sample_rate() {
3300 let mut cmp = Comparator::default();
3301 cmp.reset();
3302 cmp.set_sample_rate(48000.0);
3303 assert_eq!(cmp.type_id(), "comparator");
3304 }
3305 #[test]
3306 fn test_rectifier_default_reset_sample_rate() {
3307 let mut rect = Rectifier::default();
3308 rect.reset();
3309 rect.set_sample_rate(48000.0);
3310 assert_eq!(rect.type_id(), "rectifier");
3311 }
3312 #[test]
3313 fn test_precision_adder_default_reset_sample_rate() {
3314 let mut adder = PrecisionAdder::default();
3315 adder.reset();
3316 adder.set_sample_rate(48000.0);
3317 assert_eq!(adder.type_id(), "precision_adder");
3318 }
3319 #[test]
3320 fn test_vc_switch_default_reset_sample_rate() {
3321 let mut sw = VcSwitch::default();
3322 sw.reset();
3323 sw.set_sample_rate(48000.0);
3324 assert_eq!(sw.type_id(), "vc_switch");
3325 }
3326 #[test]
3327 fn test_bernoulli_gate_default_reset_sample_rate() {
3328 let mut bg = BernoulliGate::default();
3329 let mut inputs = PortValues::new();
3330 let mut outputs = PortValues::new();
3331 inputs.set(0, 5.0);
3332 bg.tick(&inputs, &mut outputs);
3333
3334 bg.reset();
3335 assert!(bg.prev_trigger == 0.0);
3336
3337 bg.set_sample_rate(48000.0);
3338 assert_eq!(bg.type_id(), "bernoulli_gate");
3339 }
3340 #[test]
3341 fn test_min_default_reset_sample_rate() {
3342 let mut m = Min::default();
3343 m.reset();
3344 m.set_sample_rate(48000.0);
3345 assert_eq!(m.type_id(), "min");
3346 }
3347 #[test]
3348 fn test_max_default_reset_sample_rate() {
3349 let mut m = Max::default();
3350 m.reset();
3351 m.set_sample_rate(48000.0);
3352 assert_eq!(m.type_id(), "max");
3353 }
3354 #[test]
3355 fn test_step_sequencer_skip_disabled() {
3356 let mut seq = StepSequencer::new();
3357 seq.set_step(0, 1.0, true);
3358 seq.set_step(1, 2.0, false); seq.set_step(2, 3.0, true);
3360
3361 let mut inputs = PortValues::new();
3362 let mut outputs = PortValues::new();
3363
3364 seq.tick(&inputs, &mut outputs);
3366 let _out = outputs.get(10).unwrap_or(0.0);
3367
3368 inputs.set(0, 5.0);
3370 seq.tick(&inputs, &mut outputs);
3371 }
3372 #[test]
3373 fn test_quantizer_pentatonic_scale() {
3374 let mut quant = Quantizer::new(Scale::PentatonicMajor);
3375 let mut inputs = PortValues::new();
3376 let mut outputs = PortValues::new();
3377
3378 inputs.set(0, 0.0);
3380 quant.tick(&inputs, &mut outputs);
3381 assert!(outputs.get(10).unwrap().abs() < 0.01);
3382 }
3383 #[test]
3384 fn test_quantizer_blues_scale() {
3385 let mut quant = Quantizer::new(Scale::Blues);
3386 let mut inputs = PortValues::new();
3387 let mut outputs = PortValues::new();
3388
3389 inputs.set(0, 0.0);
3390 quant.tick(&inputs, &mut outputs);
3391 assert!(outputs.get(10).is_some());
3392 }
3393 #[test]
3394 fn test_slew_limiter_falling() {
3395 let mut slew = SlewLimiter::new(1000.0);
3396 let mut inputs = PortValues::new();
3397 let mut outputs = PortValues::new();
3398
3399 inputs.set(0, 5.0);
3401 inputs.set(1, 10.0); inputs.set(2, 0.5); for _ in 0..1000 {
3404 slew.tick(&inputs, &mut outputs);
3405 }
3406
3407 inputs.set(0, 0.0);
3409 slew.tick(&inputs, &mut outputs);
3410 let falling = outputs.get(10).unwrap();
3411 assert!(falling < 5.0);
3412 assert!(falling > 0.0);
3413 }
3414 #[test]
3415 fn test_scale_dorian_and_mixolydian() {
3416 let scale = Scale::Dorian;
3417 assert!(scale.semitones().len() == 7);
3418
3419 let scale = Scale::Mixolydian;
3420 assert!(scale.semitones().len() == 7);
3421 }
3422 #[test]
3423 fn test_clock_subdivisions() {
3424 let mut clock = Clock::new(1000.0);
3425 let mut inputs = PortValues::new();
3426 let mut outputs = PortValues::new();
3427
3428 inputs.set(0, 5.0); for _ in 0..1000 {
3432 clock.tick(&inputs, &mut outputs);
3433 }
3434
3435 assert!(outputs.get(10).is_some()); assert!(outputs.get(11).is_some()); assert!(outputs.get(12).is_some()); }
3440 #[test]
3441 fn test_chord_memory_major() {
3442 let mut cm = ChordMemory::new();
3443 let mut inputs = PortValues::new();
3444 let mut outputs = PortValues::new();
3445
3446 inputs.set(0, 0.0);
3448 inputs.set(1, 0.0); inputs.set(2, 0.0); inputs.set(3, 0.0); cm.tick(&inputs, &mut outputs);
3453
3454 let voice1 = outputs.get(10).unwrap();
3456 let voice2 = outputs.get(11).unwrap();
3457 let voice3 = outputs.get(12).unwrap();
3458 let voice4 = outputs.get(13).unwrap();
3459
3460 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); }
3465 #[test]
3466 fn test_chord_memory_minor() {
3467 let mut cm = ChordMemory::new();
3468 let mut inputs = PortValues::new();
3469 let mut outputs = PortValues::new();
3470
3471 inputs.set(0, 0.0);
3472 inputs.set(1, 0.15); cm.tick(&inputs, &mut outputs);
3475
3476 let voice2 = outputs.get(11).unwrap();
3478 assert!((voice2 - 3.0 / 12.0).abs() < 0.01); }
3480 #[test]
3481 fn test_chord_memory_seventh() {
3482 let mut cm = ChordMemory::new();
3483 let mut inputs = PortValues::new();
3484 let mut outputs = PortValues::new();
3485
3486 inputs.set(0, 0.0);
3487 inputs.set(1, 0.26); cm.tick(&inputs, &mut outputs);
3490
3491 let voice4 = outputs.get(13).unwrap();
3493 assert!((voice4 - 10.0 / 12.0).abs() < 0.01); }
3495 #[test]
3496 fn test_chord_memory_inversion() {
3497 let mut cm = ChordMemory::new();
3498 let mut inputs = PortValues::new();
3499 let mut outputs = PortValues::new();
3500
3501 inputs.set(0, 0.0);
3502 inputs.set(1, 0.0); inputs.set(2, 0.4); cm.tick(&inputs, &mut outputs);
3506
3507 let voice1 = outputs.get(10).unwrap();
3509 let voice2 = outputs.get(11).unwrap();
3510 let voice3 = outputs.get(12).unwrap();
3511
3512 assert!((voice1 - 4.0 / 12.0).abs() < 0.01);
3514 assert!((voice2 - 7.0 / 12.0).abs() < 0.01);
3516 assert!((voice3 - 1.0).abs() < 0.01);
3518 }
3519 #[test]
3520 fn test_chord_memory_spread() {
3521 let mut cm = ChordMemory::new();
3522 let mut inputs = PortValues::new();
3523 let mut outputs = PortValues::new();
3524
3525 inputs.set(0, 0.0);
3526 inputs.set(1, 0.0); inputs.set(2, 0.0); inputs.set(3, 1.0); cm.tick(&inputs, &mut outputs);
3531
3532 let voice1 = outputs.get(10).unwrap();
3533 let voice2 = outputs.get(11).unwrap();
3534 let voice3 = outputs.get(12).unwrap();
3535 let voice4 = outputs.get(13).unwrap();
3536
3537 assert!(voice1 < voice2);
3543 assert!(voice2 < voice3);
3544 assert!(voice3 < voice4);
3545 }
3546 #[test]
3547 fn test_chord_memory_all_chord_types() {
3548 let mut cm = ChordMemory::new();
3549 let mut inputs = PortValues::new();
3550 let mut outputs = PortValues::new();
3551
3552 for i in 0..9 {
3554 let chord_cv = i as f64 / 9.0;
3555 inputs.set(0, 0.0);
3556 inputs.set(1, chord_cv);
3557
3558 cm.tick(&inputs, &mut outputs);
3559
3560 assert!(outputs.get(10).is_some());
3562 assert!(outputs.get(11).is_some());
3563 assert!(outputs.get(12).is_some());
3564 assert!(outputs.get(13).is_some());
3565 }
3566 }
3567 #[test]
3568 fn test_chord_memory_default_reset_sample_rate() {
3569 let mut cm = ChordMemory::default();
3570 cm.reset();
3571 cm.set_sample_rate(48000.0);
3572 assert_eq!(cm.type_id(), "chord_memory");
3573
3574 assert_eq!(cm.port_spec().inputs.len(), 4);
3576 assert_eq!(cm.port_spec().outputs.len(), 4);
3577 }
3578 #[test]
3579 fn test_chord_type_intervals() {
3580 assert_eq!(ChordType::Major.intervals(), &[0, 4, 7]);
3582 assert_eq!(ChordType::Minor.intervals(), &[0, 3, 7]);
3583 assert_eq!(ChordType::Seventh.intervals(), &[0, 4, 7, 10]);
3584 assert_eq!(ChordType::MajorSeventh.intervals(), &[0, 4, 7, 11]);
3585 assert_eq!(ChordType::MinorSeventh.intervals(), &[0, 3, 7, 10]);
3586 assert_eq!(ChordType::Diminished.intervals(), &[0, 3, 6]);
3587 assert_eq!(ChordType::Augmented.intervals(), &[0, 4, 8]);
3588 assert_eq!(ChordType::Sus2.intervals(), &[0, 2, 7]);
3589 assert_eq!(ChordType::Sus4.intervals(), &[0, 5, 7]);
3590 }
3591 #[test]
3592 fn test_chord_type_from_cv() {
3593 assert_eq!(ChordType::from_cv(0.0), ChordType::Major);
3594 assert_eq!(ChordType::from_cv(0.12), ChordType::Minor);
3595 assert_eq!(ChordType::from_cv(0.23), ChordType::Seventh);
3596 assert_eq!(ChordType::from_cv(1.0), ChordType::Sus4);
3597 }
3598 #[test]
3599 fn test_arp_pattern_from_cv() {
3600 assert_eq!(ArpPattern::from_cv(0.0), ArpPattern::Up);
3601 assert_eq!(ArpPattern::from_cv(0.1), ArpPattern::Up);
3602 assert_eq!(ArpPattern::from_cv(0.3), ArpPattern::Down);
3603 assert_eq!(ArpPattern::from_cv(0.6), ArpPattern::UpDown);
3604 assert_eq!(ArpPattern::from_cv(0.9), ArpPattern::Random);
3605 assert_eq!(ArpPattern::from_cv(1.0), ArpPattern::Random);
3606 }
3607 #[test]
3608 fn test_arpeggiator_default_reset_sample_rate() {
3609 let mut arp = Arpeggiator::default();
3610 assert_eq!(arp.sample_rate, 44100.0);
3611
3612 arp.add_note(0.0);
3614 assert_eq!(arp.num_notes, 1);
3615
3616 arp.reset();
3618 assert_eq!(arp.num_notes, 0);
3619 assert_eq!(arp.current_step, 0);
3620
3621 arp.set_sample_rate(48000.0);
3623 assert_eq!(arp.sample_rate, 48000.0);
3624
3625 assert_eq!(arp.type_id(), "arpeggiator");
3626 assert_eq!(arp.port_spec().inputs.len(), 6);
3627 assert_eq!(arp.port_spec().outputs.len(), 3);
3628 }
3629 #[test]
3630 fn test_arpeggiator_add_remove_notes() {
3631 let mut arp = Arpeggiator::new(44100.0);
3632
3633 arp.add_note(0.0); arp.add_note(0.5); arp.add_note(0.25); assert_eq!(arp.num_notes, 3);
3639 assert_eq!(arp.held_notes[0], 0.0);
3641 assert_eq!(arp.held_notes[1], 0.25);
3642 assert_eq!(arp.held_notes[2], 0.5);
3643
3644 arp.remove_note(0.25);
3646 assert_eq!(arp.num_notes, 2);
3647 assert_eq!(arp.held_notes[0], 0.0);
3648 assert_eq!(arp.held_notes[1], 0.5);
3649 }
3650 #[test]
3651 fn test_arpeggiator_up_pattern() {
3652 let mut arp = Arpeggiator::new(44100.0);
3653 let mut inputs = PortValues::new();
3654 let mut outputs = PortValues::new();
3655
3656 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);
3665
3666 inputs.set(3, 0.0); let mut notes_out = Vec::new();
3669
3670 for _ in 0..6 {
3671 inputs.set(2, 5.0); arp.tick(&inputs, &mut outputs);
3673 notes_out.push(outputs.get(10).unwrap());
3674
3675 inputs.set(2, 0.0); arp.tick(&inputs, &mut outputs);
3677 }
3678
3679 assert!(notes_out[0] < notes_out[1]);
3681 assert!(notes_out[1] < notes_out[2]);
3682 assert!((notes_out[3] - notes_out[0]).abs() < 0.01);
3684 }
3685 #[test]
3686 fn test_arpeggiator_trigger_output() {
3687 let mut arp = Arpeggiator::new(44100.0);
3688 let mut inputs = PortValues::new();
3689 let mut outputs = PortValues::new();
3690
3691 inputs.set(0, 0.0);
3693 inputs.set(1, 5.0);
3694 arp.tick(&inputs, &mut outputs);
3695
3696 inputs.set(2, 5.0);
3698 arp.tick(&inputs, &mut outputs);
3699 let trigger = outputs.get(12).unwrap();
3700 assert!(trigger > 0.0, "Should output trigger on clock");
3701
3702 inputs.set(2, 0.0);
3704 arp.tick(&inputs, &mut outputs);
3705 let trigger2 = outputs.get(12).unwrap();
3706 assert!(trigger2 > 0.0, "Trigger should persist briefly");
3707 }
3708 #[test]
3709 fn test_arpeggiator_reset_input() {
3710 let mut arp = Arpeggiator::new(44100.0);
3711 let mut inputs = PortValues::new();
3712 let mut outputs = PortValues::new();
3713
3714 inputs.set(0, 0.0);
3716 inputs.set(1, 5.0);
3717 arp.tick(&inputs, &mut outputs);
3718
3719 for _ in 0..5 {
3720 inputs.set(2, 5.0);
3721 arp.tick(&inputs, &mut outputs);
3722 inputs.set(2, 0.0);
3723 arp.tick(&inputs, &mut outputs);
3724 }
3725
3726 let step_before = arp.current_step;
3727 assert!(step_before > 0);
3728
3729 inputs.set(5, 5.0);
3731 arp.tick(&inputs, &mut outputs);
3732
3733 assert_eq!(arp.current_step, 0, "Reset should clear step");
3734 }
3735 #[test]
3736 fn test_arpeggiator_octaves() {
3737 let mut arp = Arpeggiator::new(44100.0);
3738
3739 arp.add_note(0.0); let note1 = arp.get_current_note(ArpPattern::Up, 2);
3744 arp.current_step = 1;
3745 let note2 = arp.get_current_note(ArpPattern::Up, 2);
3746
3747 assert!(
3748 (note2 - note1 - 1.0).abs() < 0.01,
3749 "Second note should be 1 octave higher"
3750 );
3751 }
3752 #[test]
3753 fn test_mixer_summation_bounded() {
3754 let mut mixer = Mixer::new(4);
3756 let mut inputs = PortValues::new();
3757 let mut outputs = PortValues::new();
3758
3759 for i in 0..4 {
3761 inputs.set(i as u32, 5.0);
3762 }
3763
3764 mixer.tick(&inputs, &mut outputs);
3765 let out = outputs.get(100).unwrap_or(0.0);
3766
3767 assert!(
3770 out.abs() <= SAFE_AUDIO_LIMIT * 2.0,
3771 "Mixer output {} is very high - consider adding limiting",
3772 out
3773 );
3774 }
3775
3776 #[test]
3779 fn test_scale_quantizer_octave_wrap_minor_11() {
3780 assert_eq!(
3782 ScaleQuantizer::quantize_to_scale(11, &ScaleQuantizer::MINOR),
3783 12
3784 );
3785 }
3786
3787 #[test]
3788 fn test_scale_quantizer_monotonic_sweep() {
3789 for scale in [
3793 &ScaleQuantizer::MINOR[..],
3794 &ScaleQuantizer::PENT_MAJOR[..],
3795 &ScaleQuantizer::BLUES[..],
3796 ] {
3797 let mut prev = i32::MIN;
3798 for note in 0..=24 {
3799 let q = ScaleQuantizer::quantize_to_scale(note, scale);
3800 assert!(
3801 q >= prev,
3802 "non-monotonic: note {} -> {} after {}",
3803 note,
3804 q,
3805 prev
3806 );
3807 prev = q;
3808 }
3809 }
3810 }
3811
3812 #[test]
3815 fn test_quantizer_hysteresis_no_chatter() {
3816 let mut quant = Quantizer::new(Scale::Chromatic);
3820 let mut inputs = PortValues::new();
3821 let mut outputs = PortValues::new();
3822
3823 let n = 4000;
3824 let mut changes = 0;
3825 let mut prev: Option<f64> = None;
3826 for i in 0..=n {
3827 let base = 2.0 * i as f64 / n as f64; let dither = if i % 2 == 0 { 0.1 } else { -0.1 };
3829 inputs.set(0, (base + dither) / 12.0);
3830 quant.tick(&inputs, &mut outputs);
3831 let out = outputs.get(10).unwrap();
3832 if let Some(p) = prev {
3833 if (out - p).abs() > 1e-9 {
3834 changes += 1;
3835 }
3836 }
3837 prev = Some(out);
3838 }
3839 assert_eq!(changes, 2, "expected exactly two boundary crossings");
3840 }
3841
3842 #[test]
3843 fn test_scale_quantizer_trigger_once_per_boundary() {
3844 let mut sq = ScaleQuantizer::new(44100.0);
3847 let mut inputs = PortValues::new();
3848 let mut outputs = PortValues::new();
3849 inputs.set(2, 0.0); let n = 4000;
3852 let mut triggers = 0;
3853 for i in 0..=n {
3854 let base = 2.0 * i as f64 / n as f64;
3855 let dither = if i % 2 == 0 { 0.1 } else { -0.1 };
3856 inputs.set(0, (base + dither) / 12.0);
3857 sq.tick(&inputs, &mut outputs);
3858 if outputs.get(11).unwrap() > 2.5 {
3859 triggers += 1;
3860 }
3861 }
3862 assert_eq!(triggers, 2, "trigger should fire once per boundary");
3863 }
3864
3865 #[test]
3866 fn test_comparator_hysteresis_no_chatter() {
3867 let mut cmp = Comparator::new();
3870 let mut inputs = PortValues::new();
3871 let mut outputs = PortValues::new();
3872 inputs.set(1, 0.0); inputs.set(0, 0.0);
3876 cmp.tick(&inputs, &mut outputs);
3877
3878 let mut gt_high = 0;
3879 let mut lt_high = 0;
3880 for i in 0..200 {
3881 let a = if i % 2 == 0 { 0.02 } else { -0.02 };
3882 inputs.set(0, a);
3883 cmp.tick(&inputs, &mut outputs);
3884 if outputs.get(10).unwrap() > 2.5 {
3885 gt_high += 1;
3886 }
3887 if outputs.get(11).unwrap() > 2.5 {
3888 lt_high += 1;
3889 }
3890 assert!(outputs.get(12).unwrap() > 2.5, "should stay equal");
3891 }
3892 assert_eq!(gt_high, 0, "gt should never fire on sub-band dither");
3893 assert_eq!(lt_high, 0, "lt should never fire on sub-band dither");
3894 }
3895
3896 #[test]
3897 fn test_comparator_still_compares() {
3898 let mut cmp = Comparator::new();
3900 let mut inputs = PortValues::new();
3901 let mut outputs = PortValues::new();
3902
3903 inputs.set(0, 3.0);
3904 inputs.set(1, 1.0);
3905 cmp.tick(&inputs, &mut outputs);
3906 assert!(outputs.get(10).unwrap() > 2.5);
3907
3908 inputs.set(0, 1.0);
3909 inputs.set(1, 3.0);
3910 cmp.tick(&inputs, &mut outputs);
3911 assert!(outputs.get(11).unwrap() > 2.5);
3912
3913 inputs.set(0, 2.0);
3914 inputs.set(1, 2.0);
3915 cmp.tick(&inputs, &mut outputs);
3916 assert!(outputs.get(12).unwrap() > 2.5);
3917 }
3918
3919 #[test]
3922 fn test_clock_divided_outputs() {
3923 let mut clock = Clock::new(1000.0);
3925 let mut inputs = PortValues::new();
3926 let mut outputs = PortValues::new();
3927 inputs.set(0, 5.0); let (mut main_c, mut div2_c, mut div4_c) = (0, 0, 0);
3930 let (mut pm, mut p2, mut p4) = (0.0, 0.0, 0.0);
3931 for _ in 0..100_000 {
3932 clock.tick(&inputs, &mut outputs);
3933 let m = outputs.get(10).unwrap();
3934 let d2 = outputs.get(11).unwrap();
3935 let d4 = outputs.get(12).unwrap();
3936 let m_rise = m > 2.5 && pm <= 2.5;
3937 if m_rise {
3938 main_c += 1;
3939 }
3940 if d2 > 2.5 && p2 <= 2.5 {
3941 div2_c += 1;
3942 }
3943 if d4 > 2.5 && p4 <= 2.5 {
3944 div4_c += 1;
3945 }
3946 pm = m;
3947 p2 = d2;
3948 p4 = d4;
3949 if m_rise && main_c == 8 {
3950 break;
3951 }
3952 }
3953 assert_eq!(main_c, 8, "main should pulse every cycle");
3954 assert_eq!(div2_c, 4, "div2 should pulse at half rate");
3955 assert_eq!(div4_c, 2, "div4 should pulse at quarter rate");
3956 }
3957
3958 #[test]
3959 fn test_clock_default_tempo_120_bpm() {
3960 let mut clock = Clock::default();
3962 let inputs = PortValues::new(); let mut outputs = PortValues::new();
3964
3965 let mut edges = Vec::new();
3966 let mut prev = 0.0;
3967 for i in 0..100_000 {
3968 clock.tick(&inputs, &mut outputs);
3969 let m = outputs.get(10).unwrap();
3970 if m > 2.5 && prev <= 2.5 {
3971 edges.push(i);
3972 }
3973 prev = m;
3974 if edges.len() >= 2 {
3975 break;
3976 }
3977 }
3978 assert!(edges.len() >= 2, "expected at least two clock pulses");
3979 let period = (edges[1] - edges[0]) as f64;
3980 let bpm = 60.0 * 44100.0 / period;
3981 assert!(
3982 (bpm - 120.0).abs() < 1.0,
3983 "default tempo {} BPM should be ~120",
3984 bpm
3985 );
3986 }
3987
3988 #[test]
3991 fn test_bernoulli_gate_latches() {
3992 let mut bg = BernoulliGate::new();
3993 let mut inputs = PortValues::new();
3994
3995 inputs.set(1, 10.0);
3997
3998 let tick = |bg: &mut BernoulliGate, inputs: &PortValues| {
4001 let mut o = PortValues::new();
4002 bg.tick(inputs, &mut o);
4003 o
4004 };
4005
4006 inputs.set(0, 0.0);
4007 tick(&mut bg, &inputs);
4008 inputs.set(0, 5.0);
4009 let o = tick(&mut bg, &inputs); assert!(o.get(12).unwrap() > 2.5, "gate_a should latch high");
4011 assert!(o.get(13).unwrap() < 2.5);
4012
4013 inputs.set(0, 0.0);
4015 for _ in 0..20 {
4016 let o = tick(&mut bg, &inputs);
4017 assert!(o.get(12).unwrap() > 2.5, "gate_a must stay latched");
4018 assert!(o.get(13).unwrap() < 2.5);
4019 }
4020
4021 inputs.set(1, 0.0);
4023 inputs.set(0, 0.0);
4024 tick(&mut bg, &inputs);
4025 inputs.set(0, 5.0);
4026 let o = tick(&mut bg, &inputs); assert!(o.get(12).unwrap() < 2.5, "gate_a should release");
4028 assert!(o.get(13).unwrap() > 2.5, "gate_b should latch high");
4029 }
4030
4031 #[test]
4034 fn test_euclidean_pulses_control_live() {
4035 let mut euc = Euclidean::new(44100.0);
4037 let mut inputs = PortValues::new();
4038 let mut outputs = PortValues::new();
4039 inputs.set(1, 0.5); inputs.set(2, 0.25); euc.tick(&inputs, &mut outputs);
4043 let active_low = euc.pattern.iter().filter(|&&x| x).count();
4044 assert_eq!(active_low, 2);
4045
4046 inputs.set(2, 0.75); euc.tick(&inputs, &mut outputs);
4048 let active_high = euc.pattern.iter().filter(|&&x| x).count();
4049 assert_eq!(active_high, 6);
4050 assert_ne!(active_low, active_high, "pulses control must be live");
4051 }
4052
4053 #[test]
4054 fn test_euclidean_accent_on_rotated_pulse() {
4055 let mut euc = Euclidean::new(44100.0);
4058 let mut inputs = PortValues::new();
4059 let mut outputs = PortValues::new();
4060 inputs.set(1, 0.4003); inputs.set(2, 0.5); inputs.set(3, 0.3); let mut accents = 0;
4065 for _ in 0..8 {
4066 inputs.set(0, 5.0); euc.tick(&inputs, &mut outputs);
4068 let out = outputs.get(10).unwrap();
4069 let accent = outputs.get(11).unwrap();
4070 if accent > 2.5 {
4071 accents += 1;
4072 assert!(out > 2.5, "accent must coincide with a pulse");
4073 }
4074 inputs.set(0, 0.0); euc.tick(&inputs, &mut outputs);
4076 }
4077 assert_eq!(accents, 1, "exactly one accent per cycle");
4078 }
4079
4080 #[test]
4081 fn test_euclidean_gate_threshold() {
4082 let mut euc = Euclidean::new(44100.0);
4085 let mut inputs = PortValues::new();
4086 let mut outputs = PortValues::new();
4087 inputs.set(1, 0.4003); inputs.set(2, 1.0); let mut low_pulses = 0;
4092 for _ in 0..8 {
4093 inputs.set(0, 1.0);
4094 euc.tick(&inputs, &mut outputs);
4095 if outputs.get(10).unwrap() > 2.5 {
4096 low_pulses += 1;
4097 }
4098 inputs.set(0, 0.0);
4099 euc.tick(&inputs, &mut outputs);
4100 }
4101 assert_eq!(low_pulses, 0, "1.0V clock must not trigger");
4102
4103 let mut high_pulses = 0;
4105 for _ in 0..8 {
4106 inputs.set(0, 5.0);
4107 euc.tick(&inputs, &mut outputs);
4108 if outputs.get(10).unwrap() > 2.5 {
4109 high_pulses += 1;
4110 }
4111 inputs.set(0, 0.0);
4112 euc.tick(&inputs, &mut outputs);
4113 }
4114 assert!(high_pulses > 0, "5V clock must trigger");
4115 }
4116
4117 #[test]
4120 fn test_arpeggiator_releases_notes() {
4121 let mut arp = Arpeggiator::new(44100.0);
4122 let mut inputs = PortValues::new();
4123 let mut outputs = PortValues::new();
4124
4125 inputs.set(0, 0.25);
4127 inputs.set(1, 5.0);
4128 arp.tick(&inputs, &mut outputs);
4129 assert_eq!(arp.num_notes, 1);
4130
4131 for _ in 0..5 {
4133 arp.tick(&inputs, &mut outputs);
4134 }
4135 assert_eq!(arp.num_notes, 1);
4136
4137 inputs.set(1, 0.0);
4139 arp.tick(&inputs, &mut outputs);
4140 assert_eq!(arp.num_notes, 0, "release must remove the held note");
4141
4142 inputs.set(0, 0.5);
4144 inputs.set(1, 5.0);
4145 arp.tick(&inputs, &mut outputs);
4146 assert_eq!(arp.num_notes, 1);
4147 inputs.set(1, 0.0);
4148 arp.tick(&inputs, &mut outputs);
4149 assert_eq!(arp.num_notes, 0);
4150 }
4151
4152 #[test]
4153 fn test_arpeggiator_reset_clears_held_notes() {
4154 let mut arp = Arpeggiator::new(44100.0);
4155 let mut inputs = PortValues::new();
4156 let mut outputs = PortValues::new();
4157
4158 inputs.set(0, 0.0);
4160 inputs.set(1, 5.0);
4161 arp.tick(&inputs, &mut outputs);
4162 assert_eq!(arp.num_notes, 1);
4163
4164 inputs.set(5, 5.0);
4166 arp.tick(&inputs, &mut outputs);
4167 assert_eq!(arp.num_notes, 0, "reset must clear held notes");
4168 }
4169
4170 #[cfg(feature = "alloc")]
4175 #[test]
4176 fn test_custom_scale_snaps_to_degrees() {
4177 let mut sq = ScaleQuantizer::new(44100.0);
4179 assert!(!sq.has_custom_scale());
4180 sq.set_custom_scale(&[0.0, 200.0, 400.0, 600.0, 800.0, 1000.0]);
4181 assert!(sq.has_custom_scale());
4182
4183 let mut inputs = PortValues::new();
4184 let mut outputs = PortValues::new();
4185
4186 inputs.set(0, 0.175);
4190 for _ in 0..4 {
4192 sq.tick(&inputs, &mut outputs);
4193 }
4194 let out_v = outputs.get(10).unwrap();
4195 assert!(
4197 (out_v - 200.0 / 1200.0).abs() < 1e-6,
4198 "custom scale should snap to 200 cents, got {} cents",
4199 out_v * 1200.0
4200 );
4201 }
4202
4203 #[cfg(feature = "alloc")]
4204 #[test]
4205 fn test_load_scala_and_quantize() {
4206 let scl = "\
4207whole tone
42086
4209200.0
4210400.0
4211600.0
4212800.0
42131000.0
42141200.0
4215";
4216 let mut sq = ScaleQuantizer::new(44100.0);
4217 sq.load_scala(scl).unwrap();
4218 assert!(sq.has_custom_scale());
4219
4220 let mut inputs = PortValues::new();
4221 let mut outputs = PortValues::new();
4222 inputs.set(0, 390.0 / 1200.0);
4224 for _ in 0..4 {
4225 sq.tick(&inputs, &mut outputs);
4226 }
4227 let out_v = outputs.get(10).unwrap();
4228 assert!(
4229 (out_v - 400.0 / 1200.0).abs() < 1e-6,
4230 "expected 400 cents, got {} cents",
4231 out_v * 1200.0
4232 );
4233 }
4234
4235 #[cfg(feature = "alloc")]
4236 #[test]
4237 fn test_clear_custom_scale_restores_enum() {
4238 let mut sq = ScaleQuantizer::new(44100.0);
4239 sq.set_custom_scale(&[0.0, 200.0, 400.0]);
4240 assert!(sq.has_custom_scale());
4241 sq.clear_custom_scale();
4242 assert!(!sq.has_custom_scale());
4243 }
4244
4245 #[cfg(feature = "alloc")]
4246 #[test]
4247 fn test_load_scala_malformed_leaves_scale_unchanged() {
4248 let mut sq = ScaleQuantizer::new(44100.0);
4249 sq.set_custom_scale(&[0.0, 500.0]);
4250 let err = sq.load_scala("bad\n3\n100.0\n");
4252 assert!(err.is_err());
4253 assert!(sq.has_custom_scale());
4255 }
4256
4257 #[test]
4260 fn test_quantizer_negative_voct_chromatic() {
4261 let cases = [
4263 (-0.5, -0.5), (-1.0, -1.0), (-13.0 / 12.0, -13.0 / 12.0), (-1.0 / 24.0, 0.0), ];
4268 for (input, expected) in cases {
4269 let mut q = Quantizer::new(Scale::Chromatic);
4270 let mut inputs = PortValues::new();
4271 let mut outputs = PortValues::new();
4272 inputs.set(0, input);
4273 q.tick(&inputs, &mut outputs);
4274 let out = outputs.get(10).unwrap();
4275 assert!(
4276 (out - expected).abs() < 1e-9,
4277 "chromatic {input}V -> {out}V, expected {expected}V"
4278 );
4279 }
4280 }
4281
4282 #[test]
4283 fn test_quantizer_negative_voct_major_scale() {
4284 let mut q = Quantizer::new(Scale::Major);
4287 let mut inputs = PortValues::new();
4288 let mut outputs = PortValues::new();
4289 inputs.set(0, -0.5);
4290 q.tick(&inputs, &mut outputs);
4291 let out = outputs.get(10).unwrap();
4292 assert!(
4293 (out - (-7.0 / 12.0)).abs() < 1e-9,
4294 "major -0.5V should snap to F3 (-7/12 V), got {out}V"
4295 );
4296
4297 let mut q2 = Quantizer::new(Scale::Major);
4299 inputs.set(0, -1.0);
4300 q2.tick(&inputs, &mut outputs);
4301 assert!((outputs.get(10).unwrap() - (-1.0)).abs() < 1e-9);
4302 }
4303
4304 #[test]
4305 fn test_scale_quantizer_negative_voct() {
4306 let cases = [(-1.0, -1.0), (-13.0 / 12.0, -13.0 / 12.0)];
4308 for (input, expected) in cases {
4309 let mut sq = ScaleQuantizer::new(44100.0);
4310 let mut inputs = PortValues::new();
4311 let mut outputs = PortValues::new();
4312 inputs.set(0, input);
4313 inputs.set(2, 0.0); sq.tick(&inputs, &mut outputs);
4315 let out = outputs.get(10).unwrap();
4316 assert!(
4317 (out - expected).abs() < 1e-9,
4318 "scale-quantizer chromatic {input}V -> {out}V, expected {expected}V"
4319 );
4320 }
4321
4322 let mut sq = ScaleQuantizer::new(44100.0);
4324 let mut inputs = PortValues::new();
4325 let mut outputs = PortValues::new();
4326 inputs.set(0, -0.5);
4327 inputs.set(2, 0.3); sq.tick(&inputs, &mut outputs);
4329 let out = outputs.get(10).unwrap();
4330 assert!(
4331 (out - (-7.0 / 12.0)).abs() < 1e-9,
4332 "minor -0.5V should snap to F3 (-7/12 V), got {out}V"
4333 );
4334 }
4335
4336 #[test]
4339 fn test_euclidean_reset_and_sample_rate() {
4340 let mut euc = Euclidean::new(44100.0);
4341 assert_eq!(euc.type_id(), "euclidean");
4342 let mut inputs = PortValues::new();
4343 let mut outputs = PortValues::new();
4344 for _ in 0..3 {
4346 inputs.set(0, 5.0);
4347 euc.tick(&inputs, &mut outputs);
4348 inputs.set(0, 0.0);
4349 euc.tick(&inputs, &mut outputs);
4350 }
4351 assert!(euc.step != 0, "clock pulses should advance the step");
4352 euc.reset();
4353 assert_eq!(euc.step, 0);
4354 assert!(!euc.cycle_accented);
4355 euc.set_sample_rate(48000.0);
4357 inputs.set(0, 5.0);
4358 euc.tick(&inputs, &mut outputs);
4359 assert!(outputs.get(10).unwrap().is_finite());
4360 }
4361
4362 #[test]
4363 fn test_scale_quantizer_reset_and_sample_rate() {
4364 let mut sq = ScaleQuantizer::new(44100.0);
4365 assert_eq!(sq.type_id(), "scale_quantizer");
4366 let mut inputs = PortValues::new();
4367 let mut outputs = PortValues::new();
4368 inputs.set(0, 0.25); sq.tick(&inputs, &mut outputs);
4370 assert!(sq.last_output.is_some());
4371 sq.reset();
4372 assert!(sq.last_output.is_none());
4373 sq.set_sample_rate(48000.0);
4374 sq.tick(&inputs, &mut outputs);
4375 assert!(outputs.get(10).unwrap().is_finite());
4376 }
4377}