use crate::*; #[derive(Clone, Debug, PartialEq, strum::Display, strum::EnumString)] pub enum TrackState { Empty, Idle, Playing, Recording, } #[derive(Debug, Clone)] pub struct Track { pub state: TrackState, pub volume: f32, } #[derive(Debug, Clone)] pub struct State { pub selected_column: usize, // 0-based (converted to 1-based for OSC) pub selected_row: usize, // 0-based (converted to 1-based for OSC) pub cells: Vec>, pub columns: usize, pub rows: usize, pub metronome_position: f32, // 0.0 - 1.0 position within current beat pub metronome_timestamp: std::time::Instant, // When position was last updated } impl State { pub fn new(columns: usize, rows: usize) -> Self { let cells = (0..columns) .map(|_| vec![Track { state: TrackState::Empty, volume: 1.0 }; rows]) .collect(); Self { selected_column: 0, selected_row: 0, cells, columns, rows, metronome_position: 0.0, metronome_timestamp: std::time::Instant::now(), } } pub fn update(&mut self, message: &Message) { match message { Message::TrackStateChanged { column, row, state } => { if *column < self.columns && *row < self.rows { self.cells[*column][*row].state = state.clone(); } } Message::TrackVolumeChanged { column, row, volume } => { if *column < self.columns && *row < self.rows { self.cells[*column][*row].volume = *volume; } } Message::SelectedColumnChanged { column } => { if *column < self.columns { self.selected_column = *column; } } Message::SelectedRowChanged { row } => { if *row < self.rows { self.selected_row = *row; } } Message::MetronomePosition { position } => { self.set_metronome_position(*position); } } } pub fn create_state_dump(&self) -> Vec { let mut messages = Vec::new(); // Send current selections messages.push(Message::SelectedColumnChanged { column: self.selected_column, }); messages.push(Message::SelectedRowChanged { row: self.selected_row, }); // Send all cell states and volumes for (column, column_cells) in self.cells.iter().enumerate() { for (row, track) in column_cells.iter().enumerate() { messages.push(Message::TrackStateChanged { column, row, state: track.state.clone(), }); messages.push(Message::TrackVolumeChanged { column, row, volume: track.volume, }); } } messages } pub fn set_track_state(&mut self, column: usize, row: usize, state: TrackState) { if column < self.columns && row < self.rows { self.cells[column][row].state = state; } } pub fn set_selected_column(&mut self, column: usize) { if column < self.columns { self.selected_column = column; } } pub fn set_selected_row(&mut self, row: usize) { if row < self.rows { self.selected_row = row; } } pub fn set_track_volume(&mut self, column: usize, row: usize, volume: f32) { if column < self.columns && row < self.rows { self.cells[column][row].volume = volume; } } pub fn set_metronome_position(&mut self, position: f32) { self.metronome_position = position; self.metronome_timestamp = std::time::Instant::now(); } }