use crate::*; macro_rules! impl_port { ($Name:ident : $Spec:ident -> $Pair:ident |$jack:ident, $name:ident|$port:expr) => { #[derive(Debug)] pub struct $Name { /// Handle to JACK client, for receiving reconnect events. jack: Jack, /// Port name name: Arc, /// Port handle. port: Port<$Spec>, /// List of ports to connect to. conn: Vec } impl AsRef> for $Name { fn as_ref (&self) -> &Port<$Spec> { &self.port } } impl $Name { pub fn name (&self) -> &str { self.name.as_ref() } pub fn port (&self) -> &Port<$Spec> { &self.port } pub fn port_mut (&mut self) -> &mut Port<$Spec> { &mut self.port } pub fn new ($jack: &Jack, name: impl AsRef, connect: &[PortConnect]) -> Usually { let $name = name.as_ref(); let jack = $jack.clone(); let port = $port?; let name = $name.into(); let conn = connect.to_vec(); let port = Self { jack, port, name, conn }; port.connect_to_matching()?; Ok(port) } } impl HasJack for $Name { fn jack (&self) -> &Jack { &self.jack } } impl JackPort for $Name { type Port = $Spec; type Pair = $Pair; fn port (&self) -> &Port<$Spec> { &self.port } } impl JackPortConnect<&str> for $Name { fn connect_to (&self, to: &str) -> Usually { self.with_client(|c|if let Some(ref port) = c.port_by_name(to.as_ref()) { self.connect_to(port) } else { Ok(Missing) }) } } impl JackPortConnect<&Port> for $Name { fn connect_to (&self, port: &Port) -> Usually { self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(&self.port, port) { Connected } else if let Ok(_) = c.connect_ports(port, &self.port) { Connected } else { Mismatch })) } } impl JackPortConnect<&Port<$Pair>> for $Name { fn connect_to (&self, port: &Port<$Pair>) -> Usually { self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(&self.port, port) { Connected } else if let Ok(_) = c.connect_ports(port, &self.port) { Connected } else { Mismatch })) } } impl JackPortAutoconnect for $Name { fn conn (&self) -> &[PortConnect] { &self.conn } } }; } impl_port!(JackAudioIn: AudioIn -> AudioOut |j, n|j.register_port::(n)); impl_port!(JackAudioOut: AudioOut -> AudioIn |j, n|j.register_port::(n)); impl_port!(JackMidiIn: MidiIn -> MidiOut |j, n|j.register_port::(n)); impl_port!(JackMidiOut: MidiOut -> MidiIn |j, n|j.register_port::(n)); pub trait JackPort: HasJack { type Port: PortSpec; type Pair: PortSpec; fn port (&self) -> &Port; } pub trait JackPortConnect: JackPort { fn connect_to (&self, to: T) -> Usually; } pub trait JackPortAutoconnect: JackPort + for<'a>JackPortConnect<&'a Port> { fn conn (&self) -> &[PortConnect]; fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec { self.with_client(|c|c.ports(re_name, re_type, flags)) } fn port_by_name (&self, name: impl AsRef) -> Option> { self.with_client(|c|c.port_by_name(name.as_ref())) } fn connect_to_matching (&self) -> Usually<()> { for connect in self.conn().iter() { let status = match &connect.name { Exact(name) => self.connect_exact(name), RegExp(re) => self.connect_regexp(re, connect.scope), }?; *connect.status.write().unwrap() = status; } Ok(()) } fn connect_exact ( &self, name: &str ) -> Usually, Arc, PortConnectStatus)>> { self.with_client(|c|{ let mut status = vec![]; for port in c.ports(None, None, PortFlags::empty()).iter() { if port.as_str() == &*name { if let Some(port) = c.port_by_name(port.as_str()) { let port_status = self.connect_to(&port)?; let name = port.name()?.into(); status.push((port, name, port_status)); if port_status == Connected { break } } } } Ok(status) }) } fn connect_regexp ( &self, re: &str, scope: PortConnectScope ) -> Usually, Arc, PortConnectStatus)>> { self.with_client(|c|{ let mut status = vec![]; let ports = c.ports(Some(&re), None, PortFlags::empty()); for port in ports.iter() { if let Some(port) = c.port_by_name(port.as_str()) { let port_status = self.connect_to(&port)?; let name = port.name()?.into(); status.push((port, name, port_status)); if port_status == Connected && scope == One { break } } } Ok(status) }) } } #[derive(Clone, Debug, PartialEq)] pub enum PortConnectName { /** Exact match */ Exact(Arc), /** Match regular expression */ RegExp(Arc), } #[derive(Clone, Copy, Debug, PartialEq)] pub enum PortConnectScope { One, All } #[derive(Clone, Copy, Debug, PartialEq)] pub enum PortConnectStatus { Missing, Disconnected, Connected, Mismatch, } #[derive(Clone, Debug)] pub struct PortConnect { pub name: PortConnectName, pub scope: PortConnectScope, pub status: Arc, Arc, PortConnectStatus)>>>, } impl PortConnect { pub fn collect (exact: &[impl AsRef], re: &[impl AsRef], re_all: &[impl AsRef]) -> Vec { let mut connections = vec![]; for port in exact.iter() { connections.push(Self::exact(port)) } for port in re.iter() { connections.push(Self::regexp(port)) } for port in re_all.iter() { connections.push(Self::regexp_all(port)) } connections } /// Connect to this exact port pub fn exact (name: impl AsRef) -> Self { let name = Exact(name.as_ref().into()); Self { name, scope: One, status: Arc::new(RwLock::new(vec![])) } } pub fn regexp (name: impl AsRef) -> Self { let name = RegExp(name.as_ref().into()); Self { name, scope: One, status: Arc::new(RwLock::new(vec![])) } } pub fn regexp_all (name: impl AsRef) -> Self { let name = RegExp(name.as_ref().into()); Self { name, scope: All, status: Arc::new(RwLock::new(vec![])) } } pub fn info (&self) -> Arc { let status = { let status = self.status.read().unwrap(); let mut ok = 0; for (_, _, state) in status.iter() { if *state == Connected { ok += 1 } } format!("{ok}/{}", status.len()) }; let scope = match self.scope { One => " ", All => "*", }; let name = match &self.name { Exact(name) => format!("= {name}"), RegExp(name) => format!("~ {name}"), }; format!("({}) {} {}", status, scope, name).into() } }