nord_format/formats/ne5/program/
effects.rs1use crate::bits::Packed;
4use crate::components::sparse_enum;
5use crate::components::{EqBand, Frequency, Rate, RotorSpeed, Time};
6use crate::formats::ne5::Level;
7use crate::types::RangedU8;
8use nord_bits_derive::bitbody;
9
10use std::fmt::{self, Display, Formatter};
11
12#[bitbody(18)]
17#[derive(Default)]
18pub struct EffectsPanel {
19 #[bits(0..=1)]
20 pub fx1: Routing,
21 #[bits(2..=5)]
22 pub fx1_type: Fx1Type,
23 #[bits(6..=12)]
26 pub fx1_rate: Rate,
27 #[bits(13..=14)]
28 pub fx2: Routing,
29 #[bits(15..=18)]
30 pub fx2_type: Fx2Type,
31 #[bits(19..=25)]
32 pub fx2_rate: Rate,
33 #[bits(26..=27)]
34 pub fx4: Routing,
35 #[bits(28..=29)]
36 pub fx4_feedback: RangedU8<3>,
37 #[bits(30..=36)]
40 pub fx4_tempo: Time,
41 #[bits(37..=43)]
43 pub fx4_moisture: Level,
44 #[bits(44..=44)]
45 pub fx4_ping_pong: bool,
46 #[bits(45..=45)]
48 pub equalizer_on: bool,
49 #[bits(117..=118)]
52 pub equalizer_part: EqualizerPart,
53 #[bits(47..=53)]
55 pub equalizer_freq: Frequency,
56 #[bits(54..=60)]
57 pub equalizer_treble: EqBand,
58 #[bits(61..=67)]
61 pub equalizer_freq_gain: EqBand,
62 #[bits(68..=74)]
63 pub equalizer_bass: EqBand,
64 #[bits(75..=76)]
65 pub fx3: Routing,
66 #[bits(77..=79)]
67 pub fx3_type: Fx3Type,
68 #[bits(80..=86)]
69 pub fx3_compression: Level,
70 #[bits(87..=87)]
71 pub fx5: bool,
72 #[bits(88..=90)]
73 pub fx5_type: Fx5Type,
74 #[bits(91..=97)]
75 pub fx5_moisture: Level,
76 #[bits(98..=98)]
77 pub rotary_stop: bool,
78 #[bits(99..=99)]
79 pub rotary_speed: RotorSpeed,
80 #[bits(115..=115)]
82 pub fx1_control: bool,
83 #[bits(116..=116)]
85 pub fx2_deep: bool,
86}
87
88#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
99pub enum Routing {
100 #[default]
101 Off = 0,
102 Unknown = 1,
108 Lower = 2,
109 Upper = 3,
110}
111
112impl Routing {
113 pub fn part(&self) -> Option<&'static str> {
115 match self {
116 Routing::Lower => Some("lower"),
117 Routing::Upper => Some("upper"),
118 Routing::Off | Routing::Unknown => None,
119 }
120 }
121
122 pub fn is_unknown(&self) -> bool {
125 matches!(self, Routing::Unknown)
126 }
127}
128
129impl Packed for Routing {
130 const MAX_BITS: u32 = 2;
131 const DECODE_BITS: u32 = 2;
132 const CONTROL: crate::fields::ControlKind = crate::fields::ControlKind::Selector;
133 type Error = std::convert::Infallible;
134
135 fn from_bits(bits: u64) -> Result<Self, Self::Error> {
136 Ok(match bits & 0b11 {
137 0 => Routing::Off,
138 1 => Routing::Unknown,
139 2 => Routing::Lower,
140 _ => Routing::Upper,
141 })
142 }
143
144 fn to_bits(&self) -> u64 {
145 *self as u64
146 }
147}
148
149impl Display for Routing {
150 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151 match self {
152 Routing::Off => f.write_str("off"),
153 Routing::Unknown => f.write_str("unknown (1)"),
154 Routing::Lower => f.write_str("lower"),
155 Routing::Upper => f.write_str("upper"),
156 }
157 }
158}
159
160sparse_enum!(
161 Fx1Type, 4, {
167 0 => Trem1, "trem 1";
168 1 => Trem2, "trem 2";
169 2 => Trem1And2, "trem 1&2";
170 3 => Pan1, "pan 1";
171 4 => Pan2, "pan 2";
172 5 => Pan1And2, "pan 1&2";
173 6 => Wah, "wah";
174 7 => Rm, "rm";
175 }
176);
177
178sparse_enum!(
179 Fx2Type, 4, {
181 0 => Phaser1, "phaser 1";
182 1 => Phaser2, "phaser 2";
183 2 => Flanger, "flanger";
184 3 => Chorus1, "chorus 1";
185 4 => Chorus2, "chorus 2";
186 5 => Vibe, "vibe";
187 }
188);
189
190sparse_enum!(
191 Fx3Type, 3, {
193 0 => None_, "none";
194 1 => Small, "small";
195 2 => Jc, "jc";
196 3 => Twin, "twin";
197 4 => Rotary, "rotary";
198 5 => Comp, "comp";
199 }
200);
201
202sparse_enum!(
203 Fx5Type, 3, {
205 0 => Room, "room";
206 1 => StageSoft, "stage soft";
207 2 => Stage, "stage";
208 3 => HallSoft, "hall soft";
209 4 => Hall, "hall";
210 }
211);
212
213sparse_enum!(
214 EqualizerPart, 2, {
217 0 => Lower, "lower";
218 1 => Upper, "upper";
219 2 => Both, "lower+upper";
220 }
221);
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
229 fn an_unrecognized_value_survives_and_announces_itself() {
230 let unknown = Fx1Type::from_bits(9).unwrap();
231 assert_eq!(unknown, Fx1Type::Unknown(9));
232 assert!(unknown.is_unknown());
233 assert_eq!(unknown.label(), None);
234 assert_eq!(unknown.to_string(), "unknown (9)");
235 assert_eq!(unknown.to_bits(), 9, "an unknown value must round-trip");
236 }
237
238 #[test]
240 fn recovered_values_round_trip() {
241 for bits in 0..8u64 {
242 let t = Fx1Type::from_bits(bits).unwrap();
243 assert!(!t.is_unknown(), "{bits} should be recovered");
244 assert_eq!(t.to_bits(), bits);
245 }
246 for bits in 0..3u64 {
247 assert_eq!(EqualizerPart::from_bits(bits).unwrap().to_bits(), bits);
248 }
249 }
250
251 #[test]
252 fn routing_matches_what_the_instrument_stores() {
253 assert_eq!(Routing::from_bits(0).unwrap(), Routing::Off);
254 assert_eq!(Routing::from_bits(1).unwrap(), Routing::Unknown);
255 assert_eq!(Routing::from_bits(2).unwrap(), Routing::Lower);
256 assert_eq!(Routing::from_bits(3).unwrap(), Routing::Upper);
257
258 for bits in 0..4u64 {
259 assert_eq!(Routing::from_bits(bits).unwrap().to_bits(), bits);
260 }
261
262 assert_eq!(Routing::Lower.to_bits(), 2);
264 assert_eq!(Routing::Upper.to_bits(), 3);
265
266 assert_eq!(Routing::Off.to_string(), "off");
268 assert_eq!(Routing::Unknown.to_string(), "unknown (1)");
269 assert!(Routing::Unknown.is_unknown());
270 assert!(!Routing::Off.is_unknown());
271 assert_eq!(Routing::Unknown.part(), None);
272 }
273}