use crate::*; #[derive(Debug)] pub struct JackPort { /// Handle to JACK client, for receiving reconnect events. pub jack: Arc>, /// Port handle. pub port: Port, /// List of ports to connect to. pub connect: Vec } impl JackPort { pub fn connect_to_matching (&mut self) { use PortConnectionName::*; use PortConnectionScope::*; use PortConnectionStatus::*; for connect in self.connect.iter_mut() { let mut status = vec![]; match &connect.name { Exact(name) => for port in self.jack.ports(None, None, PortFlags::empty()).iter() { if port.as_str() == &**name { if let Some(port) = self.jack.port_by_name(port.as_str()) { let port_status = Self::try_both_ways(&self.jack, &port, &self.port); status.push((port, port_status)); if port_status == Connected { break } } } }, RegExp(re) => for port in self.jack.ports(Some(&re), None, PortFlags::empty()).iter() { if let Some(port) = self.jack.port_by_name(port.as_str()) { let port_status = Self::try_both_ways(&self.jack, &port, &self.port); status.push((port, port_status)); if port_status == Connected && connect.scope == One { break } } } } connect.status = status } } fn try_both_ways ( jack: &impl ConnectPort, port_a: &Port, port_b: &Port ) -> PortConnectionStatus { if let Ok(_) = jack.connect_ports(port_a, port_b) { PortConnectionStatus::Connected } else if let Ok(_) = jack.connect_ports(port_b, port_a) { PortConnectionStatus::Connected } else { PortConnectionStatus::Mismatch } } } #[derive(Clone, Debug, PartialEq)] pub struct PortConnection { pub name: PortConnectionName, pub scope: PortConnectionScope, pub status: Vec<(Port, PortConnectionStatus)>, } impl PortConnection { /// Connect to this exact port pub fn exact (name: impl AsRef) -> Self { let name = PortConnectionName::Exact(name.as_ref().into()); Self { name, scope: PortConnectionScope::One, status: vec![] } } pub fn regexp (name: impl AsRef) -> Self { let name = PortConnectionName::RegExp(name.as_ref().into()); Self { name, scope: PortConnectionScope::One, status: vec![] } } pub fn regexp_all (name: impl AsRef) -> Self { let name = PortConnectionName::RegExp(name.as_ref().into()); Self { name, scope: PortConnectionScope::All, status: vec![] } } } #[derive(Clone, Debug, PartialEq)] pub enum PortConnectionName { /** Exact match */ Exact(Arc), /** Match regular expression */ RegExp(Arc), } #[derive(Clone, Copy, Debug, PartialEq)] pub enum PortConnectionScope { One, All } #[derive(Clone, Copy, Debug, PartialEq)] pub enum PortConnectionStatus { Missing, Disconnected, Connected, Mismatch, } impl AsRef> for JackPort { fn as_ref (&self) -> &Port { &self.port } } impl JackPort { pub fn new ( jack: &Arc>, name: impl AsRef, connect: &[PortConnection] ) -> Usually { let mut port = JackPort { jack: jack.clone(), port: jack.midi_in(name)?, connect: connect.to_vec() }; port.connect_to_matching(); Ok(port) } } impl JackPort { pub fn new ( jack: &Arc>, name: impl AsRef, connect: &[PortConnection] ) -> Usually { let mut port = Self { jack: jack.clone(), port: jack.midi_out(name)?, connect: connect.to_vec() }; port.connect_to_matching(); Ok(port) } } impl JackPort { pub fn new ( jack: &Arc>, name: impl AsRef, connect: &[PortConnection] ) -> Usually { let mut port = Self { jack: jack.clone(), port: jack.audio_in(name)?, connect: connect.to_vec() }; port.connect_to_matching(); Ok(port) } } impl JackPort { pub fn new ( jack: &Arc>, name: impl AsRef, connect: &[PortConnection] ) -> Usually { let mut port = Self { jack: jack.clone(), port: jack.audio_out(name)?, connect: connect.to_vec() }; port.connect_to_matching(); Ok(port) } } pub trait ConnectPort { fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec; fn port_by_name (&self, name: impl AsRef) -> Option>; fn connect_ports (&self, source: &Port, target: &Port) -> Usually<()>; fn connect_midi_from (&self, input: &Port, ports: &[impl AsRef]) -> Usually<()> { for port in ports.iter() { let port = port.as_ref(); if let Some(port) = self.port_by_name(port).as_ref() { self.connect_ports(port, input)?; } else { panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names."); } } Ok(()) } fn connect_midi_to (&self, output: &Port, ports: &[impl AsRef]) -> Usually<()> { for port in ports.iter() { let port = port.as_ref(); if let Some(port) = self.port_by_name(port).as_ref() { self.connect_ports(output, port)?; } else { panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names."); } } Ok(()) } fn connect_audio_from (&self, input: &Port, ports: &[impl AsRef]) -> Usually<()> { for port in ports.iter() { let port = port.as_ref(); if let Some(port) = self.port_by_name(port).as_ref() { self.connect_ports(port, input)?; } else { panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names."); } } Ok(()) } fn connect_audio_to (&self, output: &Port, ports: &[impl AsRef]) -> Usually<()> { for port in ports.iter() { let port = port.as_ref(); if let Some(port) = self.port_by_name(port).as_ref() { self.connect_ports(output, port)?; } else { panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names."); } } Ok(()) } } impl ConnectPort for JackConnection { fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec { self.client().ports(re_name, re_type, flags) } fn port_by_name (&self, name: impl AsRef) -> Option> { self.client().port_by_name(name.as_ref()) } fn connect_ports (&self, source: &Port, target: &Port) -> Usually<()> { Ok(self.client().connect_ports(source, target)?) } } impl ConnectPort for Arc> { fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec { self.read().unwrap().ports(re_name, re_type, flags) } fn port_by_name (&self, name: impl AsRef) -> Option> { self.read().unwrap().port_by_name(name.as_ref()) } fn connect_ports (&self, source: &Port, target: &Port) -> Usually<()> { Ok(self.read().unwrap().connect_ports(source, target)?) } } /// This is a utility trait for things that may register or connect [Port]s. /// It contains shorthand methods to this purpose. It's implemented for /// `Arc>` for terse port registration in the /// `init` callback of [JackClient::activate_with]. pub trait RegisterPort { fn midi_in (&self, name: impl AsRef) -> Usually>; fn midi_out (&self, name: impl AsRef) -> Usually>; fn audio_in (&self, name: impl AsRef) -> Usually>; fn audio_out (&self, name: impl AsRef) -> Usually>; } impl RegisterPort for JackConnection { fn midi_in (&self, name: impl AsRef) -> Usually> { Ok(self.client().register_port(name.as_ref(), MidiIn::default())?) } fn midi_out (&self, name: impl AsRef) -> Usually> { Ok(self.client().register_port(name.as_ref(), MidiOut::default())?) } fn audio_in (&self, name: impl AsRef) -> Usually> { Ok(self.client().register_port(name.as_ref(), AudioIn::default())?) } fn audio_out (&self, name: impl AsRef) -> Usually> { Ok(self.client().register_port(name.as_ref(), AudioOut::default())?) } } impl RegisterPort for Arc> { fn midi_in (&self, name: impl AsRef) -> Usually> { self.read().unwrap().midi_in(name) } fn midi_out (&self, name: impl AsRef) -> Usually> { self.read().unwrap().midi_out(name) } fn audio_in (&self, name: impl AsRef) -> Usually> { self.read().unwrap().audio_in(name) } fn audio_out (&self, name: impl AsRef) -> Usually> { self.read().unwrap().audio_out(name) } } ///// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut]. //#[derive(Default, Debug)] //pub struct JackPorts { //pub audio_ins: BTreeMap>, //pub midi_ins: BTreeMap>, //pub audio_outs: BTreeMap>, //pub midi_outs: BTreeMap>, //} ///// Collection of JACK ports as [Unowned]. //#[derive(Default, Debug)] //pub struct UnownedJackPorts { //pub audio_ins: BTreeMap>, //pub midi_ins: BTreeMap>, //pub audio_outs: BTreeMap>, //pub midi_outs: BTreeMap>, //} //impl JackPorts { //pub fn clone_unowned(&self) -> UnownedJackPorts { //let mut unowned = UnownedJackPorts::default(); //for (name, port) in self.midi_ins.iter() { //unowned.midi_ins.insert(name.clone(), port.clone_unowned()); //} //for (name, port) in self.midi_outs.iter() { //unowned.midi_outs.insert(name.clone(), port.clone_unowned()); //} //for (name, port) in self.audio_ins.iter() { //unowned.audio_ins.insert(name.clone(), port.clone_unowned()); //} //for (name, port) in self.audio_outs.iter() { //unowned //.audio_outs //.insert(name.clone(), port.clone_unowned()); //} //unowned //} //} ///// Implement the `Ports` trait. //#[macro_export] //macro_rules! ports { //($T:ty $({ $(audio: { //$(ins: |$ai_arg:ident|$ai_impl:expr,)? //$(outs: |$ao_arg:ident|$ao_impl:expr,)? //})? $(midi: { //$(ins: |$mi_arg:ident|$mi_impl:expr,)? //$(outs: |$mo_arg:ident|$mo_impl:expr,)? //})?})?) => { //impl Ports for $T {$( //$( //$(fn audio_ins <'a> (&'a self) -> Usually>> { //let cb = |$ai_arg:&'a Self|$ai_impl; //cb(self) //})? //)? //$( //$(fn audio_outs <'a> (&'a self) -> Usually>> { //let cb = (|$ao_arg:&'a Self|$ao_impl); //cb(self) //})? //)? //)? $( //$( //$(fn midi_ins <'a> (&'a self) -> Usually>> { //let cb = (|$mi_arg:&'a Self|$mi_impl); //cb(self) //})? //)? //$( //$(fn midi_outs <'a> (&'a self) -> Usually>> { //let cb = (|$mo_arg:&'a Self|$mo_impl); //cb(self) //})? //)? //)?} //}; //} /// Trait for things that may expose JACK ports. pub trait Ports { fn audio_ins(&self) -> Usually>> { Ok(vec![]) } fn audio_outs(&self) -> Usually>> { Ok(vec![]) } fn midi_ins(&self) -> Usually>> { Ok(vec![]) } fn midi_outs(&self) -> Usually>> { Ok(vec![]) } } fn register_ports( client: &Client, names: Vec, spec: T, ) -> Usually>> { names .into_iter() .try_fold(BTreeMap::new(), |mut ports, name| { let port = client.register_port(&name, spec)?; ports.insert(name, port); Ok(ports) }) } fn query_ports(client: &Client, names: Vec) -> BTreeMap> { names.into_iter().fold(BTreeMap::new(), |mut ports, name| { let port = client.port_by_name(&name).unwrap(); ports.insert(name, port); ports }) }