diff --git a/src/jack.rs b/src/jack.rs index 5b3a6978..23850b1d 100644 --- a/src/jack.rs +++ b/src/jack.rs @@ -1,5 +1,4 @@ use crate::*; - pub use ::jack as libjack; pub use ::jack::{ contrib::ClosureProcessHandler, NotificationHandler, @@ -9,13 +8,6 @@ pub use ::jack::{ Transport, TransportState, MidiIter, MidiWriter, RawMidi, }; -pub mod jack_event; -pub(crate) use self::jack_event::*; - -pub mod ports; -pub(crate) use self::ports::*; -pub use self::ports::RegisterPort; - /// Implement [TryFrom<&Arc>>]: create app state from wrapped JACK handle. #[macro_export] macro_rules! from_jack { (|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => { @@ -148,6 +140,144 @@ impl JackConnection { } } +pub trait RegisterPort { + fn midi_in (&self, name: &str) -> Usually>; + fn midi_out (&self, name: &str) -> Usually>; + fn audio_in (&self, name: &str) -> Usually>; + fn audio_out (&self, name: &str) -> Usually>; + fn connect_midi_from (&self, my_input: &Port, ports: &[String]) -> Usually<()>; + fn connect_midi_to (&self, my_output: &Port, ports: &[String]) -> Usually<()>; + fn connect_audio_from (&self, my_input: &Port, ports: &[String]) -> Usually<()>; + fn connect_audio_to (&self, my_output: &Port, ports: &[String]) -> Usually<()>; +} + +impl RegisterPort for Arc> { + fn midi_in (&self, name: &str) -> Usually> { + Ok(self.read().unwrap().client().register_port(name, MidiIn::default())?) + } + fn midi_out (&self, name: &str) -> Usually> { + Ok(self.read().unwrap().client().register_port(name, MidiOut::default())?) + } + fn audio_out (&self, name: &str) -> Usually> { + Ok(self.read().unwrap().client().register_port(name, AudioOut::default())?) + } + fn audio_in (&self, name: &str) -> Usually> { + Ok(self.read().unwrap().client().register_port(name, AudioIn::default())?) + } + fn connect_midi_from (&self, input: &Port, ports: &[String]) -> Usually<()> { + let jack = self.read().unwrap(); + for port in ports.iter() { + if let Some(port) = jack.port_by_name(port).as_ref() { + jack.client().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: &[String]) -> Usually<()> { + let jack = self.read().unwrap(); + for port in ports.iter() { + if let Some(port) = jack.port_by_name(port).as_ref() { + jack.client().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: &[String]) -> Usually<()> { + let jack = self.read().unwrap(); + for port in ports.iter() { + if let Some(port) = jack.port_by_name(port).as_ref() { + jack.client().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: &[String]) -> Usually<()> { + let jack = self.read().unwrap(); + for port in ports.iter() { + if let Some(port) = jack.port_by_name(port).as_ref() { + jack.client().connect_ports(output, port)?; + } else { + panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names."); + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +/// Event enum for JACK events. +pub enum JackEvent { + ThreadInit, + Shutdown(ClientStatus, String), + Freewheel(bool), + SampleRate(Frames), + ClientRegistration(String, bool), + PortRegistration(PortId, bool), + PortRename(PortId, String, String), + PortsConnected(PortId, PortId, bool), + GraphReorder, + XRun, +} + +/// Notification handler used by the [Jack] factory +/// when constructing [JackDevice]s. +pub type DynamicNotifications = Notifications>; + +/// Generic notification handler that emits [JackEvent] +pub struct Notifications(pub T); + +impl NotificationHandler for Notifications { + fn thread_init(&self, _: &Client) { + self.0(JackEvent::ThreadInit); + } + + unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) { + self.0(JackEvent::Shutdown(status, reason.into())); + } + + fn freewheel(&mut self, _: &Client, enabled: bool) { + self.0(JackEvent::Freewheel(enabled)); + } + + fn sample_rate(&mut self, _: &Client, frames: Frames) -> Control { + self.0(JackEvent::SampleRate(frames)); + Control::Quit + } + + fn client_registration(&mut self, _: &Client, name: &str, reg: bool) { + self.0(JackEvent::ClientRegistration(name.into(), reg)); + } + + fn port_registration(&mut self, _: &Client, id: PortId, reg: bool) { + self.0(JackEvent::PortRegistration(id, reg)); + } + + fn port_rename(&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { + self.0(JackEvent::PortRename(id, old.into(), new.into())); + Control::Continue + } + + fn ports_connected(&mut self, _: &Client, a: PortId, b: PortId, are: bool) { + self.0(JackEvent::PortsConnected(a, b, are)); + } + + fn graph_reorder(&mut self, _: &Client) -> Control { + self.0(JackEvent::GraphReorder); + Control::Continue + } + + fn xrun(&mut self, _: &Client) -> Control { + self.0(JackEvent::XRun); + Control::Continue + } +} + //////////////////////////////////////////////////////////////////////////////////// ///// A [AudioComponent] bound to a JACK client and a set of ports. @@ -424,3 +554,47 @@ impl JackConnection { ///// All things that implement the required traits can be treated as `AudioComponent`. //impl + Audio> AudioComponent for W {} + +///////// + +/* + +/// 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 + }) +} + +*/ diff --git a/src/jack/jack_event.rs b/src/jack/jack_event.rs deleted file mode 100644 index e62945d7..00000000 --- a/src/jack/jack_event.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::*; - -#[derive(Debug, Clone, PartialEq)] -/// Event enum for JACK events. -pub enum JackEvent { - ThreadInit, - Shutdown(ClientStatus, String), - Freewheel(bool), - SampleRate(Frames), - ClientRegistration(String, bool), - PortRegistration(PortId, bool), - PortRename(PortId, String, String), - PortsConnected(PortId, PortId, bool), - GraphReorder, - XRun, -} - -/// Notification handler used by the [Jack] factory -/// when constructing [JackDevice]s. -pub type DynamicNotifications = Notifications>; - -/// Generic notification handler that emits [JackEvent] -pub struct Notifications(pub T); - -impl NotificationHandler for Notifications { - fn thread_init(&self, _: &Client) { - self.0(JackEvent::ThreadInit); - } - - unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) { - self.0(JackEvent::Shutdown(status, reason.into())); - } - - fn freewheel(&mut self, _: &Client, enabled: bool) { - self.0(JackEvent::Freewheel(enabled)); - } - - fn sample_rate(&mut self, _: &Client, frames: Frames) -> Control { - self.0(JackEvent::SampleRate(frames)); - Control::Quit - } - - fn client_registration(&mut self, _: &Client, name: &str, reg: bool) { - self.0(JackEvent::ClientRegistration(name.into(), reg)); - } - - fn port_registration(&mut self, _: &Client, id: PortId, reg: bool) { - self.0(JackEvent::PortRegistration(id, reg)); - } - - fn port_rename(&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { - self.0(JackEvent::PortRename(id, old.into(), new.into())); - Control::Continue - } - - fn ports_connected(&mut self, _: &Client, a: PortId, b: PortId, are: bool) { - self.0(JackEvent::PortsConnected(a, b, are)); - } - - fn graph_reorder(&mut self, _: &Client) -> Control { - self.0(JackEvent::GraphReorder); - Control::Continue - } - - fn xrun(&mut self, _: &Client) -> Control { - self.0(JackEvent::XRun); - Control::Continue - } -} diff --git a/src/jack/ports.rs b/src/jack/ports.rs deleted file mode 100644 index 584207cf..00000000 --- a/src/jack/ports.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::*; - -pub trait RegisterPort { - fn midi_in (&self, name: &str) -> Usually>; - fn midi_out (&self, name: &str) -> Usually>; - fn audio_in (&self, name: &str) -> Usually>; - fn audio_out (&self, name: &str) -> Usually>; - fn connect_midi_from (&self, my_input: &Port, ports: &[String]) -> Usually<()>; - fn connect_midi_to (&self, my_output: &Port, ports: &[String]) -> Usually<()>; - fn connect_audio_from (&self, my_input: &Port, ports: &[String]) -> Usually<()>; - fn connect_audio_to (&self, my_output: &Port, ports: &[String]) -> Usually<()>; -} - -impl RegisterPort for Arc> { - fn midi_in (&self, name: &str) -> Usually> { - Ok(self.read().unwrap().client().register_port(name, MidiIn::default())?) - } - fn midi_out (&self, name: &str) -> Usually> { - Ok(self.read().unwrap().client().register_port(name, MidiOut::default())?) - } - fn audio_out (&self, name: &str) -> Usually> { - Ok(self.read().unwrap().client().register_port(name, AudioOut::default())?) - } - fn audio_in (&self, name: &str) -> Usually> { - Ok(self.read().unwrap().client().register_port(name, AudioIn::default())?) - } - fn connect_midi_from (&self, input: &Port, ports: &[String]) -> Usually<()> { - let jack = self.read().unwrap(); - for port in ports.iter() { - if let Some(port) = jack.port_by_name(port).as_ref() { - jack.client().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: &[String]) -> Usually<()> { - let jack = self.read().unwrap(); - for port in ports.iter() { - if let Some(port) = jack.port_by_name(port).as_ref() { - jack.client().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: &[String]) -> Usually<()> { - let jack = self.read().unwrap(); - for port in ports.iter() { - if let Some(port) = jack.port_by_name(port).as_ref() { - jack.client().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: &[String]) -> Usually<()> { - let jack = self.read().unwrap(); - for port in ports.iter() { - if let Some(port) = jack.port_by_name(port).as_ref() { - jack.client().connect_ports(output, port)?; - } else { - panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names."); - } - } - Ok(()) - } -} - -/// 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 - }) -}