A step sequencer for Adafruit's RP2040-based macropad
at main 194 lines 6.1 kB view raw
1use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel}; 2use smart_leds::RGB; 3 4use crate::{ 5 COLS, KeyGrid, NUM_KEYS, ROWS, 6 key_leds::Coord, 7 menus::{SEQUENCER_MENU, SequencerMenuValue, StepMenuValue}, 8 tasks::{ 9 display::{DISPLAY_CHANNEL, DisplayUpdate}, 10 lights::{LIGHTS_CHANNEL, LedUpdate}, 11 usb_midi::{MIDI_CHANNEL, MidiEvent}, 12 }, 13}; 14 15pub static CONTROLS_CHANNEL: Channel<ThreadModeRawMutex, ControlEvent, 10> = Channel::new(); 16 17pub enum ControlEvent { 18 Key { 19 pressed: bool, 20 held: bool, 21 coord: Coord, 22 }, 23 RotaryButton { 24 pressed: bool, 25 }, 26 SequencerStep, 27 RotaryEncoder { 28 increment: i32, 29 }, 30 SequencerMenuChange { 31 value: SequencerMenuValue, 32 }, 33 StepMenuChange { 34 value: StepMenuValue, 35 }, 36} 37 38#[derive(Default, Clone, Copy)] 39struct StepState { 40 active: bool, 41 pressed: bool, 42 held: bool, 43 value: StepMenuValue, 44} 45 46#[embassy_executor::task] 47pub async fn read_controls() { 48 let mut step_state: KeyGrid<StepState> = [[StepState::default(); COLS]; ROWS]; 49 50 let active = RGB { r: 5, g: 5, b: 5 }; 51 let off = RGB { r: 0, g: 0, b: 0 }; 52 let current = RGB { r: 32, g: 0, b: 0 }; 53 54 let mut num_steps = 12; 55 let mut num_keys_pressed = 0; 56 let mut selected_step: Option<Coord> = None; 57 let mut step_index: usize = 0; 58 let mut step: Coord = (0, 0); 59 let mut last_note: Option<StepMenuValue> = None; 60 let mut play = false; 61 62 loop { 63 match CONTROLS_CHANNEL.receive().await { 64 ControlEvent::Key { 65 pressed, 66 held, 67 coord, 68 } => { 69 let state = &mut step_state[coord.1 as usize][coord.0 as usize]; 70 let was_pressed = state.pressed; 71 let was_held = state.held; 72 state.pressed = pressed; 73 state.held = held; 74 if !pressed && was_pressed && !was_held { 75 state.active = !state.active; 76 let color = if state.active { active } else { off }; 77 update_key_light(coord, color).await; 78 } 79 80 if !pressed { 81 num_keys_pressed -= 1; 82 if num_keys_pressed != 1 { 83 set_step_menu(None).await; 84 } 85 } else { 86 if !was_pressed && pressed { 87 num_keys_pressed += 1; 88 } 89 let value = if num_keys_pressed == 1 && held { 90 selected_step = Some(coord); 91 Some(state.value) 92 } else { 93 selected_step = None; 94 None 95 }; 96 97 set_step_menu(value).await; 98 } 99 } 100 ControlEvent::RotaryButton { pressed } => { 101 if !pressed { 102 continue; 103 } 104 105 rotary_press().await; 106 } 107 ControlEvent::RotaryEncoder { increment } => rotary_change(increment).await, 108 ControlEvent::SequencerStep => { 109 let state = step_state[step.1 as usize][step.0 as usize]; 110 111 if let Some(value) = last_note { 112 send_note(false, value).await; 113 } 114 115 let prev_color = if state.active { active } else { off }; 116 117 let next_step = if num_keys_pressed > 0 { 118 step_index = (step_index + 1).rem_euclid(NUM_KEYS); 119 let mut next = coord_from_index(step_index); 120 while !step_state[next.1 as usize][next.0 as usize].pressed { 121 step_index = (step_index + 1).rem_euclid(NUM_KEYS); 122 next = coord_from_index(step_index) 123 } 124 next 125 } else { 126 step_index = (step_index + 1).rem_euclid(num_steps); 127 coord_from_index(step_index) 128 }; 129 130 let state = step_state[next_step.1 as usize][next_step.0 as usize]; 131 last_note = Some(state.value); 132 133 if state.active || num_keys_pressed > 0 { 134 send_note(true, state.value).await; 135 } 136 137 update_key_light(step, prev_color).await; 138 update_key_light(next_step, current).await; 139 step = next_step; 140 } 141 ControlEvent::SequencerMenuChange { value } => unsafe { 142 num_steps = value.steps as usize; 143 if play 144 && !value.play 145 && let Some(note) = last_note 146 { 147 send_note(false, note).await; 148 } 149 play = value.play; 150 SEQUENCER_MENU.lock_mut(|inner| *inner = Some(value)); 151 }, 152 ControlEvent::StepMenuChange { value } => { 153 if let Some(coord) = selected_step { 154 step_state[coord.1 as usize][coord.0 as usize].value = value; 155 } 156 } 157 } 158 } 159} 160 161fn coord_from_index(index: usize) -> Coord { 162 ((index % COLS) as u8, (index / COLS) as u8) 163} 164 165async fn send_note(on: bool, value: StepMenuValue) { 166 MIDI_CHANNEL 167 .send(MidiEvent::Note { 168 on, 169 note: value.note, 170 octave: value.octave as u8, 171 velocity: value.velocity as u8, 172 }) 173 .await; 174} 175 176async fn set_step_menu(value: Option<StepMenuValue>) { 177 DISPLAY_CHANNEL 178 .send(DisplayUpdate::StepMenu { value }) 179 .await; 180} 181 182async fn rotary_press() { 183 DISPLAY_CHANNEL.send(DisplayUpdate::RotaryPress).await; 184} 185 186async fn rotary_change(increment: i32) { 187 DISPLAY_CHANNEL 188 .send(DisplayUpdate::RotaryMove { increment }) 189 .await; 190} 191 192async fn update_key_light(coord: Coord, color: RGB<u8>) { 193 LIGHTS_CHANNEL.send(LedUpdate { coord, color }).await; 194}