Files
galdr/src/midi.rs
T

94 lines
2.6 KiB
Rust
Raw Normal View History

//! MIDI input. galdr owns a virtual input port named "galdr" (symmetric with
//! orca's virtual output), so orca or any client connects to it in the graph.
//! The channel nibble is kept: tagged columns voice only their channel.
use anyhow::{anyhow, Result};
use midir::os::unix::VirtualInput;
use midir::{MidiInput, MidiInputConnection};
use rtrb::Producer;
use crate::engine::Event;
/// Decode one MIDI message into an engine event.
pub fn decode(msg: &[u8]) -> Option<Event> {
if msg.len() < 3 {
return None;
}
let ch = msg[0] & 0x0F;
match msg[0] & 0xF0 {
0x90 if msg[2] > 0 => Some(Event::NoteOn {
ch,
note: msg[1],
vel: msg[2],
}),
0x80 | 0x90 => Some(Event::NoteOff { ch, note: msg[1] }), // note-on velocity 0
0xB0 => Some(Event::Cc {
idx: msg[1],
val: msg[2],
}),
_ => None,
}
}
pub fn connect(
prod: Producer<Event>,
port_name: Option<String>,
) -> Result<MidiInputConnection<Producer<Event>>> {
let mut input = MidiInput::new("galdr").map_err(|e| anyhow!("MIDI init: {e}"))?;
input.ignore(midir::Ignore::None);
let cb = move |_stamp: u64, msg: &[u8], prod: &mut Producer<Event>| {
if let Some(ev) = decode(msg) {
let _ = prod.push(ev);
}
};
if let Some(name) = port_name {
let ports = input.ports();
let port = ports
.iter()
.find(|p| {
input
.port_name(p)
.map(|n| n.contains(&name))
.unwrap_or(false)
})
.cloned()
.ok_or_else(|| anyhow!("no MIDI input port matching {name:?}"))?;
input
.connect(&port, "galdr-in", cb, prod)
.map_err(|e| anyhow!("MIDI connect: {e}"))
} else {
input
.create_virtual("galdr", cb, prod)
.map_err(|e| anyhow!("create virtual MIDI port: {e}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_keeps_channel() {
assert_eq!(
decode(&[0x92, 60, 100]),
Some(Event::NoteOn {
ch: 2,
note: 60,
vel: 100
})
);
assert_eq!(
decode(&[0x8F, 60, 0]),
Some(Event::NoteOff { ch: 15, note: 60 })
);
assert_eq!(
decode(&[0x90, 60, 0]),
Some(Event::NoteOff { ch: 0, note: 60 })
);
assert_eq!(decode(&[0xB3, 7, 64]), Some(Event::Cc { idx: 7, val: 64 }));
assert_eq!(decode(&[0xF8]), None);
}
}