189 lines
5.6 KiB
Rust
189 lines
5.6 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 Column<const ROWS: usize> {
|
|
pub tracks: [Track; ROWS],
|
|
pub beat_count: usize, // total beats in column (0 = no beats set)
|
|
pub current_beat: usize, // current beat position (0-based internally)
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct State<const COLS: usize, const ROWS: usize> {
|
|
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 columns: [Column<ROWS>; COLS],
|
|
pub metronome_position: f32, // 0.0 - 1.0 position within current beat
|
|
pub metronome_timestamp: std::time::Instant, // When position was last updated
|
|
pub tempo: f32, // BPM
|
|
}
|
|
|
|
impl<const ROWS: usize> Column<ROWS> {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
tracks: std::array::from_fn(|_| Track {
|
|
state: TrackState::Empty,
|
|
volume: 1.0,
|
|
}),
|
|
beat_count: 0,
|
|
current_beat: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<const COLS: usize, const ROWS: usize> State<COLS, ROWS> {
|
|
pub fn new(tempo: f32) -> Self {
|
|
Self {
|
|
selected_column: 0,
|
|
selected_row: 0,
|
|
columns: std::array::from_fn(|_| Column::new()),
|
|
metronome_position: 0.0,
|
|
metronome_timestamp: std::time::Instant::now(),
|
|
tempo,
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self, message: &Message) {
|
|
match message {
|
|
Message::TrackStateChanged { column, row, state } => {
|
|
if *column < COLS && *row < ROWS {
|
|
self.columns[*column].tracks[*row].state = state.clone();
|
|
}
|
|
}
|
|
Message::TrackVolumeChanged {
|
|
column,
|
|
row,
|
|
volume,
|
|
} => {
|
|
if *column < COLS && *row < ROWS {
|
|
self.columns[*column].tracks[*row].volume = *volume;
|
|
}
|
|
}
|
|
Message::SelectedColumnChanged { column } => {
|
|
if *column < COLS {
|
|
self.selected_column = *column;
|
|
}
|
|
}
|
|
Message::SelectedRowChanged { row } => {
|
|
if *row < ROWS {
|
|
self.selected_row = *row;
|
|
}
|
|
}
|
|
Message::MetronomePosition { position } => {
|
|
self.set_metronome_position(*position);
|
|
}
|
|
Message::Tempo { bpm } => {
|
|
self.set_tempo(*bpm);
|
|
}
|
|
Message::ColumnBeatsChanged { column, beats } => {
|
|
self.set_column_beats(*column, *beats);
|
|
}
|
|
Message::ColumnBeatChanged { column, beat } => {
|
|
self.set_column_beat(*column, *beat);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn create_state_dump(&self) -> Vec<Message> {
|
|
let mut messages = Vec::new();
|
|
|
|
// Send tempo
|
|
messages.push(Message::Tempo { bpm: self.tempo });
|
|
|
|
// Send current selections
|
|
messages.push(Message::SelectedColumnChanged {
|
|
column: self.selected_column,
|
|
});
|
|
messages.push(Message::SelectedRowChanged {
|
|
row: self.selected_row,
|
|
});
|
|
|
|
// Send column beat information and cell states/volumes
|
|
for (column, column_data) in self.columns.iter().enumerate() {
|
|
// Send column beat info
|
|
messages.push(Message::ColumnBeatsChanged {
|
|
column,
|
|
beats: column_data.beat_count,
|
|
});
|
|
messages.push(Message::ColumnBeatChanged {
|
|
column,
|
|
beat: column_data.current_beat,
|
|
});
|
|
|
|
// Send all cell states and volumes for this column
|
|
for (row, track) in column_data.tracks.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 < COLS && row < ROWS {
|
|
self.columns[column].tracks[row].state = state;
|
|
}
|
|
}
|
|
|
|
pub fn set_selected_column(&mut self, column: usize) {
|
|
if column < COLS {
|
|
self.selected_column = column;
|
|
}
|
|
}
|
|
|
|
pub fn set_selected_row(&mut self, row: usize) {
|
|
if row < ROWS {
|
|
self.selected_row = row;
|
|
}
|
|
}
|
|
|
|
pub fn set_track_volume(&mut self, column: usize, row: usize, volume: f32) {
|
|
if column < COLS && row < ROWS {
|
|
self.columns[column].tracks[row].volume = volume;
|
|
}
|
|
}
|
|
|
|
pub fn set_metronome_position(&mut self, position: f32) {
|
|
self.metronome_position = position;
|
|
self.metronome_timestamp = std::time::Instant::now();
|
|
}
|
|
|
|
pub fn set_tempo(&mut self, bpm: f32) {
|
|
self.tempo = bpm;
|
|
}
|
|
|
|
pub fn set_column_beats(&mut self, column: usize, beats: usize) {
|
|
if column < COLS {
|
|
self.columns[column].beat_count = beats;
|
|
}
|
|
}
|
|
|
|
pub fn set_column_beat(&mut self, column: usize, beat: usize) {
|
|
if column < COLS {
|
|
self.columns[column].current_beat = beat;
|
|
}
|
|
}
|
|
}
|