1use crate::core::{resolve, ElemNode};
2use crate::graph::{Node, Value};
3
4fn empty_props() -> Value {
5 Value::Object(Default::default())
6}
7
8pub trait IntoNodeList {
13 fn into_nodes(self) -> Vec<ElemNode>;
14}
15
16impl<T> IntoNodeList for [T; 1]
17where
18 T: Into<ElemNode>,
19{
20 fn into_nodes(self) -> Vec<ElemNode> {
21 self.into_iter().map(Into::into).collect()
22 }
23}
24
25impl<T> IntoNodeList for [T; 2]
26where
27 T: Into<ElemNode>,
28{
29 fn into_nodes(self) -> Vec<ElemNode> {
30 self.into_iter().map(Into::into).collect()
31 }
32}
33
34impl<T> IntoNodeList for [T; 3]
35where
36 T: Into<ElemNode>,
37{
38 fn into_nodes(self) -> Vec<ElemNode> {
39 self.into_iter().map(Into::into).collect()
40 }
41}
42
43impl<T> IntoNodeList for [T; 4]
44where
45 T: Into<ElemNode>,
46{
47 fn into_nodes(self) -> Vec<ElemNode> {
48 self.into_iter().map(Into::into).collect()
49 }
50}
51
52macro_rules! impl_tuple_nodes {
53 ($($ty:ident => $var:ident),+) => {
54 impl<$($ty),+> IntoNodeList for ($($ty,)+)
55 where
56 $($ty: Into<ElemNode>,)+
57 {
58 fn into_nodes(self) -> Vec<ElemNode> {
59 let ($($var,)+) = self;
60 vec![$($var.into(),)+]
61 }
62 }
63 };
64}
65
66impl_tuple_nodes!(A => a, B => b);
67impl_tuple_nodes!(A => a, B => b, C => c);
68impl_tuple_nodes!(A => a, B => b, C => c, D => d);
69impl_tuple_nodes!(A => a, B => b, C => c, D => d, E => e);
70impl_tuple_nodes!(A => a, B => b, C => c, D => d, E => e, F => f);
71impl_tuple_nodes!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
72impl_tuple_nodes!(A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h);
73
74fn fold_node(items: impl IntoNodeList, kind: &str) -> Node {
75 Node::new(
76 kind,
77 Value::Null,
78 items.into_nodes().into_iter().map(resolve).collect(),
79 )
80}
81
82fn node0(kind: &str) -> Node {
83 Node::new(kind, Value::Null, vec![])
84}
85
86fn node1(kind: &str, child: impl Into<ElemNode>) -> Node {
87 Node::new(kind, Value::Null, vec![resolve(child)])
88}
89
90fn node2(kind: &str, left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
91 Node::new(kind, Value::Null, vec![resolve(left), resolve(right)])
92}
93
94fn node3(
95 kind: &str,
96 a: impl Into<ElemNode>,
97 b: impl Into<ElemNode>,
98 c: impl Into<ElemNode>,
99) -> Node {
100 Node::new(kind, Value::Null, vec![resolve(a), resolve(b), resolve(c)])
101}
102
103fn node6(
104 kind: &str,
105 a: impl Into<ElemNode>,
106 b: impl Into<ElemNode>,
107 c: impl Into<ElemNode>,
108 d: impl Into<ElemNode>,
109 e: impl Into<ElemNode>,
110 f: impl Into<ElemNode>,
111) -> Node {
112 Node::new(
113 kind,
114 Value::Null,
115 vec![
116 resolve(a),
117 resolve(b),
118 resolve(c),
119 resolve(d),
120 resolve(e),
121 resolve(f),
122 ],
123 )
124}
125
126fn node_props0(kind: &str, props: Value) -> Node {
127 Node::new(kind, props, vec![])
128}
129
130fn node_props1(kind: &str, props: Value, child: impl Into<ElemNode>) -> Node {
131 Node::new(kind, props, vec![resolve(child)])
132}
133
134fn node_props2(kind: &str, props: Value, a: impl Into<ElemNode>, b: impl Into<ElemNode>) -> Node {
135 Node::new(kind, props, vec![resolve(a), resolve(b)])
136}
137
138fn node_props3(
139 kind: &str,
140 props: Value,
141 a: impl Into<ElemNode>,
142 b: impl Into<ElemNode>,
143 c: impl Into<ElemNode>,
144) -> Node {
145 Node::new(kind, props, vec![resolve(a), resolve(b), resolve(c)])
146}
147
148fn node_props4(
149 kind: &str,
150 props: Value,
151 a: impl Into<ElemNode>,
152 b: impl Into<ElemNode>,
153 c: impl Into<ElemNode>,
154 d: impl Into<ElemNode>,
155) -> Node {
156 Node::new(
157 kind,
158 props,
159 vec![resolve(a), resolve(b), resolve(c), resolve(d)],
160 )
161}
162
163fn node_props_variadic<T>(kind: &str, props: Value, args: impl IntoIterator<Item = T>) -> Node
164where
165 T: Into<ElemNode>,
166{
167 Node::new(kind, props, args.into_iter().map(resolve).collect())
168}
169
170pub fn constant(props: Value) -> Node {
172 Node::new("const", props, vec![])
173}
174
175pub fn const_(value: f64) -> Node {
178 constant(serde_json::json!({ "value": value }))
179}
180
181pub fn const_with_key(key: &str, value: f64) -> Node {
183 constant(serde_json::json!({ "key": key, "value": value }))
184}
185
186pub fn r#const(props: Value) -> Node {
188 constant(props)
189}
190
191pub fn custom<T>(
193 kind: impl Into<String>,
194 props: Value,
195 children: impl IntoIterator<Item = T>,
196) -> Node
197where
198 T: Into<ElemNode>,
199{
200 Node::new(kind, props, children.into_iter().map(resolve).collect())
201}
202
203pub fn sr() -> Node {
205 node0("sr")
206}
207pub fn time() -> Node {
209 node0("time")
210}
211pub fn counter(g: impl Into<ElemNode>) -> Node {
218 node1("counter", g)
219}
220pub fn accum(xn: impl Into<ElemNode>, reset: impl Into<ElemNode>) -> Node {
227 node2("accum", xn, reset)
228}
229pub fn phasor(rate: impl Into<ElemNode>) -> Node {
236 node1("phasor", rate)
237}
238pub fn syncphasor(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
240 node2("sphasor", left, right)
241}
242pub fn sphasor(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
244 syncphasor(left, right)
245}
246pub fn latch(t: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
256 node2("latch", t, x)
257}
258pub fn maxhold(props: Value, a: impl Into<ElemNode>, b: impl Into<ElemNode>) -> Node {
261 node_props2("maxhold", props, a, b)
262}
263pub fn once(props: Value, child: impl Into<ElemNode>) -> Node {
266 node_props1("once", props, child)
267}
268pub fn rand(props: Option<Value>) -> Node {
276 Node::new("rand", props.unwrap_or_else(empty_props), vec![])
277}
278
279pub fn metro(props: Option<Value>) -> Node {
294 Node::new("metro", props.unwrap_or_else(empty_props), vec![])
295}
296pub fn tap_in(props: Value) -> Node {
298 node_props0("tapIn", props)
299}
300pub fn tap_out(props: Value, child: impl Into<ElemNode>) -> Node {
303 node_props1("tapOut", props, child)
304}
305pub fn meter(props: Value, child: impl Into<ElemNode>) -> Node {
308 node_props1("meter", props, child)
309}
310pub fn snapshot(props: Value, a: impl Into<ElemNode>, b: impl Into<ElemNode>) -> Node {
313 node_props2("snapshot", props, a, b)
314}
315pub fn scope<T>(props: Value, args: impl IntoIterator<Item = T>) -> Node
316where
317 T: Into<ElemNode>,
318{
319 node_props_variadic("scope", props, args)
320}
321pub fn fft(props: Value, child: impl Into<ElemNode>) -> Node {
324 node_props1("fft", props, child)
325}
326pub fn capture(props: Value, a: impl Into<ElemNode>, b: impl Into<ElemNode>) -> Node {
329 node_props2("capture", props, a, b)
330}
331pub fn table(props: Value, child: impl Into<ElemNode>) -> Node {
334 node_props1("table", props, child)
335}
336pub fn convolve(props: Value, child: impl Into<ElemNode>) -> Node {
339 node_props1("convolve", props, child)
340}
341pub fn seq(props: Value, trigger: impl Into<ElemNode>, reset: impl Into<ElemNode>) -> Node {
344 node_props2("seq", props, trigger, reset)
345}
346pub fn seq2(props: Value, trigger: impl Into<ElemNode>, reset: impl Into<ElemNode>) -> Node {
350 node_props2("seq2", props, trigger, reset)
351}
352pub fn sparseq(props: Value, trigger: impl Into<ElemNode>, reset: impl Into<ElemNode>) -> Node {
356 node_props2("sparseq", props, trigger, reset)
357}
358pub fn sparseq2(props: Value, child: impl Into<ElemNode>) -> Node {
361 node_props1("sparseq2", props, child)
362}
363pub fn sampleseq(props: Value, child: impl Into<ElemNode>) -> Node {
366 node_props1("sampleseq", props, child)
367}
368pub fn sampleseq2(props: Value, child: impl Into<ElemNode>) -> Node {
371 node_props1("sampleseq2", props, child)
372}
373pub fn pole(p: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
379 node2("pole", p, x)
380}
381pub fn env(
387 atk_pole: impl Into<ElemNode>,
388 rel_pole: impl Into<ElemNode>,
389 x: impl Into<ElemNode>,
390) -> Node {
391 node3("env", atk_pole, rel_pole, x)
392}
393pub fn z(child: impl Into<ElemNode>) -> Node {
395 node1("z", child)
396}
397pub fn delay(
400 props: Value,
401 a: impl Into<ElemNode>,
402 b: impl Into<ElemNode>,
403 c: impl Into<ElemNode>,
404) -> Node {
405 node_props3("delay", props, a, b, c)
406}
407pub fn sdelay(props: Value, child: impl Into<ElemNode>) -> Node {
410 node_props1("sdelay", props, child)
411}
412pub fn prewarp(child: impl Into<ElemNode>) -> Node {
414 node1("prewarp", child)
415}
416pub fn mm1p(props: Value, fc: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
419 node_props2("mm1p", props, fc, x)
420}
421pub fn svf(
424 props: Value,
425 fc: impl Into<ElemNode>,
426 q: impl Into<ElemNode>,
427 x: impl Into<ElemNode>,
428) -> Node {
429 node_props3("svf", props, fc, q, x)
430}
431pub fn svfshelf(
434 props: Value,
435 fc: impl Into<ElemNode>,
436 q: impl Into<ElemNode>,
437 gain: impl Into<ElemNode>,
438 x: impl Into<ElemNode>,
439) -> Node {
440 node_props4("svfshelf", props, fc, q, gain, x)
441}
442pub fn biquad(
444 b0: impl Into<ElemNode>,
445 b1: impl Into<ElemNode>,
446 b2: impl Into<ElemNode>,
447 a1: impl Into<ElemNode>,
448 a2: impl Into<ElemNode>,
449 x: impl Into<ElemNode>,
450) -> Node {
451 node6("biquad", b0, b1, b2, a1, a2, x)
452}
453
454pub fn sin(x: impl Into<ElemNode>) -> Node {
456 node1("sin", x)
457}
458
459pub fn cos(x: impl Into<ElemNode>) -> Node {
461 node1("cos", x)
462}
463
464pub fn tan(x: impl Into<ElemNode>) -> Node {
466 node1("tan", x)
467}
468
469pub fn tanh(x: impl Into<ElemNode>) -> Node {
471 node1("tanh", x)
472}
473
474pub fn asinh(x: impl Into<ElemNode>) -> Node {
476 node1("asinh", x)
477}
478
479pub fn ln(x: impl Into<ElemNode>) -> Node {
481 node1("ln", x)
482}
483
484pub fn log(x: impl Into<ElemNode>) -> Node {
486 node1("log", x)
487}
488
489pub fn log2(x: impl Into<ElemNode>) -> Node {
491 node1("log2", x)
492}
493
494pub fn ceil(x: impl Into<ElemNode>) -> Node {
496 node1("ceil", x)
497}
498
499pub fn floor(x: impl Into<ElemNode>) -> Node {
501 node1("floor", x)
502}
503
504pub fn round(x: impl Into<ElemNode>) -> Node {
506 node1("round", x)
507}
508
509pub fn sqrt(x: impl Into<ElemNode>) -> Node {
511 node1("sqrt", x)
512}
513
514pub fn exp(x: impl Into<ElemNode>) -> Node {
516 node1("exp", x)
517}
518
519pub fn abs(x: impl Into<ElemNode>) -> Node {
521 node1("abs", x)
522}
523
524pub fn le(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
526 node2("le", left, right)
527}
528
529pub fn leq(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
531 node2("leq", left, right)
532}
533
534pub fn ge(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
536 node2("ge", left, right)
537}
538
539pub fn geq(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
541 node2("geq", left, right)
542}
543
544pub fn pow(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
546 node2("pow", left, right)
547}
548
549pub fn eq(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
551 node2("eq", left, right)
552}
553
554pub fn and(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
556 node2("and", left, right)
557}
558
559pub fn or(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
561 node2("or", left, right)
562}
563
564pub fn add(items: impl IntoNodeList) -> Node {
566 fold_node(items, "add")
567}
568
569pub fn sub(items: impl IntoNodeList) -> Node {
571 fold_node(items, "sub")
572}
573
574pub fn mul(items: impl IntoNodeList) -> Node {
576 fold_node(items, "mul")
577}
578
579pub fn div(items: impl IntoNodeList) -> Node {
581 fold_node(items, "div")
582}
583
584pub fn r#mod(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
586 node2("mod", left, right)
587}
588
589pub fn min(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
591 node2("min", left, right)
592}
593
594pub fn max(left: impl Into<ElemNode>, right: impl Into<ElemNode>) -> Node {
596 node2("max", left, right)
597}
598
599pub fn identity(props: Value, x: Option<Node>) -> Node {
601 match x {
602 Some(x) => Node::new("in", props, vec![x]),
603 None => Node::new("in", props, vec![]),
604 }
605}
606
607pub fn r#in(props: Value, x: Option<Node>) -> Node {
609 identity(props, x)
610}
611
612pub fn cycle(rate: impl Into<ElemNode>) -> Node {
614 sin(mul([const_(2.0 * std::f64::consts::PI), phasor(rate)]))
615}
616
617pub fn train(rate: impl Into<ElemNode>) -> Node {
619 le(phasor(rate), const_(0.5))
620}
621
622pub fn saw(rate: impl Into<ElemNode>) -> Node {
624 sub([mul([const_(2.0), phasor(rate)]), const_(1.0)])
625}
626
627pub fn square(rate: impl Into<ElemNode>) -> Node {
629 sub([mul([const_(2.0), train(rate)]), const_(1.0)])
630}
631
632pub fn triangle(rate: impl Into<ElemNode>) -> Node {
634 mul([const_(2.0), sub([const_(0.5), abs(saw(rate))])])
635}
636
637pub fn blepsaw(rate: impl Into<ElemNode>) -> Node {
639 node1("blepsaw", rate)
640}
641
642pub fn blepsquare(rate: impl Into<ElemNode>) -> Node {
644 node1("blepsquare", rate)
645}
646
647pub fn bleptriangle(rate: impl Into<ElemNode>) -> Node {
649 node1("bleptriangle", rate)
650}
651
652pub fn noise(props: Option<Value>) -> Node {
654 sub([mul([const_(2.0), rand(props)]), const_(1.0)])
655}
656
657pub fn pinknoise(props: Option<Value>) -> Node {
659 pink(noise(props))
660}
661
662pub fn ms2samps(t: impl Into<ElemNode>) -> Node {
664 let t = resolve(t);
665 mul([sr(), div([t, const_(1000.0)])])
666}
667
668pub fn tau2pole(t: impl Into<ElemNode>) -> Node {
670 let t = resolve(t);
671 exp(div([const_(-1.0), mul([t, sr()])]))
672}
673
674pub fn db2gain(db: impl Into<ElemNode>) -> Node {
676 let db = resolve(db);
677 pow(const_(10.0), mul([db, const_(1.0 / 20.0)]))
678}
679
680pub fn gain2db(gain: impl Into<ElemNode>) -> Node {
682 let gain = resolve(gain);
683 select(
684 ge(gain.clone(), const_(0.0)),
685 max(const_(-120.0), mul([const_(20.0), log(gain)])),
686 const_(-120.0),
687 )
688}
689
690pub fn select(g: impl Into<ElemNode>, a: impl Into<ElemNode>, b: impl Into<ElemNode>) -> Node {
692 let g = resolve(g);
693 let a = resolve(a);
694 let b = resolve(b);
695 add([mul([g.clone(), a]), mul([sub([const_(1.0), g]), b])])
696}
697
698pub fn hann(t: impl Into<ElemNode>) -> Node {
700 let t = resolve(t);
701 mul([
702 const_(0.5),
703 sub([
704 const_(1.0),
705 cos(mul([const_(2.0 * std::f64::consts::PI), t])),
706 ]),
707 ])
708}
709
710pub fn smooth(p: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
712 let p = resolve(p);
713 let x = resolve(x);
714 pole(p.clone(), mul([sub([const_(1.0), p]), x]))
715}
716
717pub fn sm(x: impl Into<ElemNode>) -> Node {
719 smooth(tau2pole(const_(0.02)), x)
720}
721
722pub fn zero(b0: impl Into<ElemNode>, b1: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
724 let b0 = resolve(b0);
725 let b1 = resolve(b1);
726 let x = resolve(x);
727 sub([mul([b0, x.clone()]), mul([b1, z(x)])])
728}
729
730pub fn dcblock(x: impl Into<ElemNode>) -> Node {
732 let x = resolve(x);
733 pole(const_(0.995), zero(const_(1.0), const_(1.0), x))
734}
735
736pub fn df11(
738 b0: impl Into<ElemNode>,
739 b1: impl Into<ElemNode>,
740 a1: impl Into<ElemNode>,
741 x: impl Into<ElemNode>,
742) -> Node {
743 let b0 = resolve(b0);
744 let b1 = resolve(b1);
745 let a1 = resolve(a1);
746 let x = resolve(x);
747 pole(a1, zero(b0, b1, x))
748}
749
750pub fn lowpass(fc: impl Into<ElemNode>, q: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
753 let fc = resolve(fc);
754 let q = resolve(q);
755 let x = resolve(x);
756 svf(serde_json::json!({ "mode": "lowpass" }), fc, q, x)
757}
758
759pub fn highpass(fc: impl Into<ElemNode>, q: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
762 let fc = resolve(fc);
763 let q = resolve(q);
764 let x = resolve(x);
765 svf(serde_json::json!({ "mode": "highpass" }), fc, q, x)
766}
767
768pub fn bandpass(fc: impl Into<ElemNode>, q: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
771 let fc = resolve(fc);
772 let q = resolve(q);
773 let x = resolve(x);
774 svf(serde_json::json!({ "mode": "bandpass" }), fc, q, x)
775}
776
777pub fn notch(fc: impl Into<ElemNode>, q: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
780 let fc = resolve(fc);
781 let q = resolve(q);
782 let x = resolve(x);
783 svf(serde_json::json!({ "mode": "notch" }), fc, q, x)
784}
785
786pub fn allpass(fc: impl Into<ElemNode>, q: impl Into<ElemNode>, x: impl Into<ElemNode>) -> Node {
789 let fc = resolve(fc);
790 let q = resolve(q);
791 let x = resolve(x);
792 svf(serde_json::json!({ "mode": "allpass" }), fc, q, x)
793}
794
795pub fn peak(
798 fc: impl Into<ElemNode>,
799 q: impl Into<ElemNode>,
800 gain_decibels: impl Into<ElemNode>,
801 x: impl Into<ElemNode>,
802) -> Node {
803 let fc = resolve(fc);
804 let q = resolve(q);
805 let gain_decibels = resolve(gain_decibels);
806 let x = resolve(x);
807 svfshelf(
808 serde_json::json!({ "mode": "peak" }),
809 fc,
810 q,
811 gain_decibels,
812 x,
813 )
814}
815
816pub fn lowshelf(
819 fc: impl Into<ElemNode>,
820 q: impl Into<ElemNode>,
821 gain_decibels: impl Into<ElemNode>,
822 x: impl Into<ElemNode>,
823) -> Node {
824 let fc = resolve(fc);
825 let q = resolve(q);
826 let gain_decibels = resolve(gain_decibels);
827 let x = resolve(x);
828 svfshelf(
829 serde_json::json!({ "mode": "lowshelf" }),
830 fc,
831 q,
832 gain_decibels,
833 x,
834 )
835}
836
837pub fn highshelf(
840 fc: impl Into<ElemNode>,
841 q: impl Into<ElemNode>,
842 gain_decibels: impl Into<ElemNode>,
843 x: impl Into<ElemNode>,
844) -> Node {
845 let fc = resolve(fc);
846 let q = resolve(q);
847 let gain_decibels = resolve(gain_decibels);
848 let x = resolve(x);
849 svfshelf(
850 serde_json::json!({ "mode": "highshelf" }),
851 fc,
852 q,
853 gain_decibels,
854 x,
855 )
856}
857
858pub fn pink(x: impl Into<ElemNode>) -> Node {
860 let x = resolve(x);
861 let clip = |lower: Node, upper: Node, x: Node| min(upper, max(lower, x));
862
863 clip(
864 const_(-1.0),
865 const_(1.0),
866 mul([
867 db2gain(const_(-30.0)),
868 add([
869 pole(const_(0.99765), mul([x.clone(), const_(0.099046)])),
870 pole(const_(0.963), mul([x.clone(), const_(0.2965164)])),
871 pole(const_(0.57), mul([x.clone(), const_(1.0526913)])),
872 mul([const_(0.1848), x]),
873 ]),
874 ]),
875 )
876}
877
878pub fn adsr(
891 a: impl Into<ElemNode>,
892 d: impl Into<ElemNode>,
893 s: impl Into<ElemNode>,
894 r: impl Into<ElemNode>,
895 g: impl Into<ElemNode>,
896) -> Node {
897 let attack_sec = resolve(a);
898 let decay_sec = resolve(d);
899 let sustain = resolve(s);
900 let release_sec = resolve(r);
901 let gate = resolve(g);
902 let atk_samps = mul([attack_sec.clone(), sr()]);
903 let atk_gate = le(counter(gate.clone()), atk_samps);
904 let target_value = select(
905 gate.clone(),
906 select(atk_gate.clone(), const_(1.0), sustain.clone()),
907 const_(0.0),
908 );
909 let t60 = max(
910 const_(0.0001),
911 select(
912 gate.clone(),
913 select(atk_gate, attack_sec, decay_sec),
914 release_sec,
915 ),
916 );
917 let p = tau2pole(div([t60, const_(6.91)]));
918
919 smooth(p, target_value)
920}
921
922pub fn compress(
925 attack_ms: Node,
926 release_ms: Node,
927 threshold: Node,
928 ratio: Node,
929 sidechain: Node,
930 xn: Node,
931) -> Node {
932 let env = env(
933 tau2pole(mul([const_(0.001), attack_ms])),
934 tau2pole(mul([const_(0.001), release_ms])),
935 sidechain,
936 );
937
938 let env_decibels = gain2db(env);
939 let adjusted_ratio = sub([const_(1.0), div([const_(1.0), ratio])]);
940 let gain = mul([adjusted_ratio, sub([threshold, env_decibels])]);
941 let clean_gain = min(const_(0.0), gain);
942 let compressed_gain = db2gain(clean_gain);
943
944 mul([xn, compressed_gain])
945}
946
947pub fn skcompress(
950 attack_ms: Node,
951 release_ms: Node,
952 threshold: Node,
953 ratio: Node,
954 knee_width: Node,
955 sidechain: Node,
956 xn: Node,
957) -> Node {
958 let env = env(
959 tau2pole(mul([const_(0.001), attack_ms])),
960 tau2pole(mul([const_(0.001), release_ms])),
961 sidechain,
962 );
963
964 let env_decibels = gain2db(env);
965 let lower_knee_bound = sub([threshold.clone(), div([knee_width.clone(), const_(2.0)])]);
966 let upper_knee_bound = add([threshold.clone(), div([knee_width.clone(), const_(2.0)])]);
967 let is_in_soft_knee_range = and(
968 geq(env_decibels.clone(), lower_knee_bound.clone()),
969 leq(env_decibels.clone(), upper_knee_bound.clone()),
970 );
971 let adjusted_ratio = sub([const_(1.0), div([const_(1.0), ratio])]);
972 let gain = select(
973 is_in_soft_knee_range,
974 mul([
975 div([adjusted_ratio.clone(), const_(2.0)]),
976 mul([
977 div([
978 sub([env_decibels.clone(), lower_knee_bound.clone()]),
979 knee_width.clone(),
980 ]),
981 sub([lower_knee_bound.clone(), env_decibels.clone()]),
982 ]),
983 ]),
984 mul([adjusted_ratio, sub([threshold, env_decibels])]),
985 );
986 let clean_gain = min(const_(0.0), gain);
987 let compressed_gain = db2gain(clean_gain);
988
989 mul([xn, compressed_gain])
990}
991
992pub fn sample(props: Value, trigger: impl Into<ElemNode>, rate: impl Into<ElemNode>) -> Node {
997 Node::new("sample", props, vec![resolve(trigger), resolve(rate)])
998}