picoKontroller/src/midi_client.rs

72 lines
2.2 KiB
Rust

use crate::{config::NanoKeys, interaction_server::InteractionServer};
use crossbeam_channel::Sender;
use midir::{Ignore, MidiInput};
use regex::Regex;
use std::thread::{self, JoinHandle};
use std::time;
const CLIENT_NAME: &str = "PicoKontroller";
const PORT_NAME: &str = "PicoController Input";
pub struct MidiClient {
pub device_name: String,
pub out_channel: Sender<KeyEvent>,
}
pub type KeyEvent = (NanoKeys, u8);
impl InteractionServer for MidiClient {
fn start(self) -> JoinHandle<()> {
thread::spawn(move || {
self.exec();
})
}
}
impl MidiClient {
pub fn new(device_name: String, out_channel: Sender<KeyEvent>) -> MidiClient {
MidiClient {
device_name,
out_channel,
}
}
fn exec(self) {
let port_filter = Regex::new(&self.device_name).expect("Creating RegEx failed");
let mut midi_in = MidiInput::new(CLIENT_NAME).expect("Creating Midi device failed");
midi_in.ignore(Ignore::None);
let in_ports = midi_in.ports();
let in_port = in_ports
.iter()
.find(|p| port_filter.is_match(&midi_in.port_name(p).unwrap()))
.expect("Couldn't find a port matching the supplied RegEx");
// _conn_in needs to be a named parameter, because it needs to be kept alive until the end of the scope
let _conn_in = midi_in.connect(
in_port,
PORT_NAME,
move |_, message, _| {
let key = NanoKeys::try_from(message[1]).unwrap();
let state = message[2];
let _ = self.out_channel.send((key, state));
},
(),
);
loop {
// sleep forever, callback functions happen on separate thread
// couldn't reuse this thread, since _conn_in needs to be alive in this scope
std::thread::sleep(time::Duration::from_secs(u64::MAX));
}
}
}
pub fn list_ports() -> () {
let midi_in = MidiInput::new(CLIENT_NAME).expect("Creating Midi device failed");
let in_ports = midi_in.ports();
println!("All available midi devices:");
for (_, p) in in_ports.iter().enumerate() {
println!("{}", midi_in.port_name(p).unwrap());
}
}