1use serde::{Deserialize, Serialize};
12
13use crate::term::{
14 quant_root_index, quant_scale_index, rect_mode_index, AudioNode, DriveMode, FilterKind,
15 ModNode, ModOp, NoiseColor, PatchTree, TableShape, Waveform, QUANT_ROOTS, QUANT_SCALES,
16 RECT_MODES,
17};
18
19#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
21#[serde(tag = "t", rename_all = "snake_case")]
22pub enum KnobKind {
23 Continuous,
25 Enum {
27 options: Vec<String>,
29 },
30 Octave,
32}
33
34#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
36pub struct Knob {
37 pub addr: String,
39 pub label: String,
41 pub value: f64,
43 pub kind: KnobKind,
45}
46
47#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
49pub struct RackModule {
50 pub key: String,
54 pub uid: u64,
66 pub kind: String,
68 pub title: String,
70 pub column: usize,
72 pub is_mod: bool,
82 pub knobs: Vec<Knob>,
84 pub structural_addrs: Vec<String>,
89}
90
91#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
93pub struct Wire {
94 pub from: String,
96 pub to: String,
98 pub kind: String,
100}
101
102#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
104pub struct RackDescription {
105 pub modules: Vec<RackModule>,
107 pub wires: Vec<Wire>,
109}
110
111pub fn waveform_options() -> Vec<String> {
113 Waveform::ALL.iter().map(|w| w.port_name().into()).collect()
114}
115
116pub fn noise_options() -> Vec<String> {
118 NoiseColor::ALL
119 .iter()
120 .map(|c| c.port_name().into())
121 .collect()
122}
123
124pub fn filter_options() -> Vec<String> {
126 ["svf lp", "svf bp", "svf hp", "ladder"]
127 .iter()
128 .map(|s| s.to_string())
129 .collect()
130}
131
132pub fn table_options() -> Vec<String> {
134 TableShape::ALL.iter().map(|t| t.label().into()).collect()
135}
136
137pub fn drive_mode_options() -> Vec<String> {
139 DriveMode::ALL.iter().map(|m| m.label().into()).collect()
140}
141
142fn knob_c(key: &str, site: &str, label: &str, value: f64) -> Knob {
143 Knob {
144 addr: format!("{key}#{site}"),
145 label: label.into(),
146 value,
147 kind: KnobKind::Continuous,
148 }
149}
150
151fn knob_e(key: &str, site: &str, label: &str, index: usize, options: Vec<String>) -> Knob {
152 Knob {
153 addr: format!("{key}#{site}"),
154 label: label.into(),
155 value: index as f64,
156 kind: KnobKind::Enum { options },
157 }
158}
159
160fn knob_oct(key: &str, octave: i8) -> Knob {
161 Knob {
162 addr: format!("{key}#oct"),
163 label: "octave".into(),
164 value: (octave + 2) as f64,
165 kind: KnobKind::Octave,
166 }
167}
168
169fn describe_mod(
178 m: &ModNode,
179 key: &str,
180 parent_key: &str,
181 column: usize,
182 out: &mut RackDescription,
183 parent_structural: &mut Vec<String>,
184) {
185 let mut structural = vec![format!("{key}#mod")];
186 match m {
189 ModNode::None => {
190 parent_structural.push(format!("{key}#mod"));
193 return;
194 }
195 ModNode::Op {
196 kind,
197 p0,
198 p1,
199 input,
200 ..
201 } => {
202 structural.push(format!("{key}#modop"));
203 let sites = kind.param_sites();
204 let knobs = match kind {
205 ModOp::Quantize => vec![
206 knob_c(
213 key,
214 "qroot",
215 &format!("root · {}", QUANT_ROOTS[quant_root_index(*p0)]),
216 *p0,
217 ),
218 knob_c(
219 key,
220 "qscale",
221 &format!("scale · {}", QUANT_SCALES[quant_scale_index(*p1)]),
222 *p1,
223 ),
224 ],
225 ModOp::Slew => vec![
226 knob_c(key, "rise", "rise", *p0),
227 knob_c(key, "fall", "fall", *p1),
228 ],
229 ModOp::Rectify => vec![knob_c(
230 key,
231 "rmode",
232 &format!("mode · {}", RECT_MODES[rect_mode_index(*p0)]),
233 *p0,
234 )],
235 ModOp::Hold => vec![knob_c(key, sites[0], "rate", *p0)],
236 };
237 out.modules.push(RackModule {
238 key: key.into(),
239 uid: m.uid().map_or(0, |u| u.0),
240 kind: kind.label().into(),
241 title: kind.label().into(),
242 column,
243 is_mod: true,
244 knobs,
245 structural_addrs: structural,
246 });
247 out.wires.push(Wire {
248 from: key.into(),
249 to: parent_key.into(),
250 kind: "mod".into(),
251 });
252 let child = format!("{key}/0");
253 out.wires.push(Wire {
254 from: child.clone(),
255 to: key.into(),
256 kind: "mod".into(),
257 });
258 let mut ignored = Vec::new();
259 describe_mod(input, &child, key, column + 1, out, &mut ignored);
260 return;
261 }
262 ModNode::Pair { kind, a, b, .. } => {
263 structural.push(format!("{key}#pairop"));
264 out.modules.push(RackModule {
265 key: key.into(),
266 uid: m.uid().map_or(0, |u| u.0),
267 kind: kind.label().into(),
268 title: kind.label().into(),
269 column,
270 is_mod: true,
271 knobs: Vec::new(),
272 structural_addrs: structural,
273 });
274 out.wires.push(Wire {
275 from: key.into(),
276 to: parent_key.into(),
277 kind: "mod".into(),
278 });
279 let (ka, kb) = (format!("{key}/0"), format!("{key}/1"));
280 for k in [&ka, &kb] {
281 out.wires.push(Wire {
282 from: k.clone(),
283 to: key.into(),
284 kind: "mod".into(),
285 });
286 }
287 let mut ignored = Vec::new();
288 describe_mod(a, &ka, key, column + 1, out, &mut ignored);
289 describe_mod(b, &kb, key, column + 1, out, &mut ignored);
290 return;
291 }
292 _ => {}
293 }
294 let (kind, title, knobs) = match m {
295 ModNode::None | ModNode::Op { .. } | ModNode::Pair { .. } => return,
297 ModNode::Lfo { wave, rate, .. } => (
298 "lfo",
299 "lfo",
300 vec![
301 knob_e(key, "wave", "wave", wave.index(), waveform_options()),
302 knob_c(key, "rate", "rate", *rate),
303 ],
304 ),
305 ModNode::Env { attack, decay, .. } => (
306 "modenv",
307 "mod env",
308 vec![
309 knob_c(key, "att", "attack", *attack),
310 knob_c(key, "dec", "decay", *decay),
311 ],
312 ),
313 ModNode::Rand { rate, glide, .. } => (
314 "rand",
315 "s&h rand",
316 vec![
317 knob_c(key, "rate", "rate", *rate),
318 knob_c(key, "glide", "glide", *glide),
319 ],
320 ),
321 ModNode::Follow { sens, release, .. } => (
322 "follow",
323 "follower",
324 vec![
325 knob_c(key, "sens", "sens", *sens),
326 knob_c(key, "rel", "release", *release),
327 ],
328 ),
329 ModNode::Euclid {
330 rate,
331 steps,
332 pulses,
333 ..
334 } => (
335 "euclid",
336 "euclid",
337 vec![
338 knob_c(key, "erate", "rate", *rate),
339 knob_c(key, "esteps", "steps", *steps),
340 knob_c(key, "epulses", "pulses", *pulses),
341 ],
342 ),
343 };
344 out.modules.push(RackModule {
345 key: key.into(),
346 uid: m.uid().map_or(0, |u| u.0),
347 kind: kind.into(),
348 title: title.into(),
349 column,
350 is_mod: true,
351 knobs,
352 structural_addrs: structural,
353 });
354 out.wires.push(Wire {
355 from: key.into(),
356 to: parent_key.into(),
357 kind: "mod".into(),
358 });
359}
360
361fn push_modulated(
370 out: &mut RackDescription,
371 mut module: RackModule,
372 input: Option<&AudioNode>,
373 modulation: &ModNode,
374) {
375 let key = module.key.clone();
376 let column = module.column;
377 let mut structural = std::mem::take(&mut module.structural_addrs);
378 let idx = out.modules.len();
379 out.modules.push(module);
380
381 let child = format!("{key}/0");
382 if input.is_some() {
383 out.wires.push(Wire {
384 from: child.clone(),
385 to: key.clone(),
386 kind: "audio".into(),
387 });
388 }
389 describe_mod(
390 modulation,
391 &format!("{key}/m"),
392 &key,
393 column + 1,
394 out,
395 &mut structural,
396 );
397 out.modules[idx].structural_addrs = structural;
398 if let Some(input) = input {
399 describe_node(input, &child, column + 1, out);
400 }
401}
402
403fn push_binary(out: &mut RackDescription, module: RackModule, a: &AudioNode, b: &AudioNode) {
405 push_binary_with(out, module, a, b, None);
406}
407
408fn push_binary_modulated(
417 out: &mut RackDescription,
418 module: RackModule,
419 a: &AudioNode,
420 b: &AudioNode,
421 modulation: &ModNode,
422) {
423 push_binary_with(out, module, a, b, Some(modulation));
424}
425
426fn push_binary_with(
427 out: &mut RackDescription,
428 mut module: RackModule,
429 a: &AudioNode,
430 b: &AudioNode,
431 modulation: Option<&ModNode>,
432) {
433 let (key, column) = (module.key.clone(), module.column);
434 let mut structural = std::mem::take(&mut module.structural_addrs);
435 let idx = out.modules.len();
436 out.modules.push(module);
437 let (ka, kb) = (format!("{key}/0"), format!("{key}/1"));
438 for k in [&ka, &kb] {
439 out.wires.push(Wire {
440 from: k.clone(),
441 to: key.clone(),
442 kind: "audio".into(),
443 });
444 }
445 if let Some(m) = modulation {
450 describe_mod(
451 m,
452 &format!("{key}/m"),
453 &key,
454 column + 1,
455 out,
456 &mut structural,
457 );
458 }
459 out.modules[idx].structural_addrs = structural;
460 describe_node(a, &ka, column + 1, out);
461 describe_node(b, &kb, column + 1, out);
462}
463
464fn describe_node(n: &AudioNode, key: &str, column: usize, out: &mut RackDescription) {
465 let leaf_src = vec![format!("{key}#leaf"), format!("{key}#src")];
466 let leaf_op = vec![format!("{key}#leaf"), format!("{key}#op")];
467 let module = |kind: &str, title: &str, knobs: Vec<Knob>, structural: Vec<String>| RackModule {
469 key: key.into(),
470 uid: n.uid().0,
471 kind: kind.into(),
472 title: title.into(),
473 column,
474 is_mod: false,
475 knobs,
476 structural_addrs: structural,
477 };
478 match n {
479 AudioNode::Vco {
480 wave,
481 octave,
482 detune,
483 mod_depth,
484 modulation,
485 ..
486 } => push_modulated(
487 out,
488 module(
489 "vco",
490 "vco",
491 vec![
492 knob_e(key, "wave", "wave", wave.index(), waveform_options()),
493 knob_oct(key, *octave),
494 knob_c(key, "det", "detune", *detune),
495 knob_c(key, "mdepth", "mod depth", *mod_depth),
496 ],
497 leaf_src,
498 ),
499 None,
502 modulation,
503 ),
504 AudioNode::Supersaw {
505 octave,
506 detune,
507 mix,
508 mod_depth,
509 modulation,
510 ..
511 } => push_modulated(
512 out,
513 module(
514 "supersaw",
515 "supersaw",
516 vec![
517 knob_oct(key, *octave),
518 knob_c(key, "det", "detune", *detune),
519 knob_c(key, "smix", "mix", *mix),
520 knob_c(key, "mdepth", "mod depth", *mod_depth),
521 ],
522 leaf_src,
523 ),
524 None,
525 modulation,
526 ),
527 AudioNode::Formant {
528 vowel,
529 shift,
530 octave,
531 mod_depth,
532 modulation,
533 ..
534 } => push_modulated(
535 out,
536 module(
537 "formant",
538 "formant",
539 vec![
540 knob_c(key, "vowel", "vowel", *vowel),
541 knob_c(key, "fshift", "shift", *shift),
542 knob_oct(key, *octave),
543 knob_c(key, "mdepth", "mod depth", *mod_depth),
544 ],
545 leaf_src,
546 ),
547 None,
548 modulation,
549 ),
550 AudioNode::Noise { color, .. } => out.modules.push(module(
551 "noise",
552 "noise",
553 vec![knob_e(
554 key,
555 "color",
556 "color",
557 color.index(),
558 noise_options(),
559 )],
560 leaf_src,
561 )),
562 AudioNode::Silence { .. } => {
567 out.modules
568 .push(module("silence", "empty", Vec::new(), leaf_src))
569 }
570 AudioNode::Wavetable {
571 table,
572 octave,
573 morph,
574 mod_depth,
575 modulation,
576 ..
577 } => push_modulated(
578 out,
579 module(
580 "wavetable",
581 "wavetable",
582 vec![
583 knob_e(key, "table", "table", table.index(), table_options()),
584 knob_oct(key, *octave),
585 knob_c(key, "morph", "morph", *morph),
586 knob_c(key, "mdepth", "mod depth", *mod_depth),
587 ],
588 leaf_src,
589 ),
590 None,
592 modulation,
593 ),
594 AudioNode::Pluck {
595 octave,
596 damping,
597 brightness,
598 mod_depth,
599 modulation,
600 ..
601 } => push_modulated(
602 out,
603 module(
604 "pluck",
605 "pluck",
606 vec![
607 knob_oct(key, *octave),
608 knob_c(key, "damp", "decay", *damping),
616 knob_c(key, "bright", "brightness", *brightness),
617 knob_c(key, "mdepth", "mod depth", *mod_depth),
618 ],
619 leaf_src,
620 ),
621 None,
622 modulation,
623 ),
624 AudioNode::Mix { balance, a, b, .. } => push_binary(
625 out,
626 module(
627 "mix",
628 "mix",
629 vec![knob_c(key, "bal", "balance", *balance)],
630 leaf_op,
631 ),
632 a,
633 b,
634 ),
635 AudioNode::RingMod { mix, a, b, .. } => push_binary(
636 out,
637 module(
638 "ringmod",
639 "ring mod",
640 vec![knob_c(key, "rgmix", "mix", *mix)],
641 leaf_op,
642 ),
643 a,
644 b,
645 ),
646 AudioNode::Filter {
647 kind,
648 cutoff,
649 resonance,
650 mod_depth,
651 input,
652 modulation,
653 ..
654 } => push_modulated(
655 out,
656 module(
657 "filter",
658 match kind {
662 FilterKind::Ladder => "ladder",
663 _ => "filter",
664 },
665 vec![
666 knob_e(key, "fkind", "mode", kind.index(), filter_options()),
667 knob_c(key, "cut", "cutoff", *cutoff),
668 knob_c(key, "res", "resonance", *resonance),
669 knob_c(key, "mdepth", "mod depth", *mod_depth),
670 ],
671 leaf_op,
672 ),
673 Some(input),
674 modulation,
675 ),
676 AudioNode::Fold {
677 threshold,
678 mod_depth,
679 input,
680 modulation,
681 ..
682 } => push_modulated(
683 out,
684 module(
685 "fold",
686 "wavefolder",
687 vec![
688 knob_c(key, "thresh", "fold", *threshold),
689 knob_c(key, "mdepth", "mod depth", *mod_depth),
690 ],
691 leaf_op,
692 ),
693 Some(input),
694 modulation,
695 ),
696 AudioNode::Delay {
697 time,
698 feedback,
699 mix,
700 mod_depth,
701 input,
702 modulation,
703 ..
704 } => push_modulated(
705 out,
706 module(
707 "delay",
708 "delay",
709 vec![
710 knob_c(key, "time", "time", *time),
711 knob_c(key, "fb", "feedback", *feedback),
712 knob_c(key, "dmix", "mix", *mix),
713 knob_c(key, "mdepth", "mod depth", *mod_depth),
714 ],
715 leaf_op,
716 ),
717 Some(input),
718 modulation,
719 ),
720 AudioNode::Chorus {
721 rate,
722 depth,
723 mix,
724 mod_depth,
725 input,
726 modulation,
727 ..
728 } => push_modulated(
729 out,
730 module(
731 "chorus",
732 "chorus",
733 vec![
734 knob_c(key, "crate", "rate", *rate),
735 knob_c(key, "cdepth", "depth", *depth),
736 knob_c(key, "cmix", "mix", *mix),
737 knob_c(key, "mdepth", "mod depth", *mod_depth),
738 ],
739 leaf_op,
740 ),
741 Some(input),
742 modulation,
743 ),
744 AudioNode::Reverb {
745 size,
746 damp,
747 mix,
748 mod_depth,
749 input,
750 modulation,
751 ..
752 } => push_modulated(
753 out,
754 module(
755 "reverb",
756 "reverb",
757 vec![
758 knob_c(key, "rsize", "size", *size),
759 knob_c(key, "rdamp", "damp", *damp),
760 knob_c(key, "rmix", "mix", *mix),
761 knob_c(key, "mdepth", "mod depth", *mod_depth),
762 ],
763 leaf_op,
764 ),
765 Some(input),
766 modulation,
767 ),
768 AudioNode::Distortion {
769 drive,
770 tone,
771 mode,
772 mod_depth,
773 input,
774 modulation,
775 ..
776 } => push_modulated(
777 out,
778 module(
779 "distortion",
780 "distortion",
781 vec![
782 knob_c(key, "drive", "drive", *drive),
783 knob_c(key, "tone", "tone", *tone),
784 knob_e(key, "dmode", "mode", mode.index(), drive_mode_options()),
785 knob_c(key, "mdepth", "mod depth", *mod_depth),
786 ],
787 leaf_op,
788 ),
789 Some(input),
790 modulation,
791 ),
792 AudioNode::Bitcrush {
793 bits,
794 downsample,
795 mod_depth,
796 input,
797 modulation,
798 ..
799 } => push_modulated(
800 out,
801 module(
802 "bitcrush",
803 "bitcrush",
804 vec![
805 knob_c(key, "bits", "bits", *bits),
806 knob_c(key, "dsamp", "rate", *downsample),
807 knob_c(key, "mdepth", "mod depth", *mod_depth),
808 ],
809 leaf_op,
810 ),
811 Some(input),
812 modulation,
813 ),
814 AudioNode::Phaser {
815 rate,
816 depth,
817 feedback,
818 mod_depth,
819 input,
820 modulation,
821 ..
822 } => push_modulated(
823 out,
824 module(
825 "phaser",
826 "phaser",
827 vec![
828 knob_c(key, "prate", "rate", *rate),
829 knob_c(key, "pdepth", "depth", *depth),
830 knob_c(key, "pfb", "feedback", *feedback),
831 knob_c(key, "mdepth", "mod depth", *mod_depth),
832 ],
833 leaf_op,
834 ),
835 Some(input),
836 modulation,
837 ),
838 AudioNode::Flanger {
839 rate,
840 depth,
841 feedback,
842 mod_depth,
843 input,
844 modulation,
845 ..
846 } => push_modulated(
847 out,
848 module(
849 "flanger",
850 "flanger",
851 vec![
852 knob_c(key, "frate", "rate", *rate),
853 knob_c(key, "fdepth", "depth", *depth),
854 knob_c(key, "ffb", "feedback", *feedback),
855 knob_c(key, "mdepth", "mod depth", *mod_depth),
856 ],
857 leaf_op,
858 ),
859 Some(input),
860 modulation,
861 ),
862 AudioNode::Tremolo {
863 rate,
864 depth,
865 shape,
866 mod_depth,
867 input,
868 modulation,
869 ..
870 } => push_modulated(
871 out,
872 module(
873 "tremolo",
874 "tremolo",
875 vec![
876 knob_c(key, "trate", "rate", *rate),
877 knob_c(key, "tdepth", "depth", *depth),
878 knob_c(key, "tshape", "shape", *shape),
879 knob_c(key, "mdepth", "mod depth", *mod_depth),
880 ],
881 leaf_op,
882 ),
883 Some(input),
884 modulation,
885 ),
886 AudioNode::Vibrato {
887 rate,
888 depth,
889 mix,
890 mod_depth,
891 input,
892 modulation,
893 ..
894 } => push_modulated(
895 out,
896 module(
897 "vibrato",
898 "vibrato",
899 vec![
900 knob_c(key, "vrate", "rate", *rate),
901 knob_c(key, "vdepth", "depth", *depth),
902 knob_c(key, "vmix", "mix", *mix),
903 knob_c(key, "mdepth", "mod depth", *mod_depth),
904 ],
905 leaf_op,
906 ),
907 Some(input),
908 modulation,
909 ),
910 AudioNode::Eq {
911 low,
912 mid,
913 high,
914 mod_depth,
915 input,
916 modulation,
917 ..
918 } => push_modulated(
919 out,
920 module(
921 "eq",
922 "eq",
923 vec![
924 knob_c(key, "low", "low", *low),
925 knob_c(key, "mid", "mid", *mid),
926 knob_c(key, "high", "high", *high),
927 knob_c(key, "mdepth", "mod depth", *mod_depth),
928 ],
929 leaf_op,
930 ),
931 Some(input),
932 modulation,
933 ),
934 AudioNode::Granular {
935 position,
936 size,
937 density,
938 mod_depth,
939 input,
940 modulation,
941 ..
942 } => push_modulated(
943 out,
944 module(
945 "granular",
946 "granular",
947 vec![
948 knob_c(key, "gpos", "position", *position),
949 knob_c(key, "gsize", "size", *size),
950 knob_c(key, "gdens", "density", *density),
951 knob_c(key, "mdepth", "mod depth", *mod_depth),
952 ],
953 leaf_op,
954 ),
955 Some(input),
956 modulation,
957 ),
958 AudioNode::Shift {
959 semis,
960 window,
961 mix,
962 mod_depth,
963 input,
964 modulation,
965 ..
966 } => push_modulated(
967 out,
968 module(
969 "shift",
970 "pitch shift",
971 vec![
972 knob_c(key, "semis", "shift", *semis),
973 knob_c(key, "window", "window", *window),
974 knob_c(key, "smix", "mix", *mix),
975 knob_c(key, "mdepth", "mod depth", *mod_depth),
976 ],
977 leaf_op,
978 ),
979 Some(input),
980 modulation,
981 ),
982 AudioNode::Comp {
987 threshold,
988 ratio,
989 makeup,
990 mod_depth,
991 input,
992 sidechain,
993 modulation,
994 ..
995 } => push_binary_modulated(
996 out,
997 module(
998 "comp",
999 "compressor",
1000 vec![
1001 knob_c(key, "thresh", "threshold", *threshold),
1002 knob_c(key, "ratio", "ratio", *ratio),
1003 knob_c(key, "makeup", "makeup", *makeup),
1004 knob_c(key, "mdepth", "mod depth", *mod_depth),
1005 ],
1006 leaf_op,
1007 ),
1008 input,
1009 sidechain,
1010 modulation,
1011 ),
1012 AudioNode::Duck {
1013 amount,
1014 threshold,
1015 release,
1016 mod_depth,
1017 input,
1018 key: key_input,
1019 modulation,
1020 ..
1021 } => push_binary_modulated(
1022 out,
1023 module(
1024 "duck",
1025 "ducker",
1026 vec![
1027 knob_c(key, "amount", "amount", *amount),
1028 knob_c(key, "dthresh", "threshold", *threshold),
1029 knob_c(key, "drel", "release", *release),
1030 knob_c(key, "mdepth", "mod depth", *mod_depth),
1031 ],
1032 leaf_op,
1033 ),
1034 input,
1035 key_input,
1036 modulation,
1037 ),
1038 AudioNode::Gate {
1039 threshold,
1040 range,
1041 release,
1042 mod_depth,
1043 input,
1044 sidechain,
1045 modulation,
1046 ..
1047 } => push_binary_modulated(
1048 out,
1049 module(
1050 "gate",
1051 "gate",
1052 vec![
1053 knob_c(key, "gthresh", "threshold", *threshold),
1054 knob_c(key, "range", "range", *range),
1055 knob_c(key, "grel", "release", *release),
1056 knob_c(key, "mdepth", "mod depth", *mod_depth),
1057 ],
1058 leaf_op,
1059 ),
1060 input,
1061 sidechain,
1062 modulation,
1063 ),
1064 AudioNode::Vocoder {
1065 bands,
1066 attack,
1067 release,
1068 mod_depth,
1069 carrier,
1070 modulator,
1071 modulation,
1072 ..
1073 } => push_binary_modulated(
1074 out,
1075 module(
1076 "vocoder",
1077 "vocoder",
1078 vec![
1079 knob_c(key, "bands", "bands", *bands),
1080 knob_c(key, "vatt", "attack", *attack),
1081 knob_c(key, "vrel", "release", *release),
1082 knob_c(key, "mdepth", "mod depth", *mod_depth),
1083 ],
1084 leaf_op,
1085 ),
1086 carrier,
1087 modulator,
1088 modulation,
1089 ),
1090 }
1091}
1092
1093pub fn describe(tree: &PatchTree) -> RackDescription {
1098 let mut out = RackDescription {
1099 modules: Vec::new(),
1100 wires: Vec::new(),
1101 };
1102 out.modules.push(RackModule {
1103 key: "amp".into(),
1104 uid: 0,
1105 kind: "amp".into(),
1106 title: "env / out".into(),
1107 column: 0,
1108 is_mod: false,
1109 knobs: vec![
1110 knob_c("amp", "attack", "attack", tree.amp.attack),
1111 knob_c("amp", "decay", "decay", tree.amp.decay),
1112 knob_c("amp", "sustain", "sustain", tree.amp.sustain),
1113 knob_c("amp", "release", "release", tree.amp.release),
1114 ],
1115 structural_addrs: Vec::new(),
1116 });
1117 out.wires.push(Wire {
1118 from: "node".into(),
1119 to: "amp".into(),
1120 kind: "audio".into(),
1121 });
1122 describe_node(&tree.root, "node", 1, &mut out);
1123 out
1124}