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