fcb_looper/osc/src/state.rs

131 lines
3.9 KiB
Rust

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<Vec<Track>>,
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<Message> {
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();
}
}