1use sim_kernel::Expr;
4use sim_lib_music_synth::{
5 INSTRUMENT_EDITOR_DESCRIPTORS, InstrumentEditorDescriptor, SYSTEM55_EDITOR_FIXTURE_NAMES,
6 SYSTEM55_EDITOR_ROUTE_NAME, SYSTEM55_EDITOR_VIEW_ID,
7};
8use sim_lib_scene::{node, sym};
9
10use crate::instrument::{action, editor_root, graph, grid, number, row, table, validation_panel};
11
12pub const SYSTEM55_EDITOR_ROUTE: &str = SYSTEM55_EDITOR_ROUTE_NAME;
14pub const SYSTEM55_EDITOR_VIEW: &str = SYSTEM55_EDITOR_VIEW_ID;
16
17pub fn system55_editor_fixture_names() -> &'static [&'static str] {
19 &SYSTEM55_EDITOR_FIXTURE_NAMES
20}
21
22pub fn system55_editor_snapshot(name: &str) -> Option<Expr> {
24 SYSTEM55_EDITOR_FIXTURE_NAMES
25 .contains(&name)
26 .then(|| system55_editor_view(name))
27}
28
29pub fn system55_editor_view(fixture_name: &str) -> Expr {
31 let invalid = fixture_name == SYSTEM55_EDITOR_FIXTURE_NAMES[2];
32 let empty = fixture_name == SYSTEM55_EDITOR_FIXTURE_NAMES[1];
33 let representative = fixture_name == SYSTEM55_EDITOR_FIXTURE_NAMES[3];
34 let mut children = vec![
35 action("cabinet-load", "load-cabinet", "Load cabinet"),
36 cabinet_rows(empty, representative),
37 cabinet_graph(empty, representative),
38 filter_bank(representative),
39 s_trigger_panel(),
40 ];
41 if invalid {
42 children.push(validation_panel("s-trigger-gate-mismatch", "m55-911"));
43 children.push(validation_panel("cabinet-row-overflow", "top-cabinet"));
44 }
45 editor_root(
46 descriptor(),
47 "system55-cabinet-editor",
48 fixture_name,
49 children,
50 )
51}
52
53fn descriptor() -> &'static InstrumentEditorDescriptor {
54 INSTRUMENT_EDITOR_DESCRIPTORS
55 .iter()
56 .find(|descriptor| descriptor.instrument == "system55")
57 .expect("System 55 editor descriptor")
58}
59
60fn cabinet_rows(empty: bool, representative: bool) -> Expr {
61 let rows = if empty {
62 Vec::new()
63 } else {
64 let mut rows = vec![
65 module_row("m55-921a", "oscillator-driver", "top"),
66 module_row("m55-921b-a", "oscillator", "top"),
67 module_row("m55-904a", "low-pass-filter", "middle"),
68 module_row("m55-902", "vca", "middle"),
69 module_row("m55-911", "envelope", "bottom"),
70 ];
71 if representative {
72 rows.push(module_row("m55-907", "fixed-filter-bank", "middle"));
73 rows.push(module_row("m55-960", "sequential-controller", "bottom"));
74 }
75 rows
76 };
77 table("cabinet-row", rows)
78}
79
80fn cabinet_graph(empty: bool, representative: bool) -> Expr {
81 if empty {
82 return graph("system55-cabinet-graph", &[], &[]);
83 }
84 let mut nodes = vec![
85 "m55-921a",
86 "m55-921b-a",
87 "m55-904a",
88 "m55-902",
89 "m55-911",
90 "out",
91 ];
92 let mut edges = vec![
93 ("m55-921a", "m55-921b-a"),
94 ("m55-921b-a", "m55-904a"),
95 ("m55-904a", "m55-902"),
96 ("m55-902", "out"),
97 ("m55-911", "m55-902"),
98 ];
99 if representative {
100 nodes.extend(["m55-907", "m55-960"]);
101 edges.push(("m55-904a", "m55-907"));
102 edges.push(("m55-960", "m55-921a"));
103 }
104 graph("system55-cabinet-graph", &nodes, &edges)
105}
106
107fn filter_bank(representative: bool) -> Expr {
108 let gain = if representative { 1.0 } else { 0.4 };
109 grid(
110 "fixed-filter-bank-editor",
111 &[vec![gain, 0.2, -0.5, 0.8], vec![0.1, -0.3, 0.6, -0.2]],
112 &["low-bands", "high-bands"],
113 &["50hz", "120hz", "400hz", "1k2"],
114 )
115}
116
117fn s_trigger_panel() -> Expr {
118 node(
119 "stack",
120 vec![
121 ("role", sym("s-trigger-panel")),
122 ("dir", sym("row")),
123 (
124 "children",
125 Expr::List(vec![
126 action(
127 "s-trigger-convert",
128 "convert-s-trigger",
129 "Convert S-trigger",
130 ),
131 table(
132 "s-trigger-map",
133 vec![row(vec![
134 ("source", Expr::String("gate".to_owned())),
135 ("target", Expr::String("s-trigger".to_owned())),
136 ("inverted", Expr::Bool(true)),
137 ])],
138 ),
139 ]),
140 ),
141 ],
142 )
143}
144
145fn module_row(id: &str, role: &str, cabinet: &str) -> Expr {
146 row(vec![
147 ("module", Expr::String(id.to_owned())),
148 ("role", Expr::String(role.to_owned())),
149 ("cabinet", Expr::String(cabinet.to_owned())),
150 ("slots", number(1.0)),
151 ])
152}