1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Nord Stage 4 (`.ns4p`, `.ns4l`, `.ns4y`, `.ns4n`, `.ns4o`, `.ns4t`).
//!
//! The Stage 4 banks its three sections separately from its programs: a synth
//! (`.ns4y`), piano (`.ns4n`) or organ (`.ns4o`) preset holds one section's
//! parameters under its own tag, and a program holds all three plus the globals
//! that route them. ⚠️ The extension letters do not follow the Stage 3's — `p`
//! is the program here, where the Stage 3 writes `f`.
//!
//! # What decodes
//!
//! Every parameter placement in the program body and the three preset bodies.
//! Bits no parameter claims ride through a re-encode verbatim.
//!
//! **A field's type says what kind of control it is, and stops there.** A knob is a
//! `Level` or a `Time`, so a caller knows to draw a dial and which unit to label it
//! in; a fixed list is a [`Selector`](crate::components::Selector), so a caller knows
//! to draw positions rather than a range. What none of them do is *name* a position:
//! no specimen says which index is `LP24`, so a filter type stays a number under a
//! type that says it is a selector. Give a field a
//! `sparse_enum!` the moment its table is known —
//! that is strictly better — but never before.
//!
//! The three exceptions all rest on the corpus rather than on a guess, and each says so
//! at its own definition: the octave shift reads as two's complement
//! ([`OctaveShiftNibble`](crate::components::OctaveShiftNibble)), the keyboard zone holds
//! the Stage 3's table ([`KbZone4`](crate::components::KbZone4)), and the arpeggiator's
//! three `u32` rows read as sixteen two-bit steps
//! ([`ArpPattern`](crate::components::ArpPattern)).
//!
//! The settings (`.ns4t`) are a container-verified stub.
//!
//! # Naming
//!
//! A field is `<section>_<layer>_<parameter>`: the organ and piano have layers
//! `a` and `b`, the synth `a`, `b` and `c`, and program-wide parameters have no
//! prefix. The organ's two layers share one effects chain, so those fields are
//! program-wide too: `organ_fx_*`, with no layer in the name.
//!
//! A parameter that can be driven from a performance control has three siblings
//! holding the value that control morphs *to*: `_wheel`, `_aftertouch` and
//! `_ctrl_pedal`. `_scene_2` is the second layer scene's copy.
//!
//! # Provenance
//!
//! Every placement is derived from the offset tables published by
//! [ns4decode](https://ns4decode.netlify.app) (MIT, © 2024 Randy), a Stage 4
//! file viewer. Reported by public documentation; not confirmed on hardware. Its
//! notation is 1-based file bytes with bits numbered 1..=8 MSB-first, which our
//! bit numbering matches once the 44-byte container header comes off.
//!
//! That mapping is confirmed against the corpus rather than assumed: the tables
//! place five parameters inside the header (the tag, version, bank, slot and
//! checksum) and one — `version_echo` — in the first bytes of the body, and all
//! six agree with the container's own parse on every Stage 4 specimen. The value
//! tables ns4decode also publishes are not used here.
use raw_format;
pub use FxChain;
pub use OrganLayer;
pub use PianoLayer;
pub use SynthPerformance;
pub use SynthVoice;
pub use Program;
raw_format!;