Skip to main content

nord_format/formats/ns4/
program.rs

1//! The Stage 4 program body (`.ns4p`, `.ns4l`): 824 bytes, every parameter placed.
2//!
3//! All three sections and the globals that route them. Placements, naming and
4//! provenance are the [module docs](super); values are raw.
5
6use super::fx::FxChain;
7use super::organ_layers::OrganLayer;
8use super::piano_layers::PianoLayer;
9use super::synth_performance::SynthPerformance;
10use super::synth_voice::SynthVoice;
11use crate::cbin::{self, Cbin};
12use crate::components::{Level, MorphTarget, Pan, RotorSpeed, Selector};
13use crate::error::Error;
14use crate::types::RangedU8;
15use std::io::{Read, Seek};
16
17mod panel;
18pub use panel::PANEL;
19
20pub const FORMAT: &str = "ns4p";
21/// Schema versions this build's field offsets have been validated against,
22/// stored ×100. The corpus holds 3.13 throughout; ns4decode reports the same
23/// offsets back to 3.04, changing only how three values are *read*, which this
24/// decode does not do.
25pub const KNOWN_VERSIONS: &[u32] = &[304, 305, 306, 307, 308, 309, 310, 311, 312, 313];
26pub const BODY_LEN: usize = 824;
27
28/// The 824-byte program body: all three sections and the globals that route them.
29///
30/// Reads and writes byte-exactly. A read verifies the container checksum, gates
31/// on [`KNOWN_VERSIONS`], and range-checks every field; unclaimed bits survive a
32/// re-encode verbatim. Placements derived from ns4decode's published tables;
33/// values raw. Inferred from specimens; not confirmed on hardware.
34#[nord_bits_derive::bitbody(824)]
35pub struct Program {
36    #[bits(24..=31)]
37    pub version_echo: u8,
38    #[bits(40..=40)]
39    pub split_enabled: bool,
40    #[bits(41..=41)]
41    pub kb_zones_1_2_split_point_enabled: bool,
42    #[bits(42..=42)]
43    pub kb_zones_2_3_split_point_enabled: bool,
44    #[bits(43..=43)]
45    pub kb_zones_3_4_split_point_enabled: bool,
46    #[bits(44..=47)]
47    pub kb_zones_1_2_split_point: Selector<4>,
48    #[bits(48..=51)]
49    pub kb_zones_2_3_split_point: Selector<4>,
50    #[bits(52..=55)]
51    pub kb_zones_3_4_split_point: Selector<4>,
52    #[bits(56..=57)]
53    pub kb_zones_1_2_split_point_xfade: Selector<2>,
54    #[bits(58..=59)]
55    pub kb_zones_2_3_split_point_xfade: Selector<2>,
56    #[bits(60..=61)]
57    pub kb_zones_3_4_split_point_xfade: Selector<2>,
58    #[bits(62..=62)]
59    pub program_transpose_enabled: bool,
60    #[bits(63..=66)]
61    pub program_transpose_amount: RangedU8<15>,
62    #[bits(79..=79)]
63    pub fx_comp_global_enabled: bool,
64    #[bits(80..=80)]
65    pub fx_delay_global_enabled: bool,
66    #[bits(81..=81)]
67    pub fx_reverb_global_enabled: bool,
68    #[bits(316..=316)]
69    pub organ_section_enabled: bool,
70    #[bits(317..=317)]
71    pub piano_section_enabled: bool,
72    #[bits(318..=318)]
73    pub synth_section_enabled: bool,
74    #[bits(319..=319)]
75    pub organ_section_enabled_scene_2: bool,
76    #[bits(320..=320)]
77    pub piano_section_enabled_scene_2: bool,
78    #[bits(321..=321)]
79    pub synth_section_enabled_scene_2: bool,
80    #[bits(322..=322)]
81    pub active_layer_scene: bool,
82    #[bits(347..=347)]
83    pub fx_enabled: bool,
84    #[bits(401..=401)]
85    pub organ_b_layer_enabled: bool,
86    #[bits(402..=402)]
87    pub organ_a_layer_enabled: bool,
88    #[bits(404..=404)]
89    pub organ_b_layer_enabled_scene_2: bool,
90    #[bits(405..=405)]
91    pub organ_a_layer_enabled_scene_2: bool,
92    #[bits(406..=412)]
93    pub organ_a_volume: Level,
94    #[bits(413..=420)]
95    pub organ_a_volume_wheel: MorphTarget,
96    #[bits(421..=428)]
97    pub organ_a_volume_aftertouch: MorphTarget,
98    #[bits(429..=436)]
99    pub organ_a_volume_ctrl_pedal: MorphTarget,
100    #[bits(437..=443)]
101    pub organ_b_volume: Level,
102    #[bits(444..=451)]
103    pub organ_b_volume_wheel: MorphTarget,
104    #[bits(452..=459)]
105    pub organ_b_volume_aftertouch: MorphTarget,
106    #[bits(460..=467)]
107    pub organ_b_volume_ctrl_pedal: MorphTarget,
108    #[bits(468..=468)]
109    pub organ_pitch_stick_enabled: bool,
110    #[bits(472..=472)]
111    pub organ_rotary_speaker_enabled: bool,
112    #[bits(484..=490)]
113    pub rotary_speaker_drive: Level,
114    #[bits(508..=510)]
115    pub rotary_speaker_stop_position: Selector<3>,
116    #[bits(514..=514)]
117    pub rotary_speaker_stop_enabled: bool,
118    #[bits(515..=515)]
119    pub rotary_speaker_slow_fast: RotorSpeed,
120    /// ⚠️ Seven bits and knob-spaced values in the corpus (`0, 21, 42, 59, 75, 89, 91`),
121    /// not a six-way index — what maps them onto the panel's C1–V3 is open.
122    #[bits(522..=528)]
123    pub organ_vib_chorus_type: Level,
124    #[bits(1481..=1481)]
125    pub piano_b_layer_enabled: bool,
126    #[bits(1482..=1482)]
127    pub piano_a_layer_enabled: bool,
128    #[bits(1484..=1484)]
129    pub piano_b_layer_enabled_scene_2: bool,
130    #[bits(1485..=1485)]
131    pub piano_a_layer_enabled_scene_2: bool,
132    #[bits(1486..=1492)]
133    pub piano_a_volume: Level,
134    #[bits(1493..=1500)]
135    pub piano_a_volume_wheel: MorphTarget,
136    #[bits(1501..=1508)]
137    pub piano_a_volume_aftertouch: MorphTarget,
138    #[bits(1509..=1516)]
139    pub piano_a_volume_ctrl_pedal: MorphTarget,
140    #[bits(1517..=1523)]
141    pub piano_b_volume: Level,
142    #[bits(1524..=1531)]
143    pub piano_b_volume_wheel: MorphTarget,
144    #[bits(1532..=1539)]
145    pub piano_b_volume_aftertouch: MorphTarget,
146    #[bits(1540..=1547)]
147    pub piano_b_volume_ctrl_pedal: MorphTarget,
148    #[bits(2658..=2658)]
149    pub synth_c_layer_enabled: bool,
150    #[bits(2659..=2659)]
151    pub synth_b_layer_enabled: bool,
152    #[bits(2660..=2660)]
153    pub synth_a_layer_enabled: bool,
154    #[bits(2663..=2663)]
155    pub synth_c_layer_enabled_scene_2: bool,
156    #[bits(2664..=2664)]
157    pub synth_b_layer_enabled_scene_2: bool,
158    #[bits(2665..=2665)]
159    pub synth_a_layer_enabled_scene_2: bool,
160    #[bits(2666..=2672)]
161    pub synth_a_volume: Level,
162    #[bits(2673..=2680)]
163    pub synth_a_volume_wheel: MorphTarget,
164    #[bits(2681..=2688)]
165    pub synth_a_volume_aftertouch: MorphTarget,
166    #[bits(2689..=2696)]
167    pub synth_a_volume_ctrl_pedal: MorphTarget,
168    #[bits(2697..=2703)]
169    pub synth_b_volume: Level,
170    #[bits(2704..=2711)]
171    pub synth_b_volume_wheel: MorphTarget,
172    #[bits(2712..=2719)]
173    pub synth_b_volume_aftertouch: MorphTarget,
174    #[bits(2720..=2727)]
175    pub synth_b_volume_ctrl_pedal: MorphTarget,
176    #[bits(2728..=2734)]
177    pub synth_c_volume: Level,
178    #[bits(2735..=2742)]
179    pub synth_c_volume_wheel: MorphTarget,
180    #[bits(2743..=2750)]
181    pub synth_c_volume_aftertouch: MorphTarget,
182    #[bits(2751..=2758)]
183    pub synth_c_volume_ctrl_pedal: MorphTarget,
184    #[bits(2759..=2764)]
185    pub synth_a_pan: Pan,
186    #[bits(2790..=2795)]
187    pub synth_b_pan: Pan,
188    #[bits(2821..=2826)]
189    pub synth_c_pan: Pan,
190    #[bits(2853..=2853)]
191    pub synth_arp_group_enabled: bool,
192    #[bits(2855..=2855)]
193    pub synth_kb_hold_enabled: bool,
194
195    #[at(68..97)]
196    pub organ_a: OrganLayer,
197    #[at(99..128)]
198    pub organ_b: OrganLayer,
199    #[at(130..182)]
200    pub organ_fx: FxChain,
201    #[at(198..207)]
202    pub piano_a: PianoLayer,
203    #[at(210..219)]
204    pub piano_b: PianoLayer,
205    #[at(222..274)]
206    pub piano_a_fx: FxChain,
207    #[at(277..329)]
208    pub piano_b_fx: FxChain,
209    #[at(363..410)]
210    pub synth_a_performance: SynthPerformance,
211    #[at(414..461)]
212    pub synth_b_performance: SynthPerformance,
213    #[at(465..512)]
214    pub synth_c_performance: SynthPerformance,
215    #[at(516..560)]
216    pub synth_a_voice: SynthVoice,
217    #[at(564..608)]
218    pub synth_b_voice: SynthVoice,
219    #[at(612..656)]
220    pub synth_c_voice: SynthVoice,
221    #[at(660..712)]
222    pub synth_a_fx: FxChain,
223    #[at(715..767)]
224    pub synth_b_fx: FxChain,
225    #[at(770..822)]
226    pub synth_c_fx: FxChain,
227}
228
229/// The `(bank, location)` pair from the header, uninterpreted: bank 0..=5 for
230/// the six program banks and location 0..=63 on current exports. Not validated
231/// — see the Stage 3's note on out-of-range locations in old files.
232pub fn location(file: &Cbin<Program>) -> (u16, u16) {
233    file.header.slot()
234}
235
236pub fn read_from(reader: &mut (impl Read + Seek)) -> Result<Cbin<Program>, Error> {
237    let file: Cbin<Program> = cbin::read(reader, FORMAT)?;
238    crate::formats::known_version(FORMAT, file.header.version, KNOWN_VERSIONS)?;
239    Ok(file)
240}