92 lines
3.2 KiB
Rust
92 lines
3.2 KiB
Rust
use crate::display_state::{DisplayState, TrackState};
|
|
use anyhow::{anyhow, Result};
|
|
use rosc::{OscMessage, OscPacket, OscType};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum StateUpdate {
|
|
TrackStateChanged {
|
|
column: usize, // 0-based
|
|
row: usize, // 0-based
|
|
state: TrackState,
|
|
},
|
|
SelectedColumnChanged {
|
|
column: usize, // 0-based
|
|
},
|
|
SelectedRowChanged {
|
|
row: usize, // 0-based
|
|
},
|
|
}
|
|
|
|
impl StateUpdate {
|
|
pub fn from_osc_packet(packet: OscPacket) -> Result<Option<Self>> {
|
|
match packet {
|
|
OscPacket::Message(msg) => Self::from_osc_message(msg),
|
|
OscPacket::Bundle(_) => {
|
|
// For now, we don't handle bundles
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn apply_to_state(&self, state: &mut DisplayState) {
|
|
match self {
|
|
StateUpdate::TrackStateChanged {
|
|
column,
|
|
row,
|
|
state: track_state,
|
|
} => {
|
|
state.update_track_state(*column, *row, track_state.clone());
|
|
}
|
|
StateUpdate::SelectedColumnChanged { column } => {
|
|
state.update_selected_column(*column);
|
|
}
|
|
StateUpdate::SelectedRowChanged { row } => {
|
|
state.update_selected_row(*row);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn from_osc_message(msg: OscMessage) -> Result<Option<Self>> {
|
|
let OscMessage { addr, args } = msg;
|
|
|
|
if addr.starts_with("/looper/cell/") && addr.ends_with("/state") {
|
|
// Parse: /looper/cell/{column}/{row}/state
|
|
let parts: Vec<&str> = addr.split('/').collect();
|
|
if parts.len() != 6 {
|
|
return Err(anyhow!("Invalid cell state address format: {}", addr));
|
|
}
|
|
|
|
let column: usize = parts[3].parse::<usize>()? - 1; // Convert to 0-based
|
|
let row: usize = parts[4].parse::<usize>()? - 1; // Convert to 0-based
|
|
|
|
if let Some(OscType::String(state_str)) = args.first() {
|
|
let state = Self::track_state_from_osc_string(state_str);
|
|
return Ok(Some(StateUpdate::TrackStateChanged { column, row, state }));
|
|
}
|
|
} else if addr == "/looper/selected/column" {
|
|
if let Some(OscType::Int(column_1based)) = args.first() {
|
|
let column = (*column_1based as usize).saturating_sub(1); // Convert to 0-based
|
|
return Ok(Some(StateUpdate::SelectedColumnChanged { column }));
|
|
}
|
|
} else if addr == "/looper/selected/row" {
|
|
if let Some(OscType::Int(row_1based)) = args.first() {
|
|
let row = (*row_1based as usize).saturating_sub(1); // Convert to 0-based
|
|
return Ok(Some(StateUpdate::SelectedRowChanged { row }));
|
|
}
|
|
}
|
|
|
|
// Unknown or unsupported message
|
|
Ok(None)
|
|
}
|
|
|
|
fn track_state_from_osc_string(s: &str) -> TrackState {
|
|
match s {
|
|
"empty" => TrackState::Empty,
|
|
"ready" => TrackState::Ready,
|
|
"recording" => TrackState::Recording,
|
|
"playing" => TrackState::Playing,
|
|
_ => TrackState::Empty, // Default fallback
|
|
}
|
|
}
|
|
}
|