1use crate::action::{action_to_midi, MidiMessage};
5use crate::config::{EncoderAction, Preset, MAX_PRESETS};
6
7const NUM_BUTTONS: usize = 6;
8const NUM_ENCODERS: usize = 2;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct PresetState {
13 pub button_active: [bool; NUM_BUTTONS],
14 pub cycle_index: [u8; NUM_BUTTONS],
15 pub encoder_values: [u8; NUM_ENCODERS],
16}
17
18impl Default for PresetState {
19 fn default() -> Self {
20 Self {
21 button_active: [false; NUM_BUTTONS],
22 cycle_index: [0; NUM_BUTTONS],
23 encoder_values: [0; NUM_ENCODERS],
24 }
25 }
26}
27
28impl PresetState {
29 pub fn from_defaults(preset: &Preset) -> Self {
31 let mut state = Self::default();
32 for (i, &active) in preset.defaults.button_active.iter().enumerate() {
33 if i < NUM_BUTTONS {
34 state.button_active[i] = active;
35 }
36 }
37 for (i, &val) in preset.defaults.encoder_values.iter().enumerate() {
38 if i < NUM_ENCODERS {
39 state.encoder_values[i] = val;
40 }
41 }
42 state
43 }
44}
45
46#[derive(Clone)]
48pub struct PresetStateStore {
49 states: [PresetState; MAX_PRESETS],
50 active: u8,
51}
52
53impl Default for PresetStateStore {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl PresetStateStore {
60 pub fn new() -> Self {
61 Self {
62 states: core::array::from_fn(|_| PresetState::default()),
63 active: 0,
64 }
65 }
66
67 pub fn current(&self) -> &PresetState {
69 &self.states[self.active as usize]
70 }
71
72 pub fn current_mut(&mut self) -> &mut PresetState {
74 &mut self.states[self.active as usize]
75 }
76
77 pub fn active_index(&self) -> u8 {
79 self.active
80 }
81
82 pub fn save_working(&mut self, working: &PresetState) {
84 self.states[self.active as usize] = working.clone();
85 }
86
87 pub fn clear(&mut self) {
89 *self = Self::new();
90 }
91
92 pub fn set_state(&mut self, index: usize, state: PresetState) {
94 if index < MAX_PRESETS {
95 self.states[index] = state;
96 }
97 }
98
99 pub fn apply_defaults(&mut self, presets: &[&Preset]) {
102 for (i, preset) in presets.iter().enumerate() {
103 if i < MAX_PRESETS {
104 self.states[i] = PresetState::from_defaults(preset);
105 }
106 }
107 }
108
109 pub fn cleared_eeprom() -> [u8; 128] {
111 let mut buf = [0u8; 128];
112 Self::new().to_eeprom(&mut buf);
113 buf
114 }
115
116 pub fn switch(
119 &mut self,
120 new_preset: u8,
121 working: &mut PresetState,
122 preset: &Preset,
123 ) -> heapless::Vec<MidiMessage, 16> {
124 let mut recall = heapless::Vec::new();
125
126 self.states[self.active as usize] = working.clone();
128
129 self.active = new_preset;
131 *working = self.states[new_preset as usize].clone();
132
133 for (i, btn) in preset.buttons.iter().enumerate() {
135 if working.button_active[i] {
136 for action in &btn.on_press {
137 if let Some(msg) = action_to_midi(action) {
138 recall.push(msg).ok();
139 }
140 }
141 } else if !btn.on_release.is_empty() {
142 for action in &btn.on_release {
143 if let Some(msg) = action_to_midi(action) {
144 recall.push(msg).ok();
145 }
146 }
147 }
148 }
149
150 for (i, enc) in preset.encoders.iter().enumerate() {
152 if let EncoderAction::Cc { cc, channel, .. } = &enc.action {
153 recall
154 .push(MidiMessage {
155 data: [0xB0 | (channel - 1), *cc as u8, working.encoder_values[i]],
156 len: 3,
157 })
158 .ok();
159 }
160 }
161
162 recall
163 }
164}
165
166const EEPROM_VERSION: u8 = 1;
171const EEPROM_MAGIC: u8 = 0xE0 | EEPROM_VERSION;
172const EEPROM_HEADER_SIZE: usize = 2; const PRESET_STATE_SIZE: usize = 14; pub const EEPROM_MAX_PRESETS: usize = (128 - EEPROM_HEADER_SIZE) / PRESET_STATE_SIZE; impl PresetState {
178 pub fn to_bytes(&self, buf: &mut [u8; PRESET_STATE_SIZE]) {
180 for (i, &active) in self.button_active.iter().enumerate() {
181 buf[i] = active as u8;
182 }
183 buf[NUM_BUTTONS..NUM_BUTTONS * 2].copy_from_slice(&self.cycle_index);
184 buf[12] = self.encoder_values[0];
185 buf[13] = self.encoder_values[1];
186 }
187
188 pub fn from_bytes(buf: &[u8; PRESET_STATE_SIZE]) -> Self {
190 let mut state = Self::default();
191 for (i, &b) in buf[..NUM_BUTTONS].iter().enumerate() {
192 state.button_active[i] = b != 0;
193 }
194 state
195 .cycle_index
196 .copy_from_slice(&buf[NUM_BUTTONS..NUM_BUTTONS * 2]);
197 state.encoder_values[0] = buf[12];
198 state.encoder_values[1] = buf[13];
199 state
200 }
201}
202
203impl PresetStateStore {
204 pub fn to_eeprom(&self, buf: &mut [u8; 128]) {
207 buf.fill(0xFF);
208 buf[0] = EEPROM_MAGIC;
209 buf[1] = self.active;
210 for i in 0..EEPROM_MAX_PRESETS {
211 let offset = EEPROM_HEADER_SIZE + i * PRESET_STATE_SIZE;
212 let mut state_buf = [0u8; PRESET_STATE_SIZE];
213 self.states[i].to_bytes(&mut state_buf);
214 buf[offset..offset + PRESET_STATE_SIZE].copy_from_slice(&state_buf);
215 }
216 }
217
218 pub fn from_eeprom(buf: &[u8; 128]) -> Option<Self> {
220 if buf[0] != EEPROM_MAGIC {
221 return None;
222 }
223 let mut store = Self::new();
224 store.active = buf[1];
225 for i in 0..EEPROM_MAX_PRESETS {
226 let offset = EEPROM_HEADER_SIZE + i * PRESET_STATE_SIZE;
227 let state_buf: &[u8; PRESET_STATE_SIZE] =
228 buf[offset..offset + PRESET_STATE_SIZE].try_into().ok()?;
229 store.states[i] = PresetState::from_bytes(state_buf);
230 }
231 Some(store)
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use crate::config::*;
239 use heapless::Vec;
240
241 fn make_preset() -> Preset {
242 let mut buttons: Vec<ButtonConfig, MAX_BUTTONS> = Vec::new();
243 let mut on_press: Vec<Action, MAX_ACTIONS> = Vec::new();
244 on_press.push(Action::cc(10, 127, 1).unwrap()).ok();
245 let mut on_release: Vec<Action, MAX_ACTIONS> = Vec::new();
246 on_release.push(Action::cc(10, 0, 1).unwrap()).ok();
247 buttons
248 .push(ButtonConfig {
249 label: Label::new(),
250 color: LedConfig::default(),
251 mode: ButtonMode::Toggle,
252 on_press,
253 on_release,
254 on_long_press: Vec::new(),
255 cycle_values: Vec::new(),
256 listen_cc: None,
257 })
258 .ok();
259
260 let mut encoders: Vec<EncoderConfig, MAX_ENCODERS> = Vec::new();
261 encoders
262 .push(EncoderConfig {
263 label: Label::try_from("Vol").unwrap(),
264 action: EncoderAction::Cc {
265 cc: 7,
266 channel: 1,
267 min: 0,
268 max: 127,
269 },
270 })
271 .ok();
272
273 Preset {
274 name: Label::try_from("Test").unwrap(),
275 buttons,
276 encoders,
277 analog: Vec::new(),
278 defaults: Default::default(),
279 on_enter: heapless::Vec::new(),
280 on_exit: heapless::Vec::new(),
281 triggers: heapless::Vec::new(),
282 }
283 }
284
285 #[test]
286 fn switch_saves_and_restores_state() {
287 let preset = make_preset();
288 let mut store = PresetStateStore::new();
289 let mut working = PresetState::default();
290
291 working.button_active[0] = true;
293 working.encoder_values[0] = 80;
294
295 store.switch(1, &mut working, &preset);
297 assert!(!working.button_active[0]);
298 assert_eq!(working.encoder_values[0], 0);
299
300 store.switch(0, &mut working, &preset);
302 assert!(working.button_active[0]);
303 assert_eq!(working.encoder_values[0], 80);
304 }
305
306 #[test]
307 fn recall_sends_active_button_on_press() {
308 let preset = make_preset();
309 let mut store = PresetStateStore::new();
310 let mut working = PresetState::default();
311
312 working.button_active[0] = true;
314 store.switch(1, &mut working, &preset);
315 let recall = store.switch(0, &mut working, &preset);
316
317 assert!(recall.iter().any(|m| m.data == [0xB0, 10, 127]));
319 }
320
321 #[test]
322 fn recall_sends_inactive_button_on_release() {
323 let preset = make_preset();
324 let mut store = PresetStateStore::new();
325 let mut working = PresetState::default();
326
327 store.switch(1, &mut working, &preset);
329 let recall = store.switch(0, &mut working, &preset);
330
331 assert!(recall.iter().any(|m| m.data == [0xB0, 10, 0]));
333 }
334
335 #[test]
336 fn recall_sends_encoder_cc() {
337 let preset = make_preset();
338 let mut store = PresetStateStore::new();
339 let mut working = PresetState::default();
340
341 working.encoder_values[0] = 64;
342 store.switch(1, &mut working, &preset);
343 let recall = store.switch(0, &mut working, &preset);
344
345 assert!(recall.iter().any(|m| m.data == [0xB0, 7, 64]));
347 }
348
349 #[test]
350 fn eeprom_roundtrip() {
351 let mut store = PresetStateStore::new();
352 store.states[0].button_active[0] = true;
353 store.states[0].button_active[3] = true;
354 store.states[0].cycle_index[2] = 5;
355 store.states[0].encoder_values[0] = 100;
356 store.states[0].encoder_values[1] = 42;
357 store.states[1].button_active[5] = true;
358 store.active = 1;
359
360 let mut buf = [0u8; 128];
361 store.to_eeprom(&mut buf);
362
363 let restored = PresetStateStore::from_eeprom(&buf).unwrap();
364 assert_eq!(restored.active_index(), 1);
365 assert!(restored.states[0].button_active[0]);
366 assert!(restored.states[0].button_active[3]);
367 assert_eq!(restored.states[0].cycle_index[2], 5);
368 assert_eq!(restored.states[0].encoder_values[0], 100);
369 assert_eq!(restored.states[0].encoder_values[1], 42);
370 assert!(restored.states[1].button_active[5]);
371 }
372
373 #[test]
374 fn eeprom_invalid_magic_returns_none() {
375 let buf = [0xFFu8; 128];
376 assert!(PresetStateStore::from_eeprom(&buf).is_none());
377 }
378
379 #[test]
380 fn from_defaults_applies_initial_state() {
381 use crate::config::InitialState;
382
383 let mut preset = make_preset();
384 let mut btn_active = Vec::new();
385 btn_active.push(true).ok(); let mut enc_vals = Vec::new();
387 enc_vals.push(64).ok(); preset.defaults = InitialState {
389 button_active: btn_active,
390 encoder_values: enc_vals,
391 };
392
393 let state = PresetState::from_defaults(&preset);
394 assert!(state.button_active[0]);
395 assert!(!state.button_active[1]);
396 assert_eq!(state.encoder_values[0], 64);
397 assert_eq!(state.encoder_values[1], 0);
398 }
399}