pub mod alsa; use queues::{CircularBuffer, IsQueue}; use crate::config::device::Identifier; use crate::midi::alsa::MidiInputAlsa; use crate::Error; extern crate libc; use crate::config::DeviceConfig; use crate::eventmap::EventMap; use crate::event::{Event, EventBuf}; use std::thread; use std::time::{SystemTime, Instant}; use std::sync::{mpsc, Mutex, Arc}; #[derive(Eq,PartialEq,Debug,Clone)] pub struct MidiPort{ pub name: String, pub addr: T, } #[derive(Debug,Clone)] pub enum PortFilter { All, Name(String), Regex(regex::Regex), Addr(MidiAddrHandler), } #[derive(Debug,Clone,Eq,PartialEq)] pub enum MidiHandlerDriver { ALSA, } #[derive(Debug,Clone,Eq,PartialEq)] pub enum MidiAddrHandler { ALSA(alsa::DeviceAddr), } #[derive(Debug,Clone,Eq,PartialEq)] pub enum MidiPortHandler { ALSA(MidiPort), } pub enum MidiHandler { ALSA(MidiInputAlsa), } impl From for MidiAddrHandler { fn from(a: MidiPortHandler) -> Self { match a { MidiPortHandler::ALSA(p) => MidiAddrHandler::ALSA(p.addr), } } } impl From<&DeviceConfig> for PortFilter { fn from(conf: &DeviceConfig) -> Self { match &conf.identifier { Identifier::All => PortFilter::All, Identifier::Name(s) => PortFilter::Name(s.clone()), Identifier::Regex(s) => PortFilter::Regex(s.clone()), _ => todo!("match type not implemented"), } } } pub trait MidiInput { fn new(client_name: &str) -> Result where Self: Sized; fn close(self) -> Result<(), Error>; fn ports(&self) -> Result>, Error>; fn ports_handle(&self) -> Result, Error>; fn filter_ports(&self, ports: Vec>, filter: PortFilter) -> Vec>; fn connect(&mut self, port_addr: &T, port_name: &str) -> Result<(), Error>; fn device_events(&mut self, ts: mpsc::Sender) -> Result<(), Error>; } pub trait MidiInputHandler { fn signal_stop_input(&self) -> Result<(), Error>; fn handle_input(&mut self, callback: F, rts: (mpsc::Receiver, mpsc::Sender), userdata: D) -> Result<(), Error> where F: Fn(Option, &[u8], &mut D) + Send, D: Send, ; } macro_rules! handler_try_connect { ( $m:expr , $filter:expr, $port:expr, $( $handler:ident ),+ ) => { match $m { $( MidiHandler::$handler(v) => { match $port { MidiPortHandler::$handler(_) => { let maddr = MidiAddrHandler::from($port); let portmap = v.ports()?; let pv = v.filter_ports(portmap, PortFilter::Addr(maddr)); let pv = v.filter_ports(pv, $filter); if pv.len() > 0 { let port = &pv[0]; let mut h = MidiHandler::new_with_driver("rmidimap-handler", MidiHandlerDriver::$handler)?; match &mut h { MidiHandler::$handler(v) => { v.connect(&port.addr, "rmidimap-handler")?; Ok(Some(h)) } _ => panic!("unexpected midi driver failure"), } } else { Ok(None) } }, _ => panic!("unexpected midi driver failure"), } } )* } }; } macro_rules! handler_fcall { ( $m:expr , $fct:expr , $arg:expr , $( $handler:ident ),+ ) => { match $m { $( MidiHandler::$handler(v) => $fct(v, $arg), )* } }; } impl MidiHandler { pub fn new(name: &str) -> Result { Self::new_with_driver(name, MidiHandlerDriver::ALSA) } pub fn new_with_driver(name: &str, driver: MidiHandlerDriver) -> Result { match driver { MidiHandlerDriver::ALSA => Ok(MidiHandler::ALSA(MidiInputAlsa::new(name)?)), } } pub fn ports(&self) -> Result, Error> { handler_fcall!{ self, handle_port_list ,(), ALSA } } pub fn try_connect(&self, addr: MidiPortHandler, filter: PortFilter) -> Result, Error> { let r: Result, Error> = handler_try_connect!{ self, filter, addr, ALSA }; r } pub fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, (rs,ts): (mpsc::Receiver, mpsc::Sender)) -> Result<(), Error> { handler_fcall!{ self, handle_inputport, (conf, eventmap,(rs,ts)), ALSA } } pub fn stop(&self) -> Result<(), Error> { handler_fcall!{ self, handle_signal_stop, (), ALSA } } pub fn device_events(&mut self, ts: mpsc::Sender) -> Result<(), Error> { handler_fcall!{ self, device_events, ts, ALSA } } } fn handle_port_list(input: &T, _: ()) -> Result, Error> where T: MidiInput { input.ports_handle() } fn handle_inputport(input: &mut T, (conf, eventmap, (rs, ts)): (&DeviceConfig, &EventMap, (mpsc::Receiver, mpsc::Sender))) -> Result<(), Error> where T: MidiInputHandler + Send { thread::scope(|s| -> Result<(), Error> { // parking signal for runner, true = stop let (pts,prs) = mpsc::channel::(); let evq = Arc::new(Mutex::new(CircularBuffer::::new(conf.queue_length))); // background execution loop let rq = evq.clone(); let exec_thread = s.spawn(move || -> Result<(),Error> { loop { if prs.recv()? { break; } loop { // nest the lock into a scope to release it before run let (ev,start): (EventBuf,Instant) = { let mut evq = rq.lock().unwrap(); if evq.size() > 0 { (evq.remove().unwrap(), Instant::now()) } else { break; } }; eventmap.run_event(&ev.as_event()).unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e) ); let elapsed_time = start.elapsed(); if elapsed_time < conf.interval { thread::sleep(conf.interval - elapsed_time); } } } Ok(()) }); input.handle_input(|t,m,(evq,pts)| { let mut event: EventBuf = Event::from(m).into(); event.timestamp = t; let mut evq = evq.lock().unwrap(); evq.add(event).unwrap(); pts.send(false).expect("unexpected write error"); }, (rs,ts), (evq,pts.clone()))?; pts.send(true).expect("unexpected write error"); let _ = exec_thread.join(); Ok(()) })?; Ok(()) } fn handle_signal_stop(input: &T, _: ()) -> Result<(), Error> where T: MidiInputHandler { input.signal_stop_input() } fn device_events(input: &mut T, ts: mpsc::Sender) -> Result<(), Error> where T: MidiInput { input.device_events(ts) }