use alsa::seq::{ClientIter, EventType, PortCap, PortIter, PortType, Seq};
use std::sync::mpsc::SyncSender;
pub enum MidiEvent {
Msg(u32),
Sysex(Vec<u8>),
}
pub fn list_ports() {
let seq = Seq::open(None, None, false).expect("failed to open sequencer");
for ci in ClientIter::new(&seq) {
let cid = ci.get_client();
let cname = ci.get_name().unwrap_or_default();
for pi in PortIter::new(&seq, cid) {
let pid = pi.get_port();
let pname = pi.get_name().unwrap_or_default();
let caps = pi.get_capability();
if caps.contains(PortCap::READ) || caps.contains(PortCap::SUBS_READ)
{
println!("{cid}:{pid}\t{cname} - {pname}");
}
}
}
}
pub fn open_port(tx: SyncSender<MidiEvent>) -> std::thread::JoinHandle<()> {
let seq = Seq::open(None, None, false).expect("failed to open sequencer");
seq.set_client_name(&std::ffi::CString::new("moont").unwrap())
.expect("failed to set client name");
let caps = PortCap::WRITE | PortCap::SUBS_WRITE;
let ptype = PortType::MIDI_GENERIC | PortType::APPLICATION;
let port = seq
.create_simple_port(
&std::ffi::CString::new("moont").unwrap(),
caps,
ptype,
)
.expect("failed to create ALSA sequencer port");
let cid = seq.client_id().expect("failed to get client id");
eprintln!("ALSA MIDI port: {cid}:{port}");
eprintln!("Connect with: aconnect <source> {cid}:{port}");
std::thread::spawn(move || {
let mut input = seq.input();
loop {
let ev = match input.event_input() {
Ok(ev) => ev,
Err(_) => continue,
};
let evt = match ev.get_type() {
EventType::Noteon => {
let d: alsa::seq::EvNote = ev.get_data().unwrap();
let status = 0x90 | (d.channel as u32);
let msg = status
| ((d.note as u32) << 8)
| ((d.velocity as u32) << 16);
Some(MidiEvent::Msg(msg))
}
EventType::Noteoff => {
let d: alsa::seq::EvNote = ev.get_data().unwrap();
let status = 0x80 | (d.channel as u32);
let msg = status | ((d.note as u32) << 8);
Some(MidiEvent::Msg(msg))
}
EventType::Controller => {
let d: alsa::seq::EvCtrl = ev.get_data().unwrap();
let status = 0xB0 | (d.channel as u32);
let msg = status
| ((d.param as u32) << 8)
| ((d.value as u32) << 16);
Some(MidiEvent::Msg(msg))
}
EventType::Pgmchange => {
let d: alsa::seq::EvCtrl = ev.get_data().unwrap();
let status = 0xC0 | (d.channel as u32);
let msg = status | ((d.value as u32) << 8);
Some(MidiEvent::Msg(msg))
}
EventType::Pitchbend => {
let d: alsa::seq::EvCtrl = ev.get_data().unwrap();
let bend = (d.value + 8192) as u16;
let status = 0xE0 | (d.channel as u32);
let msg = status
| (((bend & 0x7F) as u32) << 8)
| (((bend >> 7) as u32) << 16);
Some(MidiEvent::Msg(msg))
}
EventType::Sysex => {
if let Some(data) = ev.get_ext() {
Some(MidiEvent::Sysex(data.to_vec()))
} else {
None
}
}
_ => None,
};
if let Some(e) = evt {
if tx.send(e).is_err() {
break;
}
}
}
})
}