nord_format/formats/ns2/program.rs
1//! The Stage 2 program body (`.ns2p`, `.ns2l`): 521 bytes, every documented
2//! parameter placed.
3//!
4//! The program-wide globals were decoded first and by hand; everything else — the
5//! organ's B3, Vox and Farfisa drawbar banks, the piano, synth, extern and the
6//! effects chain — comes from the byte maps. They are
7//! [Chris55/nord-documentation](https://github.com/Chris55/nord-documentation), the
8//! public documentation this module's provenance marks name.
9//!
10//! ⚠️ Stage 2 files are **type-0** containers, where the Stage 3's are type-1. The
11//! byte maps number both in the type-1 layout; since type-0 differs only by
12//! omitting `0x18..0x2b`, the body is the same either way and a documented offset
13//! is `doc - 0x2c` in both.
14//!
15//! The body is 23 bytes of globals and then two [`Slot`]s — the program's two
16//! complete setups — so the slot is declared once and placed twice. Registry paths
17//! follow: `slot_a.organ_volume`.
18//!
19//! Values are raw except where the documentation enumerates them; see [the module
20//! docs](super) for what that ceiling is and why.
21
22use super::slot::Slot;
23use crate::cbin::{self, Cbin};
24use crate::components::{
25 Level, MasterTempo, MorphOf, ReverbType, RotorSpeed, Selector, SplitNote, StageTranspose,
26};
27use crate::error::Error;
28use std::io::{Read, Seek};
29
30pub const FORMAT: &str = "ns2p";
31/// Schema versions this build's field offsets have been validated against. The
32/// corpus factory banks hold 6 and 7; the ns3-program-viewer accepts 2 through 7
33/// with the same offsets.
34pub const KNOWN_VERSIONS: &[u32] = &[2, 3, 4, 5, 6, 7];
35pub const BODY_LEN: usize = 521;
36
37/// The program-wide globals at the head of the body. Bits are MSB-first from body
38/// byte 0 (`0x2c` in a type-1 file), so byte 0x02 bit 5 is bit 18.
39///
40/// Reads and writes byte-exactly. A read verifies the container checksum, gates
41/// on [`KNOWN_VERSIONS`], and range-checks every field; unclaimed bits survive a
42/// re-encode verbatim. Placements from the community byte maps; values raw except
43/// where those maps enumerate them. Inferred from specimens; not confirmed on
44/// hardware.
45#[nord_bits_derive::bitbody(521)]
46pub struct Program {
47 #[bits(16..=17)]
48 pub slot_selection: Selector<2>,
49 #[bits(18..=18)]
50 pub dual_keyboard: bool,
51 #[bits(20..=23)]
52 pub split_low_note: SplitNote,
53 #[bits(24..=27)]
54 pub split_high_note: SplitNote,
55 #[bits(28..=28)]
56 pub split_three_zones: bool,
57 #[bits(29..=29)]
58 pub split_two_zones: bool,
59 #[bits(33..=33)]
60 pub organ_pitch_stick: bool,
61 #[bits(34..=34)]
62 pub transpose_enabled: bool,
63 #[bits(35..=38)]
64 pub transpose: StageTranspose,
65 #[bits(43..=50)]
66 pub master_clock: MasterTempo,
67 #[bits(64..=65)]
68 pub organ_model: Selector<2>,
69 #[bits(72..=74)]
70 pub organ_b3_vibrato_mode: Selector<3>,
71 #[bits(75..=75)]
72 pub organ_b3_harmonic_third: bool,
73 #[bits(76..=76)]
74 pub organ_b3_decay_fast: bool,
75 #[bits(77..=77)]
76 pub organ_b3_volume_soft: bool,
77 #[bits(89..=90)]
78 pub organ_vox_vibrato_mode: Selector<2>,
79 #[bits(91..=91)]
80 pub organ_vox_vibrato_on: bool,
81 #[bits(105..=106)]
82 pub organ_farfisa_vibrato_mode: Selector<2>,
83 #[bits(107..=107)]
84 pub organ_farfisa_vibrato_on: bool,
85 #[bits(120..=122)]
86 pub piano_slot_detune: Selector<3>,
87 #[bits(136..=136)]
88 pub reverb_on: bool,
89 #[bits(137..=139)]
90 pub reverb_type: ReverbType,
91 #[bits(140..=146)]
92 pub reverb_amount: Level,
93 #[bits(147..=147)]
94 pub compressor_on: bool,
95 #[bits(148..=154)]
96 pub compressor_amount: Level,
97 #[bits(155..=155)]
98 pub rotary_speaker_on: bool,
99 #[bits(156..=157)]
100 pub rotary_speaker_source: Selector<2>,
101 #[bits(158..=164)]
102 pub rotary_speaker_drive: Level,
103 #[bits(165..=165)]
104 pub rotary_speaker_stop_mode: bool,
105 #[bits(166..=166)]
106 pub rotary_speaker_speed: RotorSpeed,
107 #[bits(167..=167)]
108 pub rotary_speaker_speed_wheel: MorphOf<1>,
109 #[bits(168..=168)]
110 pub rotary_speaker_speed_aftertouch: MorphOf<1>,
111 #[bits(169..=169)]
112 pub rotary_speaker_speed_ctrl_pedal: MorphOf<1>,
113
114 /// Slot A — the first of the program's two complete setups.
115 #[at(23..272)]
116 pub slot_a: Slot,
117
118 /// Slot B. Same type: the two are the same layout, and neither is
119 /// a copy of the other — `slot_selection` says which sounds.
120 #[at(272..521)]
121 pub slot_b: Slot,
122}
123
124impl Program {
125 /// Whether any split is active.
126 pub fn split_enabled(&self) -> bool {
127 self.split_two_zones || self.split_three_zones
128 }
129}
130
131/// The `(bank, location)` pair from the header, uninterpreted: bank 0..=3,
132/// location 0..=99 on current exports. Not validated — see the Stage 3's note on
133/// out-of-range locations in old files.
134pub fn location(file: &Cbin<Program>) -> (u16, u16) {
135 file.header.slot()
136}
137
138pub fn read_from(reader: &mut (impl Read + Seek)) -> Result<Cbin<Program>, Error> {
139 let file: Cbin<Program> = cbin::read(reader, FORMAT)?;
140 crate::formats::known_version(FORMAT, file.header.version, KNOWN_VERSIONS)?;
141 Ok(file)
142}
143
144/// The Stage 2's octave shift: a nibble biased by 7.
145///
146/// **Corpus:** over the factory banks the slot holds 5..=10 with a decisive mode at 7,
147/// which is where an untransposed program has to sit. The Stage 3 centres on 6 and the
148/// Stage 4 stores two's complement, so each model names its own. Inferred from specimens;
149/// not confirmed on hardware.
150///
151/// Total over the nibble: the widest encoding is `8 + 7 = 15`, so no stored pattern is
152/// refused.
153pub type OctaveShift = crate::components::OctaveShift<7, -7, 8>;