From dfd024cab7eaea6898530ea8f152f09b19e5cd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Sat, 31 Jan 2026 23:23:36 +0100 Subject: [PATCH] better quality midi --- src/engine/mod.rs | 3 ++ src/engine/sequencer.rs | 29 +++++++++++++++ src/main.rs | 13 +++++++ src/midi.rs | 79 +++++++++++++++++------------------------ 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/engine/mod.rs b/src/engine/mod.rs index d2eef50..b76e29d 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -2,8 +2,11 @@ mod audio; mod link; pub mod sequencer; +// AnalysisHandle and SequencerHandle are used by src/bin/desktop.rs +#[allow(unused_imports)] pub use audio::{build_stream, AnalysisHandle, AudioStreamConfig, ScopeBuffer, SpectrumBuffer}; pub use link::LinkState; +#[allow(unused_imports)] pub use sequencer::{ spawn_sequencer, AudioCommand, MidiCommand, PatternChange, PatternSnapshot, SeqCommand, SequencerConfig, SequencerHandle, SequencerSnapshot, StepSnapshot, diff --git a/src/engine/sequencer.rs b/src/engine/sequencer.rs index e04c38f..340691a 100644 --- a/src/engine/sequencer.rs +++ b/src/engine/sequencer.rs @@ -56,6 +56,13 @@ pub enum MidiCommand { NoteOn { channel: u8, note: u8, velocity: u8 }, NoteOff { channel: u8, note: u8 }, CC { channel: u8, cc: u8, value: u8 }, + PitchBend { channel: u8, value: u16 }, + Pressure { channel: u8, value: u8 }, + ProgramChange { channel: u8, program: u8 }, + Clock, + Start, + Stop, + Continue, } pub enum SeqCommand { @@ -1049,6 +1056,28 @@ fn parse_midi_command(cmd: &str) -> Option<(MidiCommand, Option)> { None, )) } + "bend" => { + // /midi/bend//chan/ + let value: u16 = parts.get(2)?.parse().ok()?; + let chan: u8 = parts.get(4)?.parse().ok()?; + Some((MidiCommand::PitchBend { channel: chan, value }, None)) + } + "pressure" => { + // /midi/pressure//chan/ + let value: u8 = parts.get(2)?.parse().ok()?; + let chan: u8 = parts.get(4)?.parse().ok()?; + Some((MidiCommand::Pressure { channel: chan, value }, None)) + } + "program" => { + // /midi/program//chan/ + let program: u8 = parts.get(2)?.parse().ok()?; + let chan: u8 = parts.get(4)?.parse().ok()?; + Some((MidiCommand::ProgramChange { channel: chan, program }, None)) + } + "clock" => Some((MidiCommand::Clock, None)), + "start" => Some((MidiCommand::Start, None)), + "stop" => Some((MidiCommand::Stop, None)), + "continue" => Some((MidiCommand::Continue, None)), _ => None, } } diff --git a/src/main.rs b/src/main.rs index 92ed83b..6426468 100644 --- a/src/main.rs +++ b/src/main.rs @@ -249,6 +249,19 @@ fn main() -> io::Result<()> { engine::MidiCommand::CC { channel, cc, value } => { app.midi.send_cc(channel, cc, value); } + engine::MidiCommand::PitchBend { channel, value } => { + app.midi.send_pitch_bend(channel, value); + } + engine::MidiCommand::Pressure { channel, value } => { + app.midi.send_pressure(channel, value); + } + engine::MidiCommand::ProgramChange { channel, program } => { + app.midi.send_program_change(channel, program); + } + engine::MidiCommand::Clock => app.midi.send_realtime(0xF8), + engine::MidiCommand::Start => app.midi.send_realtime(0xFA), + engine::MidiCommand::Stop => app.midi.send_realtime(0xFC), + engine::MidiCommand::Continue => app.midi.send_realtime(0xFB), } } diff --git a/src/midi.rs b/src/midi.rs index f6a5782..997d9fe 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -1,10 +1,12 @@ -use midir::{MidiInput, MidiOutput}; use std::sync::{Arc, Mutex}; +use midir::{MidiInput, MidiOutput}; + +use crate::model::CcMemory; + #[derive(Clone, Debug)] pub struct MidiDeviceInfo { pub name: String, - pub port_index: usize, } pub fn list_midi_outputs() -> Vec { @@ -14,12 +16,11 @@ pub fn list_midi_outputs() -> Vec { midi_out .ports() .iter() - .enumerate() - .filter_map(|(idx, port)| { - midi_out.port_name(port).ok().map(|name| MidiDeviceInfo { - name, - port_index: idx, - }) + .filter_map(|port| { + midi_out + .port_name(port) + .ok() + .map(|name| MidiDeviceInfo { name }) }) .collect() } @@ -31,18 +32,15 @@ pub fn list_midi_inputs() -> Vec { midi_in .ports() .iter() - .enumerate() - .filter_map(|(idx, port)| { - midi_in.port_name(port).ok().map(|name| MidiDeviceInfo { - name, - port_index: idx, - }) + .filter_map(|port| { + midi_in + .port_name(port) + .ok() + .map(|name| MidiDeviceInfo { name }) }) .collect() } -pub type CcMemory = Arc>; - pub struct MidiState { output_conn: Option, input_conn: Option>, @@ -80,13 +78,6 @@ impl MidiState { Ok(()) } - pub fn disconnect_output(&mut self) { - if let Some(conn) = self.output_conn.take() { - conn.close(); - } - self.selected_output = None; - } - pub fn connect_input(&mut self, index: usize) -> Result<(), String> { let midi_in = MidiInput::new("cagire-in").map_err(|e| e.to_string())?; let ports = midi_in.ports(); @@ -120,13 +111,6 @@ impl MidiState { Ok(()) } - pub fn disconnect_input(&mut self) { - if let Some(conn) = self.input_conn.take() { - conn.close(); - } - self.selected_input = None; - } - pub fn send_note_on(&mut self, channel: u8, note: u8, velocity: u8) { if let Some(conn) = &mut self.output_conn { let status = 0x90 | (channel & 0x0F); @@ -148,29 +132,32 @@ impl MidiState { } } - pub fn send_all_notes_off(&mut self) { + pub fn send_pitch_bend(&mut self, channel: u8, value: u16) { if let Some(conn) = &mut self.output_conn { - for channel in 0..16u8 { - let status = 0xB0 | channel; - let _ = conn.send(&[status, 123, 0]); // CC 123 = All Notes Off - } + let status = 0xE0 | (channel & 0x0F); + let lsb = (value & 0x7F) as u8; + let msb = ((value >> 7) & 0x7F) as u8; + let _ = conn.send(&[status, lsb, msb]); } } - pub fn get_cc(&self, channel: u8, cc: u8) -> u8 { - let channel = (channel as usize).min(15); - let cc = (cc as usize).min(127); - self.cc_memory - .lock() - .map(|mem| mem[channel][cc]) - .unwrap_or(0) + pub fn send_pressure(&mut self, channel: u8, value: u8) { + if let Some(conn) = &mut self.output_conn { + let status = 0xD0 | (channel & 0x0F); + let _ = conn.send(&[status, value & 0x7F]); + } } - pub fn is_output_connected(&self) -> bool { - self.output_conn.is_some() + pub fn send_program_change(&mut self, channel: u8, program: u8) { + if let Some(conn) = &mut self.output_conn { + let status = 0xC0 | (channel & 0x0F); + let _ = conn.send(&[status, program & 0x7F]); + } } - pub fn is_input_connected(&self) -> bool { - self.input_conn.is_some() + pub fn send_realtime(&mut self, msg: u8) { + if let Some(conn) = &mut self.output_conn { + let _ = conn.send(&[msg]); + } } }