better quality midi
This commit is contained in:
@@ -2,8 +2,11 @@ mod audio;
|
|||||||
mod link;
|
mod link;
|
||||||
pub mod sequencer;
|
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 audio::{build_stream, AnalysisHandle, AudioStreamConfig, ScopeBuffer, SpectrumBuffer};
|
||||||
pub use link::LinkState;
|
pub use link::LinkState;
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use sequencer::{
|
pub use sequencer::{
|
||||||
spawn_sequencer, AudioCommand, MidiCommand, PatternChange, PatternSnapshot, SeqCommand,
|
spawn_sequencer, AudioCommand, MidiCommand, PatternChange, PatternSnapshot, SeqCommand,
|
||||||
SequencerConfig, SequencerHandle, SequencerSnapshot, StepSnapshot,
|
SequencerConfig, SequencerHandle, SequencerSnapshot, StepSnapshot,
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ pub enum MidiCommand {
|
|||||||
NoteOn { channel: u8, note: u8, velocity: u8 },
|
NoteOn { channel: u8, note: u8, velocity: u8 },
|
||||||
NoteOff { channel: u8, note: u8 },
|
NoteOff { channel: u8, note: u8 },
|
||||||
CC { channel: u8, cc: u8, value: 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 {
|
pub enum SeqCommand {
|
||||||
@@ -1049,6 +1056,28 @@ fn parse_midi_command(cmd: &str) -> Option<(MidiCommand, Option<f64>)> {
|
|||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
"bend" => {
|
||||||
|
// /midi/bend/<value>/chan/<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/<value>/chan/<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/<value>/chan/<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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -249,6 +249,19 @@ fn main() -> io::Result<()> {
|
|||||||
engine::MidiCommand::CC { channel, cc, value } => {
|
engine::MidiCommand::CC { channel, cc, value } => {
|
||||||
app.midi.send_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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
79
src/midi.rs
79
src/midi.rs
@@ -1,10 +1,12 @@
|
|||||||
use midir::{MidiInput, MidiOutput};
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use midir::{MidiInput, MidiOutput};
|
||||||
|
|
||||||
|
use crate::model::CcMemory;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MidiDeviceInfo {
|
pub struct MidiDeviceInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub port_index: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
||||||
@@ -14,12 +16,11 @@ pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
|||||||
midi_out
|
midi_out
|
||||||
.ports()
|
.ports()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.filter_map(|port| {
|
||||||
.filter_map(|(idx, port)| {
|
midi_out
|
||||||
midi_out.port_name(port).ok().map(|name| MidiDeviceInfo {
|
.port_name(port)
|
||||||
name,
|
.ok()
|
||||||
port_index: idx,
|
.map(|name| MidiDeviceInfo { name })
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -31,18 +32,15 @@ pub fn list_midi_inputs() -> Vec<MidiDeviceInfo> {
|
|||||||
midi_in
|
midi_in
|
||||||
.ports()
|
.ports()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.filter_map(|port| {
|
||||||
.filter_map(|(idx, port)| {
|
midi_in
|
||||||
midi_in.port_name(port).ok().map(|name| MidiDeviceInfo {
|
.port_name(port)
|
||||||
name,
|
.ok()
|
||||||
port_index: idx,
|
.map(|name| MidiDeviceInfo { name })
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CcMemory = Arc<Mutex<[[u8; 128]; 16]>>;
|
|
||||||
|
|
||||||
pub struct MidiState {
|
pub struct MidiState {
|
||||||
output_conn: Option<midir::MidiOutputConnection>,
|
output_conn: Option<midir::MidiOutputConnection>,
|
||||||
input_conn: Option<midir::MidiInputConnection<CcMemory>>,
|
input_conn: Option<midir::MidiInputConnection<CcMemory>>,
|
||||||
@@ -80,13 +78,6 @@ impl MidiState {
|
|||||||
Ok(())
|
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> {
|
pub fn connect_input(&mut self, index: usize) -> Result<(), String> {
|
||||||
let midi_in = MidiInput::new("cagire-in").map_err(|e| e.to_string())?;
|
let midi_in = MidiInput::new("cagire-in").map_err(|e| e.to_string())?;
|
||||||
let ports = midi_in.ports();
|
let ports = midi_in.ports();
|
||||||
@@ -120,13 +111,6 @@ impl MidiState {
|
|||||||
Ok(())
|
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) {
|
pub fn send_note_on(&mut self, channel: u8, note: u8, velocity: u8) {
|
||||||
if let Some(conn) = &mut self.output_conn {
|
if let Some(conn) = &mut self.output_conn {
|
||||||
let status = 0x90 | (channel & 0x0F);
|
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 {
|
if let Some(conn) = &mut self.output_conn {
|
||||||
for channel in 0..16u8 {
|
let status = 0xE0 | (channel & 0x0F);
|
||||||
let status = 0xB0 | channel;
|
let lsb = (value & 0x7F) as u8;
|
||||||
let _ = conn.send(&[status, 123, 0]); // CC 123 = All Notes Off
|
let msb = ((value >> 7) & 0x7F) as u8;
|
||||||
}
|
let _ = conn.send(&[status, lsb, msb]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cc(&self, channel: u8, cc: u8) -> u8 {
|
pub fn send_pressure(&mut self, channel: u8, value: u8) {
|
||||||
let channel = (channel as usize).min(15);
|
if let Some(conn) = &mut self.output_conn {
|
||||||
let cc = (cc as usize).min(127);
|
let status = 0xD0 | (channel & 0x0F);
|
||||||
self.cc_memory
|
let _ = conn.send(&[status, value & 0x7F]);
|
||||||
.lock()
|
}
|
||||||
.map(|mem| mem[channel][cc])
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_output_connected(&self) -> bool {
|
pub fn send_program_change(&mut self, channel: u8, program: u8) {
|
||||||
self.output_conn.is_some()
|
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 {
|
pub fn send_realtime(&mut self, msg: u8) {
|
||||||
self.input_conn.is_some()
|
if let Some(conn) = &mut self.output_conn {
|
||||||
|
let _ = conn.send(&[msg]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user