Fix and display metronome

This commit is contained in:
2025-06-21 22:58:49 +02:00
parent 89645db1d9
commit 7e6a539296
19 changed files with 345 additions and 131 deletions

View File

@@ -6,5 +6,6 @@ edition = "2021"
[dependencies]
strum = { version = "0.23", features = ["derive"] }
log.workspace = true
rosc.workspace = true
thiserror.workspace = true

View File

@@ -10,8 +10,13 @@ pub trait AddressParseResult<T> {
fn address_part_result(self, context: &'static str) -> Result<T>;
}
impl <T> AddressParseResult <T> for std::result::Result<T, std::num::ParseIntError> {
impl<T> AddressParseResult<T> for std::result::Result<T, std::num::ParseIntError> {
fn address_part_result(self, context: &'static str) -> Result<T> {
self.map_err(|e| Error::AddressParseError(format!("failed to parse number in address part {}: {}", context, e)))
self.map_err(|e| {
Error::AddressParseError(format!(
"failed to parse number in address part {}: {}",
context, e
))
})
}
}
}

View File

@@ -8,4 +8,4 @@ pub use error::Result;
pub use message::Message;
pub use state::State;
pub use state::Track;
pub use state::TrackState;
pub use state::TrackState;

View File

@@ -77,6 +77,7 @@ impl Message {
if let Some(rosc::OscType::String(state_str)) = args.first() {
let state = state_str.parse::<TrackState>().unwrap_or(TrackState::Empty);
log::trace!("TrackStateChanged: column={column}, row={row}, state={state}");
return Ok(Some(Message::TrackStateChanged { column, row, state }));
}
} else if addr.starts_with("/looper/cell/") && addr.ends_with("/volume") {
@@ -90,26 +91,30 @@ impl Message {
let row: usize = parts[4].parse::<usize>().address_part_result("row")? - 1; // Convert to 0-based
if let Some(rosc::OscType::Float(volume)) = args.first() {
return Ok(Some(Message::TrackVolumeChanged {
column,
row,
volume: *volume
log::trace!("TrackVolumeChanged: column={column}, row={row}, volume={volume}");
return Ok(Some(Message::TrackVolumeChanged {
column,
row,
volume: *volume,
}));
}
} else if addr == "/looper/selected/column" {
if let Some(rosc::OscType::Int(column_1based)) = args.first() {
log::trace!("SelectedColumnChanged: column={column_1based}");
let column = (*column_1based as usize).saturating_sub(1); // Convert to 0-based
return Ok(Some(Message::SelectedColumnChanged { column }));
}
} else if addr == "/looper/selected/row" {
if let Some(rosc::OscType::Int(row_1based)) = args.first() {
log::trace!("SelectedRowChanged: row={row_1based}");
let row = (*row_1based as usize).saturating_sub(1); // Convert to 0-based
return Ok(Some(Message::SelectedRowChanged { row }));
}
} else if addr == "/looper/metronome/position" {
if let Some(rosc::OscType::Float(position)) = args.first() {
return Ok(Some(Message::MetronomePosition {
position: *position
log::trace!("MetronomePosition: position={position}");
return Ok(Some(Message::MetronomePosition {
position: *position,
}));
}
}
@@ -121,15 +126,19 @@ impl Message {
match self {
Message::TrackStateChanged { column, row, state } => {
let address = format!("/looper/cell/{}/{}/state", column + 1, row + 1); // 1-based indexing
rosc::OscPacket::Message(rosc::OscMessage {
addr: address,
args: vec![rosc::OscType::String(state.to_string())],
})
}
Message::TrackVolumeChanged { column, row, volume } => {
Message::TrackVolumeChanged {
column,
row,
volume,
} => {
let address = format!("/looper/cell/{}/{}/volume", column + 1, row + 1); // 1-based indexing
rosc::OscPacket::Message(rosc::OscMessage {
addr: address,
args: vec![rosc::OscType::Float(volume)],
@@ -137,7 +146,7 @@ impl Message {
}
Message::SelectedColumnChanged { column } => {
let address = "/looper/selected/column".to_string();
rosc::OscPacket::Message(rosc::OscMessage {
addr: address,
args: vec![rosc::OscType::Int((column + 1) as i32)], // 1-based indexing
@@ -145,7 +154,7 @@ impl Message {
}
Message::SelectedRowChanged { row } => {
let address = "/looper/selected/row".to_string();
rosc::OscPacket::Message(rosc::OscMessage {
addr: address,
args: vec![rosc::OscType::Int((row + 1) as i32)], // 1-based indexing
@@ -153,7 +162,7 @@ impl Message {
}
Message::MetronomePosition { position } => {
let address = "/looper/metronome/position".to_string();
rosc::OscPacket::Message(rosc::OscMessage {
addr: address,
args: vec![rosc::OscType::Float(position)],
@@ -161,4 +170,4 @@ impl Message {
}
}
}
}
}

View File

@@ -16,8 +16,8 @@ pub struct Track {
#[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 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,
@@ -28,9 +28,17 @@ pub struct State {
impl State {
pub fn new(columns: usize, rows: usize) -> Self {
let cells = (0..columns)
.map(|_| vec![Track { state: TrackState::Empty, volume: 1.0 }; rows])
.map(|_| {
vec![
Track {
state: TrackState::Empty,
volume: 1.0
};
rows
]
})
.collect();
Self {
selected_column: 0,
selected_row: 0,
@@ -49,7 +57,11 @@ impl State {
self.cells[*column][*row].state = state.clone();
}
}
Message::TrackVolumeChanged { column, row, volume } => {
Message::TrackVolumeChanged {
column,
row,
volume,
} => {
if *column < self.columns && *row < self.rows {
self.cells[*column][*row].volume = *volume;
}
@@ -128,4 +140,4 @@ impl State {
self.metronome_position = position;
self.metronome_timestamp = std::time::Instant::now();
}
}
}