Skip to main content

sim_lib_view_daw/
system55.rs

1//! Moog System 55 instrument editor scenes.
2
3use 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
12/// Moog System 55 editor route name.
13pub const SYSTEM55_EDITOR_ROUTE: &str = SYSTEM55_EDITOR_ROUTE_NAME;
14/// Moog System 55 editor view id.
15pub const SYSTEM55_EDITOR_VIEW: &str = SYSTEM55_EDITOR_VIEW_ID;
16
17/// Return Moog System 55 editor fixture names.
18pub fn system55_editor_fixture_names() -> &'static [&'static str] {
19    &SYSTEM55_EDITOR_FIXTURE_NAMES
20}
21
22/// Build a deterministic Moog System 55 editor snapshot.
23pub fn system55_editor_snapshot(name: &str) -> Option<Expr> {
24    SYSTEM55_EDITOR_FIXTURE_NAMES
25        .contains(&name)
26        .then(|| system55_editor_view(name))
27}
28
29/// Build the Moog System 55 cabinet editor view.
30pub 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}