1use crate::port::{GraphModule, PortDef, PortId, PortSpec, PortValues, SignalKind};
131use alloc::string::String;
132use alloc::vec;
133use core::marker::PhantomData;
134
135pub trait Module: Send {
185 type In;
187 type Out;
189
190 fn tick(&mut self, input: Self::In) -> Self::Out;
195
196 fn process(&mut self, input: &[Self::In], output: &mut [Self::Out])
202 where
203 Self::In: Clone,
204 {
205 for (i, o) in input.iter().zip(output.iter_mut()) {
206 *o = self.tick(i.clone());
207 }
208 }
209
210 fn reset(&mut self);
215
216 fn set_sample_rate(&mut self, _sample_rate: f64) {}
221}
222
223pub trait ModuleExt: Module + Sized {
225 fn then<M: Module<In = Self::Out>>(self, next: M) -> Chain<Self, M> {
227 Chain {
228 first: self,
229 second: next,
230 }
231 }
232
233 fn parallel<M: Module>(self, other: M) -> Parallel<Self, M> {
235 Parallel {
236 left: self,
237 right: other,
238 }
239 }
240
241 fn fanout<M: Module<In = Self::In>>(self, other: M) -> Fanout<Self, M>
243 where
244 Self::In: Clone,
245 {
246 Fanout {
247 left: self,
248 right: other,
249 }
250 }
251
252 fn map<F, U>(self, f: F) -> Map<Self, F>
254 where
255 F: Fn(Self::Out) -> U,
256 {
257 Map { module: self, f }
258 }
259
260 fn contramap<F, U>(self, f: F) -> Contramap<Self, F, U>
262 where
263 F: Fn(U) -> Self::In,
264 {
265 Contramap {
266 module: self,
267 f,
268 _phantom: PhantomData,
269 }
270 }
271
272 fn feedback<F>(self, combine: F) -> Feedback<Self, F>
301 where
302 Self::Out: Default + Clone,
303 {
304 Feedback {
305 module: self,
306 combine,
307 delay_buffer: Self::Out::default(),
308 }
309 }
310
311 fn first<C>(self) -> First<Self, C> {
313 First {
314 module: self,
315 _phantom: PhantomData,
316 }
317 }
318
319 fn second<C>(self) -> Second<Self, C> {
321 Second {
322 module: self,
323 _phantom: PhantomData,
324 }
325 }
326}
327
328impl<M: Module> ModuleExt for M {}
330
331pub struct Chain<A, B> {
333 pub first: A,
334 pub second: B,
335}
336
337impl<A, B> Module for Chain<A, B>
338where
339 A: Module,
340 B: Module<In = A::Out>,
341{
342 type In = A::In;
343 type Out = B::Out;
344
345 #[inline]
346 fn tick(&mut self, input: Self::In) -> Self::Out {
347 self.second.tick(self.first.tick(input))
348 }
349
350 fn reset(&mut self) {
351 self.first.reset();
352 self.second.reset();
353 }
354
355 fn set_sample_rate(&mut self, sample_rate: f64) {
356 self.first.set_sample_rate(sample_rate);
357 self.second.set_sample_rate(sample_rate);
358 }
359}
360
361pub struct Parallel<A, B> {
363 pub left: A,
364 pub right: B,
365}
366
367impl<A, B> Module for Parallel<A, B>
368where
369 A: Module,
370 B: Module,
371{
372 type In = (A::In, B::In);
373 type Out = (A::Out, B::Out);
374
375 #[inline]
376 fn tick(&mut self, (a, b): Self::In) -> Self::Out {
377 (self.left.tick(a), self.right.tick(b))
378 }
379
380 fn reset(&mut self) {
381 self.left.reset();
382 self.right.reset();
383 }
384
385 fn set_sample_rate(&mut self, sample_rate: f64) {
386 self.left.set_sample_rate(sample_rate);
387 self.right.set_sample_rate(sample_rate);
388 }
389}
390
391pub struct Fanout<A, B> {
393 pub left: A,
394 pub right: B,
395}
396
397impl<A, B> Module for Fanout<A, B>
398where
399 A: Module,
400 B: Module<In = A::In>,
401 A::In: Clone,
402{
403 type In = A::In;
404 type Out = (A::Out, B::Out);
405
406 #[inline]
407 fn tick(&mut self, input: Self::In) -> Self::Out {
408 (self.left.tick(input.clone()), self.right.tick(input))
409 }
410
411 fn reset(&mut self) {
412 self.left.reset();
413 self.right.reset();
414 }
415
416 fn set_sample_rate(&mut self, sample_rate: f64) {
417 self.left.set_sample_rate(sample_rate);
418 self.right.set_sample_rate(sample_rate);
419 }
420}
421
422pub struct Feedback<M: Module, F> {
430 pub module: M,
431 pub combine: F,
432 pub delay_buffer: M::Out,
434}
435
436impl<M, F, Combined> Module for Feedback<M, F>
437where
438 M: Module<In = Combined>,
439 F: Fn(M::Out, M::Out) -> Combined + Send,
440 M::Out: Default + Clone + Send,
441{
442 type In = M::Out;
443 type Out = M::Out;
444
445 fn tick(&mut self, input: Self::In) -> Self::Out {
446 let combined = (self.combine)(input, self.delay_buffer.clone());
447 let output = self.module.tick(combined);
448 self.delay_buffer = output.clone();
449 output
450 }
451
452 fn reset(&mut self) {
453 self.module.reset();
454 self.delay_buffer = M::Out::default();
455 }
456
457 fn set_sample_rate(&mut self, sample_rate: f64) {
458 self.module.set_sample_rate(sample_rate);
459 }
460}
461
462pub struct Map<M, F> {
464 pub module: M,
465 pub f: F,
466}
467
468impl<M, F, U> Module for Map<M, F>
469where
470 M: Module,
471 F: Fn(M::Out) -> U + Send,
472{
473 type In = M::In;
474 type Out = U;
475
476 #[inline]
477 fn tick(&mut self, input: Self::In) -> Self::Out {
478 (self.f)(self.module.tick(input))
479 }
480
481 fn reset(&mut self) {
482 self.module.reset();
483 }
484
485 fn set_sample_rate(&mut self, sample_rate: f64) {
486 self.module.set_sample_rate(sample_rate);
487 }
488}
489
490pub struct Contramap<M, F, U> {
492 pub module: M,
493 pub f: F,
494 pub _phantom: PhantomData<U>,
495}
496
497impl<M, F, U> Module for Contramap<M, F, U>
498where
499 M: Module,
500 F: Fn(U) -> M::In + Send,
501 U: Send,
502{
503 type In = U;
504 type Out = M::Out;
505
506 #[inline]
507 fn tick(&mut self, input: Self::In) -> Self::Out {
508 self.module.tick((self.f)(input))
509 }
510
511 fn reset(&mut self) {
512 self.module.reset();
513 }
514
515 fn set_sample_rate(&mut self, sample_rate: f64) {
516 self.module.set_sample_rate(sample_rate);
517 }
518}
519
520pub struct Split<T> {
522 _phantom: PhantomData<T>,
523}
524
525impl<T> Split<T> {
526 pub fn new() -> Self {
527 Self {
528 _phantom: PhantomData,
529 }
530 }
531}
532
533impl<T> Default for Split<T> {
534 fn default() -> Self {
535 Self::new()
536 }
537}
538
539impl<T: Clone + Send> Module for Split<T> {
540 type In = T;
541 type Out = (T, T);
542
543 #[inline]
544 fn tick(&mut self, input: Self::In) -> Self::Out {
545 (input.clone(), input)
546 }
547
548 fn reset(&mut self) {}
549}
550
551pub struct Merge<T, F> {
553 pub f: F,
554 _phantom: PhantomData<T>,
555}
556
557impl<T, F> Merge<T, F>
558where
559 F: Fn(T, T) -> T,
560{
561 pub fn new(f: F) -> Self {
562 Self {
563 f,
564 _phantom: PhantomData,
565 }
566 }
567}
568
569impl<T, F> Module for Merge<T, F>
570where
571 T: Send,
572 F: Fn(T, T) -> T + Send,
573{
574 type In = (T, T);
575 type Out = T;
576
577 #[inline]
578 fn tick(&mut self, (a, b): Self::In) -> Self::Out {
579 (self.f)(a, b)
580 }
581
582 fn reset(&mut self) {}
583}
584
585pub struct Swap<A, B> {
587 _phantom: PhantomData<(A, B)>,
588}
589
590impl<A, B> Swap<A, B> {
591 pub fn new() -> Self {
592 Self {
593 _phantom: PhantomData,
594 }
595 }
596}
597
598impl<A, B> Default for Swap<A, B> {
599 fn default() -> Self {
600 Self::new()
601 }
602}
603
604impl<A: Send, B: Send> Module for Swap<A, B> {
605 type In = (A, B);
606 type Out = (B, A);
607
608 #[inline]
609 fn tick(&mut self, (a, b): Self::In) -> Self::Out {
610 (b, a)
611 }
612
613 fn reset(&mut self) {}
614}
615
616pub struct First<M, C> {
618 pub module: M,
619 pub _phantom: PhantomData<C>,
620}
621
622impl<M, C> Module for First<M, C>
623where
624 M: Module,
625 C: Send,
626{
627 type In = (M::In, C);
628 type Out = (M::Out, C);
629
630 #[inline]
631 fn tick(&mut self, (a, c): Self::In) -> Self::Out {
632 (self.module.tick(a), c)
633 }
634
635 fn reset(&mut self) {
636 self.module.reset();
637 }
638
639 fn set_sample_rate(&mut self, sample_rate: f64) {
640 self.module.set_sample_rate(sample_rate);
641 }
642}
643
644pub struct Second<M, C> {
646 pub module: M,
647 pub _phantom: PhantomData<C>,
648}
649
650impl<M, C> Module for Second<M, C>
651where
652 M: Module,
653 C: Send,
654{
655 type In = (C, M::In);
656 type Out = (C, M::Out);
657
658 #[inline]
659 fn tick(&mut self, (c, a): Self::In) -> Self::Out {
660 (c, self.module.tick(a))
661 }
662
663 fn reset(&mut self) {
664 self.module.reset();
665 }
666
667 fn set_sample_rate(&mut self, sample_rate: f64) {
668 self.module.set_sample_rate(sample_rate);
669 }
670}
671
672pub struct Identity<T> {
674 _phantom: PhantomData<T>,
675}
676
677impl<T> Identity<T> {
678 pub fn new() -> Self {
679 Self {
680 _phantom: PhantomData,
681 }
682 }
683}
684
685impl<T> Default for Identity<T> {
686 fn default() -> Self {
687 Self::new()
688 }
689}
690
691impl<T: Send> Module for Identity<T> {
692 type In = T;
693 type Out = T;
694
695 #[inline]
696 fn tick(&mut self, input: Self::In) -> Self::Out {
697 input
698 }
699
700 fn reset(&mut self) {}
701}
702
703pub struct Constant<T> {
705 pub value: T,
706}
707
708impl<T> Constant<T> {
709 pub fn new(value: T) -> Self {
710 Self { value }
711 }
712}
713
714impl<T: Clone + Send> Module for Constant<T> {
715 type In = ();
716 type Out = T;
717
718 #[inline]
719 fn tick(&mut self, _input: Self::In) -> Self::Out {
720 self.value.clone()
721 }
722
723 fn reset(&mut self) {}
724}
725
726pub struct Arr<F, A> {
733 f: F,
734 _phantom: PhantomData<A>,
735}
736
737pub fn arr<F, A, B>(f: F) -> Arr<F, A>
752where
753 F: Fn(A) -> B,
754{
755 Arr {
756 f,
757 _phantom: PhantomData,
758 }
759}
760
761impl<F, A, B> Module for Arr<F, A>
762where
763 F: Fn(A) -> B + Send,
764 A: Send,
765{
766 type In = A;
767 type Out = B;
768
769 #[inline]
770 fn tick(&mut self, input: Self::In) -> Self::Out {
771 (self.f)(input)
772 }
773
774 fn reset(&mut self) {}
775}
776
777pub struct GraphModuleAdapter<G: GraphModule> {
788 module: G,
789 input_port: PortId,
790 output_port: PortId,
791 inputs: PortValues,
792 outputs: PortValues,
793}
794
795impl<G: GraphModule> GraphModuleAdapter<G> {
796 pub fn new(module: G, input_port: PortId, output_port: PortId) -> Self {
799 let mut inputs = PortValues::new();
800 for port in &module.port_spec().inputs {
801 inputs.set(port.id, port.default);
802 }
803 Self {
804 module,
805 input_port,
806 output_port,
807 inputs,
808 outputs: PortValues::new(),
809 }
810 }
811
812 pub fn from_audio_ports(module: G) -> Option<Self> {
819 let (input_port, output_port) = {
820 let spec = module.port_spec();
821 let input_port = spec.inputs.iter().find(|p| p.kind == SignalKind::Audio)?.id;
822 let output_port = spec
823 .outputs
824 .iter()
825 .find(|p| p.kind == SignalKind::Audio)?
826 .id;
827 (input_port, output_port)
828 };
829 Some(Self::new(module, input_port, output_port))
830 }
831
832 pub fn inner(&self) -> &G {
834 &self.module
835 }
836
837 pub fn into_inner(self) -> G {
839 self.module
840 }
841}
842
843impl<G: GraphModule> Module for GraphModuleAdapter<G> {
844 type In = f64;
845 type Out = f64;
846
847 #[inline]
848 fn tick(&mut self, input: Self::In) -> Self::Out {
849 self.inputs.set(self.input_port, input);
850 self.outputs.clear();
857 self.module.tick(&self.inputs, &mut self.outputs);
858 self.outputs.get_or(self.output_port, 0.0)
859 }
860
861 fn reset(&mut self) {
862 self.module.reset();
863 }
864
865 fn set_sample_rate(&mut self, sample_rate: f64) {
866 self.module.set_sample_rate(sample_rate);
867 }
868}
869
870pub struct ModuleGraphAdapter<M> {
878 module: M,
879 spec: PortSpec,
880}
881
882impl<M> ModuleGraphAdapter<M>
883where
884 M: Module<In = f64, Out = f64>,
885{
886 pub fn new(module: M) -> Self {
889 Self::with_ports(module, "in", "out")
890 }
891
892 pub fn with_ports(
894 module: M,
895 input_name: impl Into<String>,
896 output_name: impl Into<String>,
897 ) -> Self {
898 let spec = PortSpec {
899 inputs: vec![PortDef::new(0, input_name, SignalKind::Audio)],
900 outputs: vec![PortDef::new(10, output_name, SignalKind::Audio)],
901 };
902 Self { module, spec }
903 }
904}
905
906impl<M> GraphModule for ModuleGraphAdapter<M>
907where
908 M: Module<In = f64, Out = f64> + Sync,
909{
910 fn port_spec(&self) -> &PortSpec {
911 &self.spec
912 }
913
914 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
915 let x = inputs.get_or(0, 0.0);
916 let y = self.module.tick(x);
917 outputs.set(10, y);
918 }
919
920 fn reset(&mut self) {
921 self.module.reset();
922 }
923
924 fn set_sample_rate(&mut self, sample_rate: f64) {
925 self.module.set_sample_rate(sample_rate);
926 }
927
928 fn type_id(&self) -> &'static str {
929 "combinator_chain"
930 }
931}
932
933#[cfg(test)]
934mod tests {
935 use super::*;
936 use crate::port::{GraphModule, PortDef, PortSpec, PortValues, SignalKind};
937
938 struct Gain {
940 factor: f64,
941 }
942
943 impl Module for Gain {
944 type In = f64;
945 type Out = f64;
946
947 fn tick(&mut self, input: Self::In) -> Self::Out {
948 input * self.factor
949 }
950
951 fn reset(&mut self) {}
952 }
953
954 #[test]
955 fn test_chain() {
956 let mut chain = Gain { factor: 2.0 }.then(Gain { factor: 3.0 });
957 assert!((chain.tick(1.0) - 6.0).abs() < 1e-10);
958 }
959
960 #[test]
961 fn test_parallel() {
962 let mut par = Gain { factor: 2.0 }.parallel(Gain { factor: 3.0 });
963 let (a, b) = par.tick((1.0, 1.0));
964 assert!((a - 2.0).abs() < 1e-10);
965 assert!((b - 3.0).abs() < 1e-10);
966 }
967
968 #[test]
969 fn test_fanout() {
970 let mut fan = Gain { factor: 2.0 }.fanout(Gain { factor: 3.0 });
971 let (a, b) = fan.tick(1.0);
972 assert!((a - 2.0).abs() < 1e-10);
973 assert!((b - 3.0).abs() < 1e-10);
974 }
975
976 #[test]
977 fn test_map() {
978 let mut mapped = Gain { factor: 2.0 }.map(|x| x + 1.0);
979 assert!((mapped.tick(1.0) - 3.0).abs() < 1e-10);
980 }
981
982 #[test]
983 fn test_identity() {
984 let mut id = Identity::<f64>::new();
985 assert!((id.tick(42.0) - 42.0).abs() < 1e-10);
986 }
987
988 #[test]
989 fn test_constant() {
990 let mut c = Constant::new(42.0_f64);
991 assert!((c.tick(()) - 42.0).abs() < 1e-10);
992 }
993
994 #[test]
995 fn test_split() {
996 let mut split = Split::<f64>::new();
997 let (a, b) = split.tick(5.0);
998 assert!((a - 5.0).abs() < 1e-10);
999 assert!((b - 5.0).abs() < 1e-10);
1000 }
1001
1002 #[test]
1003 fn test_merge() {
1004 let mut merge = Merge::new(|a: f64, b: f64| a + b);
1005 assert!((merge.tick((2.0, 3.0)) - 5.0).abs() < 1e-10);
1006 }
1007
1008 #[test]
1009 fn test_swap() {
1010 let mut swap = Swap::<i32, f64>::new();
1011 assert_eq!(swap.tick((1, 2.0)), (2.0, 1));
1012 }
1013
1014 struct SampleRateAware {
1018 sample_rate: f64,
1019 count: u32,
1020 }
1021
1022 impl SampleRateAware {
1023 fn new() -> Self {
1024 Self {
1025 sample_rate: 44100.0,
1026 count: 0,
1027 }
1028 }
1029 }
1030
1031 impl Module for SampleRateAware {
1032 type In = f64;
1033 type Out = f64;
1034
1035 fn tick(&mut self, input: Self::In) -> Self::Out {
1036 self.count += 1;
1037 input * self.sample_rate / 44100.0
1038 }
1039
1040 fn reset(&mut self) {
1041 self.count = 0;
1042 }
1043
1044 fn set_sample_rate(&mut self, sample_rate: f64) {
1045 self.sample_rate = sample_rate;
1046 }
1047 }
1048
1049 #[test]
1050 fn test_chain_reset_and_sample_rate() {
1051 let mut chain = SampleRateAware::new().then(SampleRateAware::new());
1052
1053 chain.tick(1.0);
1054 chain.tick(1.0);
1055
1056 chain.reset();
1058 assert_eq!(chain.first.count, 0);
1059 assert_eq!(chain.second.count, 0);
1060
1061 chain.set_sample_rate(48000.0);
1063 assert_eq!(chain.first.sample_rate, 48000.0);
1064 assert_eq!(chain.second.sample_rate, 48000.0);
1065 }
1066
1067 #[test]
1068 fn test_parallel_reset_and_sample_rate() {
1069 let mut par = SampleRateAware::new().parallel(SampleRateAware::new());
1070
1071 par.tick((1.0, 1.0));
1072 par.tick((1.0, 1.0));
1073
1074 par.reset();
1075 par.set_sample_rate(48000.0);
1076
1077 let result = par.tick((1.0, 1.0));
1078 assert!(result.0.abs() < 10.0);
1079 }
1080
1081 #[test]
1082 fn test_fanout_reset_and_sample_rate() {
1083 let mut fan = SampleRateAware::new().fanout(SampleRateAware::new());
1084
1085 fan.tick(1.0);
1086 fan.tick(1.0);
1087
1088 fan.reset();
1089 fan.set_sample_rate(48000.0);
1090
1091 let result = fan.tick(1.0);
1092 assert!(result.0.abs() < 10.0);
1093 }
1094
1095 #[test]
1096 fn test_feedback_reset_and_sample_rate() {
1097 let feedback_fn = |x: f64, prev: f64| x + prev * 0.5;
1098 let mut fb = SampleRateAware::new().feedback(feedback_fn);
1099
1100 for _ in 0..10 {
1101 fb.tick(1.0);
1102 }
1103
1104 fb.reset();
1105 fb.set_sample_rate(48000.0);
1106 }
1107
1108 #[test]
1109 fn test_map_reset_and_sample_rate() {
1110 let mut mapped = SampleRateAware::new().map(|x| x + 1.0);
1111
1112 mapped.tick(1.0);
1113 mapped.tick(1.0);
1114
1115 mapped.reset();
1116 mapped.set_sample_rate(48000.0);
1117
1118 let result = mapped.tick(1.0);
1119 assert!(result.abs() < 10.0);
1120 }
1121
1122 #[test]
1123 fn test_contramap() {
1124 let mut contra = Gain { factor: 2.0 }.contramap(|x: f64| x + 1.0);
1125 assert!((contra.tick(1.0) - 4.0).abs() < 1e-10); contra.reset();
1129 contra.set_sample_rate(48000.0);
1130 }
1131
1132 #[test]
1133 fn test_contramap_reset_and_sample_rate() {
1134 let mut contra = SampleRateAware::new().contramap(|x: f64| x * 2.0);
1135
1136 contra.tick(1.0);
1137 contra.reset();
1138 contra.set_sample_rate(48000.0);
1139
1140 let result = contra.tick(1.0);
1141 assert!(result.abs() < 10.0);
1142 }
1143
1144 #[test]
1145 fn test_first() {
1146 let mut first = Gain { factor: 2.0 }.first::<i32>();
1147 let (a, b) = first.tick((3.0, 42));
1148 assert!((a - 6.0).abs() < 1e-10);
1149 assert_eq!(b, 42);
1150 }
1151
1152 #[test]
1153 fn test_first_reset_and_sample_rate() {
1154 let mut first = SampleRateAware::new().first::<i32>();
1155
1156 first.tick((1.0, 0));
1157 first.reset();
1158 first.set_sample_rate(48000.0);
1159
1160 let (result, _) = first.tick((1.0, 0));
1161 assert!(result.abs() < 10.0);
1162 }
1163
1164 #[test]
1165 fn test_second() {
1166 let mut second = Gain { factor: 2.0 }.second::<i32>();
1167 let (a, b) = second.tick((42, 3.0));
1168 assert_eq!(a, 42);
1169 assert!((b - 6.0).abs() < 1e-10);
1170 }
1171
1172 #[test]
1173 fn test_second_reset_and_sample_rate() {
1174 let mut second = SampleRateAware::new().second::<i32>();
1175
1176 second.tick((0, 1.0));
1177 second.reset();
1178 second.set_sample_rate(48000.0);
1179
1180 let (_, result) = second.tick((0, 1.0));
1181 assert!(result.abs() < 10.0);
1182 }
1183
1184 #[test]
1185 fn test_identity_reset() {
1186 let mut id = Identity::<f64>::new();
1187 id.reset(); assert!((id.tick(42.0) - 42.0).abs() < 1e-10);
1189 }
1190
1191 #[test]
1192 fn test_identity_default() {
1193 let id: Identity<f64> = Identity::default();
1194 assert!(std::mem::size_of_val(&id) == 0);
1195 }
1196
1197 #[test]
1198 fn test_constant_reset() {
1199 let mut c = Constant::new(42.0_f64);
1200 c.reset(); assert!((c.tick(()) - 42.0).abs() < 1e-10);
1202 }
1203
1204 #[test]
1205 fn test_split_reset() {
1206 let mut split = Split::<f64>::new();
1207 split.reset(); }
1209
1210 #[test]
1211 fn test_split_default() {
1212 let split: Split<f64> = Split::default();
1213 let (a, b) = Split::<f64>::new().tick(1.0);
1214 assert!((a - 1.0).abs() < 1e-10);
1215 assert!((b - 1.0).abs() < 1e-10);
1216 let _ = split;
1217 }
1218
1219 #[test]
1220 fn test_merge_reset() {
1221 let mut merge = Merge::new(|a: f64, b: f64| a + b);
1222 merge.reset(); }
1224
1225 #[test]
1226 fn test_swap_reset() {
1227 let mut swap = Swap::<i32, f64>::new();
1228 swap.reset(); }
1230
1231 #[test]
1232 fn test_swap_default() {
1233 let swap: Swap<i32, f64> = Swap::default();
1234 let _ = swap;
1235 }
1236
1237 #[test]
1238 fn test_process_block() {
1239 let mut gain = Gain { factor: 2.0 };
1240 let input = vec![1.0, 2.0, 3.0, 4.0];
1241 let mut output = vec![0.0; 4];
1242 gain.process(&input, &mut output);
1243 assert_eq!(output, vec![2.0, 4.0, 6.0, 8.0]);
1244 }
1245
1246 struct Accum {
1253 sum: f64,
1254 }
1255
1256 impl Accum {
1257 fn new() -> Self {
1258 Self { sum: 0.0 }
1259 }
1260 }
1261
1262 impl Module for Accum {
1263 type In = f64;
1264 type Out = f64;
1265
1266 fn tick(&mut self, input: Self::In) -> Self::Out {
1267 self.sum += input;
1268 self.sum
1269 }
1270
1271 fn reset(&mut self) {
1272 self.sum = 0.0;
1273 }
1274 }
1275
1276 const SEQ: [f64; 8] = [0.5, -0.3, 1.0, 2.0, -1.5, 0.25, 3.0, -0.7];
1278
1279 fn run_mono<M: Module<In = f64, Out = f64>>(mut m: M) -> Vec<f64> {
1280 SEQ.iter().map(|&x| m.tick(x)).collect()
1281 }
1282
1283 fn run_pair<M: Module<In = (f64, f64), Out = (f64, f64)>>(mut m: M) -> Vec<(f64, f64)> {
1286 SEQ.iter()
1287 .enumerate()
1288 .map(|(i, &x)| m.tick((x, i as f64)))
1289 .collect()
1290 }
1291
1292 fn assert_seq_close(a: &[f64], b: &[f64]) {
1293 assert_eq!(a.len(), b.len());
1294 for (x, y) in a.iter().zip(b) {
1295 assert!((x - y).abs() < 1e-12, "sequence mismatch: {x} != {y}");
1296 }
1297 }
1298
1299 #[test]
1300 fn test_arr_basic() {
1301 let mut m = arr(|x: f64| x * 2.0 + 1.0);
1302 assert!((m.tick(3.0) - 7.0).abs() < 1e-12);
1303 m.reset();
1305 m.set_sample_rate(48_000.0);
1306 assert!((m.tick(0.0) - 1.0).abs() < 1e-12);
1307 }
1308
1309 #[test]
1310 fn arrow_law_identity() {
1311 let reference = run_mono(Accum::new());
1313 let left_id = run_mono(Identity::<f64>::new().then(Accum::new()));
1314 let right_id = run_mono(Accum::new().then(Identity::<f64>::new()));
1315 assert_seq_close(&left_id, &reference);
1316 assert_seq_close(&right_id, &reference);
1317 }
1318
1319 #[test]
1320 fn arrow_law_associativity() {
1321 let lhs = run_mono((Accum::new().then(Gain { factor: 2.0 })).then(Accum::new()));
1324 let rhs = run_mono(Accum::new().then(Gain { factor: 2.0 }.then(Accum::new())));
1325 assert_seq_close(&lhs, &rhs);
1326 }
1327
1328 #[test]
1329 fn arrow_law_first_distributes() {
1330 let lhs = run_pair(Accum::new().then(Gain { factor: 3.0 }).first::<f64>());
1332 let rhs = run_pair(
1333 Accum::new()
1334 .first::<f64>()
1335 .then(Gain { factor: 3.0 }.first::<f64>()),
1336 );
1337 assert_eq!(lhs.len(), rhs.len());
1338 for (a, b) in lhs.iter().zip(&rhs) {
1339 assert!((a.0 - b.0).abs() < 1e-12, "processed element differs");
1340 assert!((a.1 - b.1).abs() < 1e-12, "pass-through element differs");
1341 }
1342 for (i, pair) in lhs.iter().enumerate() {
1344 assert!((pair.1 - i as f64).abs() < 1e-12);
1345 }
1346 }
1347
1348 struct DoublerGm {
1354 spec: PortSpec,
1355 }
1356
1357 impl DoublerGm {
1358 fn new() -> Self {
1359 Self {
1360 spec: PortSpec {
1361 inputs: vec![PortDef::new(0, "in", SignalKind::Audio)],
1362 outputs: vec![PortDef::new(10, "out", SignalKind::Audio)],
1363 },
1364 }
1365 }
1366 }
1367
1368 impl GraphModule for DoublerGm {
1369 fn port_spec(&self) -> &PortSpec {
1370 &self.spec
1371 }
1372 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1373 outputs.set(10, inputs.get_or(0, 0.0) * 2.0);
1374 }
1375 fn reset(&mut self) {}
1376 fn set_sample_rate(&mut self, _sample_rate: f64) {}
1377 }
1378
1379 #[test]
1380 fn test_graph_module_adapter_drives_selected_ports() {
1381 let mut adapter = GraphModuleAdapter::new(DoublerGm::new(), 0, 10);
1383 assert!((adapter.tick(3.0) - 6.0).abs() < 1e-12);
1384 assert!((adapter.tick(-2.5) - (-5.0)).abs() < 1e-12);
1385 assert_eq!(adapter.inner().port_spec().outputs[0].id, 10);
1387 adapter.reset();
1389 adapter.set_sample_rate(48_000.0);
1390 }
1391
1392 struct GatedEmit {
1396 spec: PortSpec,
1397 }
1398 impl GatedEmit {
1399 fn new() -> Self {
1400 Self {
1401 spec: PortSpec {
1402 inputs: vec![PortDef::new(0, "trig", SignalKind::Audio)],
1403 outputs: vec![PortDef::new(10, "out", SignalKind::Audio)],
1404 },
1405 }
1406 }
1407 }
1408 impl GraphModule for GatedEmit {
1409 fn port_spec(&self) -> &PortSpec {
1410 &self.spec
1411 }
1412 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
1413 if inputs.get_or(0, 0.0) > 0.5 {
1415 outputs.set(10, 1.0);
1416 }
1417 }
1418 fn reset(&mut self) {}
1419 fn set_sample_rate(&mut self, _sample_rate: f64) {}
1420 }
1421
1422 #[test]
1427 fn test_graph_module_adapter_clears_stale_outputs() {
1428 let mut adapter = GraphModuleAdapter::new(GatedEmit::new(), 0, 10);
1429 assert!((adapter.tick(1.0) - 1.0).abs() < 1e-12);
1431 assert!(
1433 adapter.tick(0.0).abs() < 1e-12,
1434 "unwritten output must read as default 0.0, not the stale prior value"
1435 );
1436 assert!((adapter.tick(1.0) - 1.0).abs() < 1e-12);
1438 }
1439
1440 #[test]
1441 fn test_graph_module_adapter_from_audio_ports() {
1442 use crate::modules::{Svf, Vco};
1443 let svf = GraphModuleAdapter::from_audio_ports(Svf::new(44_100.0));
1445 assert!(svf.is_some());
1446 assert!(GraphModuleAdapter::from_audio_ports(Vco::new(44_100.0)).is_none());
1448 }
1449
1450 #[test]
1451 fn test_graph_module_adapter_vco_svf_chain_produces_audio() {
1452 use crate::modules::{Svf, Vco};
1453 let vco = GraphModuleAdapter::new(Vco::new(44_100.0), 0, 12); let svf = GraphModuleAdapter::from_audio_ports(Svf::new(44_100.0)).unwrap();
1457 let mut chain = vco.then(svf);
1458
1459 let mut peak = 0.0_f64;
1460 for _ in 0..512 {
1461 peak = peak.max(chain.tick(0.0).abs()); }
1463 assert!(
1464 peak > 1e-6,
1465 "expected nonzero audio from Vco -> Svf combinator chain, got {peak}"
1466 );
1467 }
1468
1469 #[test]
1470 fn test_module_graph_adapter_direct() {
1471 let mut node = ModuleGraphAdapter::new(arr(|x: f64| x * 3.0));
1473 assert_eq!(node.port_spec().inputs.len(), 1);
1474 assert_eq!(node.port_spec().outputs.len(), 1);
1475 assert_eq!(node.port_spec().inputs[0].id, 0);
1476 assert_eq!(node.port_spec().outputs[0].id, 10);
1477 assert_eq!(node.type_id(), "combinator_chain");
1478
1479 let mut inputs = PortValues::new();
1480 inputs.set(0, 2.0);
1481 let mut outputs = PortValues::new();
1482 node.tick(&inputs, &mut outputs);
1483 assert!((outputs.get_or(10, 0.0) - 6.0).abs() < 1e-12);
1484
1485 node.reset();
1486 node.set_sample_rate(48_000.0);
1487 }
1488
1489 #[test]
1490 fn test_module_graph_adapter_in_patch() {
1491 use crate::graph::Patch;
1492 use crate::modules::{StereoOutput, Vco};
1493
1494 let mut patch = Patch::new(44_100.0);
1496 let chain = arr(|x: f64| x * 0.5).then(arr(|x: f64| x + 0.1));
1497 let node = patch.add("chain", ModuleGraphAdapter::new(chain));
1498 let vco = patch.add("vco", Vco::new(44_100.0));
1499 let out = patch.add("out", StereoOutput::new());
1500
1501 patch.connect(vco.out("saw"), node.in_("in")).unwrap();
1502 patch.connect(node.out("out"), out.in_("left")).unwrap();
1503 patch.set_output(out.id());
1504 patch.compile().unwrap();
1505
1506 let mut peak = 0.0_f64;
1507 for _ in 0..512 {
1508 let (left, _right) = patch.tick();
1509 peak = peak.max(left.abs());
1510 }
1511 assert!(
1512 peak > 1e-6,
1513 "combinator chain wrapped as a GraphModule should tick inside a Patch, got {peak}"
1514 );
1515 }
1516}