Skip to main content

nord_format/formats/ns4/
synth.rs

1//! The Stage 4 synth preset body (`.ns4y`): 497 bytes.
2//!
3//! One synth section as a program stores it, moved down 327 bytes. The keyboard
4//! zone and the extern CC values ride along for every layer, though a preset has
5//! no use for them. Layers are 408 bits apart; layer A's zone is confirmed
6//! against the corpus (it varies exactly as B's and C's do), its extern CC block
7//! is placed by the stride alone, since no specimen sets any layer's.
8
9use super::fx::FxChain;
10use super::synth_performance::SynthPerformance;
11use super::synth_voice::SynthVoice;
12use crate::cbin::{self, Cbin};
13use crate::components::{Level, MorphTarget, Pan};
14use crate::error::Error;
15use std::io::{Read, Seek};
16
17pub const FORMAT: &str = "ns4y";
18/// Stored ×100. The corpus holds 2.08; ns4decode was tested from 2.03.
19pub const KNOWN_VERSIONS: &[u32] = &[203, 204, 205, 206, 207, 208];
20pub const BODY_LEN: usize = 497;
21
22/// The 497-byte preset body: one synth section as a program stores it.
23///
24/// Reads and writes byte-exactly. A read verifies the container checksum, gates
25/// on [`KNOWN_VERSIONS`], and range-checks every field; unclaimed bits survive a
26/// re-encode verbatim. Placements derived from ns4decode's published tables;
27/// values raw. Inferred from specimens; not confirmed on hardware.
28#[nord_bits_derive::bitbody(497)]
29pub struct SynthPreset {
30    #[bits(42..=42)]
31    pub synth_c_layer_enabled: bool,
32    #[bits(43..=43)]
33    pub synth_b_layer_enabled: bool,
34    #[bits(44..=44)]
35    pub synth_a_layer_enabled: bool,
36    #[bits(47..=47)]
37    pub synth_c_layer_enabled_scene_2: bool,
38    #[bits(48..=48)]
39    pub synth_b_layer_enabled_scene_2: bool,
40    #[bits(49..=49)]
41    pub synth_a_layer_enabled_scene_2: bool,
42    #[bits(50..=56)]
43    pub synth_a_volume: Level,
44    #[bits(57..=64)]
45    pub synth_a_volume_wheel: MorphTarget,
46    #[bits(65..=72)]
47    pub synth_a_volume_aftertouch: MorphTarget,
48    #[bits(73..=80)]
49    pub synth_a_volume_ctrl_pedal: MorphTarget,
50    #[bits(81..=87)]
51    pub synth_b_volume: Level,
52    #[bits(88..=95)]
53    pub synth_b_volume_wheel: MorphTarget,
54    #[bits(96..=103)]
55    pub synth_b_volume_aftertouch: MorphTarget,
56    #[bits(104..=111)]
57    pub synth_b_volume_ctrl_pedal: MorphTarget,
58    #[bits(112..=118)]
59    pub synth_c_volume: Level,
60    #[bits(119..=126)]
61    pub synth_c_volume_wheel: MorphTarget,
62    #[bits(127..=134)]
63    pub synth_c_volume_aftertouch: MorphTarget,
64    #[bits(135..=142)]
65    pub synth_c_volume_ctrl_pedal: MorphTarget,
66    #[bits(143..=148)]
67    pub synth_a_pan: Pan,
68    #[bits(174..=179)]
69    pub synth_b_pan: Pan,
70    #[bits(205..=210)]
71    pub synth_c_pan: Pan,
72
73    #[at(36..83)]
74    pub synth_a_performance: SynthPerformance,
75    #[at(87..134)]
76    pub synth_b_performance: SynthPerformance,
77    #[at(138..185)]
78    pub synth_c_performance: SynthPerformance,
79    #[at(189..233)]
80    pub synth_a_voice: SynthVoice,
81    #[at(237..281)]
82    pub synth_b_voice: SynthVoice,
83    #[at(285..329)]
84    pub synth_c_voice: SynthVoice,
85    #[at(333..385)]
86    pub synth_a_fx: FxChain,
87    #[at(388..440)]
88    pub synth_b_fx: FxChain,
89    #[at(443..495)]
90    pub synth_c_fx: FxChain,
91}
92
93pub fn read_from(reader: &mut (impl Read + Seek)) -> Result<Cbin<SynthPreset>, Error> {
94    let file: Cbin<SynthPreset> = cbin::read(reader, FORMAT)?;
95    crate::formats::known_version(FORMAT, file.header.version, KNOWN_VERSIONS)?;
96    Ok(file)
97}