nord_format/formats/ne5/program/
center.rs1use crate::components::sparse_enum;
4use crate::components::PartMix;
5use crate::formats::ne5::{Instrument, Level, OctaveShift, SplitPoint, Transpose};
6use nord_bits_derive::bitbody;
7
8#[bitbody(7)]
13#[derive(Default)]
14pub struct CenterPanel {
15 #[bits(0..=2)]
16 pub lower_part: Instrument,
17 #[bits(3..=5)]
18 pub upper_part: Instrument,
19 #[bits(6..=9)]
20 pub lower_octave_shift: OctaveShift,
21 #[bits(10..=13)]
22 pub upper_octave_shift: OctaveShift,
23 #[bits(14..=14)]
24 pub lower_sustain: bool,
25 #[bits(15..=15)]
26 pub upper_sustain: bool,
27 #[bits(16..=16)]
28 pub lower_control: bool,
29 #[bits(17..=17)]
30 pub upper_control: bool,
31 #[bits(18..=18)]
33 pub unknown_boolean1: bool,
34 #[bits(19..=19)]
35 pub split: bool,
36 #[bits(20..=22)]
37 pub split_point: SplitPoint,
38 #[bits(23..=23)]
46 pub transpose_enabled: bool,
47
48 #[bits(24..=27)]
54 pub transpose: Transpose,
55 #[bits(28..=34)]
56 pub part_mix: PartMix,
57 #[bits(35..=41)]
58 pub gain: Level,
59 #[bits(42..=44)]
60 pub organ_type: OrganType,
61 #[bits(45..=45)]
62 pub lower_enabled: bool,
63 #[bits(46..=46)]
64 pub upper_enabled: bool,
65 #[bits(47..=47)]
66 pub drawbar_live: bool,
67}
68
69sparse_enum!(
70 OrganType, 3, {
75 0 => B3, "b3";
76 1 => B3Bass, "b3+bass";
77 2 => Pipe, "pipe";
78 3 => Vox, "vox";
79 4 => Farfisa, "farfisa";
80 }
81);
82
83impl OrganType {
84 pub fn storage(&self) -> Option<crate::formats::ne5::program::OrganModel> {
88 use crate::formats::ne5::program::OrganModel;
89 match self {
90 OrganType::B3 | OrganType::B3Bass => Some(OrganModel::B3),
91 OrganType::Pipe => Some(OrganModel::Pipe),
92 OrganType::Vox => Some(OrganModel::Vox),
93 OrganType::Farfisa => Some(OrganModel::Farfisa),
94 OrganType::Unknown(_) => None,
95 }
96 }
97
98 pub fn is_b3_bass(&self) -> bool {
108 matches!(self, OrganType::B3Bass)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::super::{self as program, OrganModel, FILE_LEN};
115 use super::*;
116 use crate::bits::Packed;
117 use crate::types::RangedU8;
118 use std::io::Cursor;
119
120 #[test]
123 fn an_out_of_range_value_is_refused_where_it_is_written() {
124 let too_wide: Result<RangedU8<127>, _> = 200u8.try_into();
126 assert!(too_wide.is_err(), "200 must not be a valid seven-bit gain");
127 assert!(
128 RangedU8::<127>::new(127).is_ok(),
129 "127 is the largest that fits"
130 );
131
132 let mut program = program::new((0, 0).try_into().unwrap());
133 program.center_panel.gain = 96u8.try_into().unwrap();
134
135 let mut bytes = Vec::new();
136 program
137 .write_to(&mut Cursor::new(&mut bytes))
138 .expect("a panel built from ranged values always writes");
139 assert_eq!(bytes.len(), FILE_LEN);
140 }
141
142 #[test]
145 fn the_default_panel_encodes_and_decodes() {
146 let panel = CenterPanel::default();
147 let raw = <[u8; 7]>::from(&panel);
148 let back = CenterPanel::try_from(raw).expect("default panel must decode");
149
150 assert_eq!(back.lower_octave_shift, 0);
151 assert_eq!(back.upper_octave_shift, 0);
152 assert_eq!(back.transpose, 0);
153 assert_eq!(back.lower_part, Instrument::Organ);
154 }
155
156 #[test]
158 fn organ_type_values_round_trip() {
159 for bits in 0..5u64 {
160 assert_eq!(OrganType::from_bits(bits).unwrap().to_bits(), bits);
161 }
162 assert!(OrganType::from_bits(6).unwrap().is_unknown());
163 assert_eq!(OrganType::B3Bass.storage(), Some(OrganModel::B3));
164 assert!(OrganType::B3Bass.is_b3_bass());
165 assert!(!OrganType::B3.is_b3_bass());
166 }
167}