use crate::*; use jack::*; /// Trait for things that wrap a JACK client. pub trait AudioEngine { fn activate ( self, process: impl FnMut(&Arc>, &Client, &ProcessScope) -> Control + Send + 'static ) -> Usually>> where Self: Send + Sync + 'static; fn client (&self) -> &Client; fn transport (&self) -> Transport { self.client().transport() } fn port_by_name (&self, name: &str) -> Option> { self.client().port_by_name(name) } fn register_port (&self, name: &str, spec: PS) -> Usually> { Ok(self.client().register_port(name, spec)?) } fn thread_init (&self, _: &Client) {} unsafe fn shutdown (&mut self, status: ClientStatus, reason: &str) {} fn freewheel (&mut self, _: &Client, enabled: bool) {} fn client_registration (&mut self, _: &Client, name: &str, reg: bool) {} fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) {} fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) {} fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { Control::Continue } fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { Control::Continue } fn graph_reorder (&mut self, _: &Client) -> Control { Control::Continue } fn xrun (&mut self, _: &Client) -> Control { Control::Continue } } /// Wraps [Client] or [DynamicAsyncClient] in place. pub enum JackClient { /// Before activation. Inactive(Client), /// During activation. Activating, /// After activation. Must not be dropped for JACK thread to persist. Active(DynamicAsyncClient), } pub type DynamicAsyncClient = AsyncClient; pub type DynamicAudioHandler = contrib::ClosureProcessHandler<(), BoxedAudioHandler>; pub type BoxedAudioHandler = Box Control + Send>; impl JackClient { pub fn new (name: &str) -> Usually { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; Ok(Self::Inactive(client)) } pub fn activate_with ( self, init: impl FnOnce(&Arc>)->Usually ) -> Usually>> { let client = Arc::new(RwLock::new(self)); let target = Arc::new(RwLock::new(init(&client)?)); let event = Box::new(move|_|{/*TODO*/}) as Box; let events = Notifications(event); let frame = Box::new({ let target = target.clone(); move|c: &_, s: &_|if let Ok(mut target) = target.write() { target.process(c, s) } else { Control::Quit } }); let frames = contrib::ClosureProcessHandler::new(frame as BoxedAudioHandler); let mut buffer = Self::Activating; std::mem::swap(&mut*client.write().unwrap(), &mut buffer); *client.write().unwrap() = Self::Active(Client::from(buffer).activate_async(events, frames)?); Ok(target) } } impl From for Client { fn from (jack: JackClient) -> Client { match jack { JackClient::Inactive(client) => client, JackClient::Activating => panic!("jack client still activating"), JackClient::Active(_) => panic!("jack client already activated"), } } } impl AudioEngine for JackClient { fn client(&self) -> &Client { match self { Self::Inactive(ref client) => client, Self::Activating => panic!("jack client has not finished activation"), Self::Active(ref client) => client.as_client(), } } fn activate( self, mut cb: impl FnMut(&Arc>, &Client, &ProcessScope) -> Control + Send + 'static, ) -> Usually>> where Self: Send + Sync + 'static { let client = Client::from(self); let state = Arc::new(RwLock::new(Self::Activating)); let event = Box::new(move|_|{/*TODO*/}) as Box; let events = Notifications(event); let frame = Box::new({let state = state.clone(); move|c: &_, s: &_|cb(&state, c, s)}); let frames = contrib::ClosureProcessHandler::new(frame as BoxedAudioHandler); *state.write().unwrap() = Self::Active(client.activate_async(events, frames)?); Ok(state) } } /// Trait for things that have a JACK process callback. pub trait Audio: Send + Sync { fn process(&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } fn callback( state: &Arc>, client: &Client, scope: &ProcessScope ) -> Control where Self: Sized { if let Ok(mut state) = state.write() { state.process(client, scope) } else { Control::Quit } } } /// 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![]) } } /// A UI component that may be associated with a JACK client by the `Jack` factory. pub trait AudioComponent: Component + Audio { /// Perform type erasure for collecting heterogeneous devices. fn boxed(self) -> Box> where Self: Sized + 'static, { Box::new(self) } } /// All things that implement the required traits can be treated as `AudioComponent`. impl + Audio> AudioComponent for W {} /// `JackDevice` factory. Creates JACK `Client`s, performs port registration /// and activation, and encapsulates a `AudioComponent` into a `JackDevice`. pub struct Jack { pub client: Client, pub midi_ins: Vec, pub audio_ins: Vec, pub midi_outs: Vec, pub audio_outs: Vec, } impl Jack { pub fn new(name: &str) -> Usually { Ok(Self { midi_ins: vec![], audio_ins: vec![], midi_outs: vec![], audio_outs: vec![], client: Client::new(name, ClientOptions::NO_START_SERVER)?.0, }) } pub fn run<'a: 'static, D, E>( self, state: impl FnOnce(JackPorts) -> Box, ) -> Usually> where D: AudioComponent + Sized + 'static, E: Engine + 'static, { let owned_ports = JackPorts { audio_ins: register_ports(&self.client, self.audio_ins, AudioIn::default())?, audio_outs: register_ports(&self.client, self.audio_outs, AudioOut::default())?, midi_ins: register_ports(&self.client, self.midi_ins, MidiIn::default())?, midi_outs: register_ports(&self.client, self.midi_outs, MidiOut::default())?, }; let midi_outs = owned_ports .midi_outs .values() .map(|p| Ok(p.name()?)) .collect::>>()?; let midi_ins = owned_ports .midi_ins .values() .map(|p| Ok(p.name()?)) .collect::>>()?; let audio_outs = owned_ports .audio_outs .values() .map(|p| Ok(p.name()?)) .collect::>>()?; let audio_ins = owned_ports .audio_ins .values() .map(|p| Ok(p.name()?)) .collect::>>()?; let state = Arc::new(RwLock::new(state(owned_ports) as Box>)); let client = self.client.activate_async( Notifications(Box::new({ let _state = state.clone(); move |_event| { // FIXME: this deadlocks //state.lock().unwrap().handle(&event).unwrap(); } }) as Box), contrib::ClosureProcessHandler::new(Box::new({ let state = state.clone(); move |c: &Client, s: &ProcessScope| state.write().unwrap().process(c, s) }) as BoxedAudioHandler), )?; Ok(JackDevice { ports: UnownedJackPorts { audio_ins: query_ports(&client.as_client(), audio_ins), audio_outs: query_ports(&client.as_client(), audio_outs), midi_ins: query_ports(&client.as_client(), midi_ins), midi_outs: query_ports(&client.as_client(), midi_outs), }, client, state, }) } pub fn audio_in(mut self, name: &str) -> Self { self.audio_ins.push(name.to_string()); self } pub fn audio_out(mut self, name: &str) -> Self { self.audio_outs.push(name.to_string()); self } pub fn midi_in(mut self, name: &str) -> Self { self.midi_ins.push(name.to_string()); self } pub fn midi_out(mut self, name: &str) -> Self { self.midi_outs.push(name.to_string()); self } } 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 }) } /// Notification handler used by the [Jack] factory /// when constructing [JackDevice]s. pub type DynamicNotifications = Notifications>; #[derive(Debug)] /// 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, } /// 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. pub struct JackDevice { /// The active JACK client of this device. pub client: DynamicAsyncClient, /// The device state, encapsulated for sharing between threads. pub state: Arc>>>, /// Unowned copies of the device's JACK ports, for connecting to the device. /// The "real" readable/writable `Port`s are owned by the `state`. pub ports: UnownedJackPorts, } impl std::fmt::Debug for JackDevice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("JackDevice") .field("ports", &self.ports) .finish() } } impl Widget for JackDevice { type Engine = E; fn layout(&self, to: E::Size) -> Perhaps { self.state.read().unwrap().layout(to) } fn render(&self, to: &mut E::Output) -> Usually<()> { self.state.read().unwrap().render(to) } } impl Handle for JackDevice { fn handle(&mut self, from: &E::Input) -> Perhaps { self.state.write().unwrap().handle(from) } } impl Ports for JackDevice { fn audio_ins(&self) -> Usually>> { Ok(self.ports.audio_ins.values().collect()) } fn audio_outs(&self) -> Usually>> { Ok(self.ports.audio_outs.values().collect()) } fn midi_ins(&self) -> Usually>> { Ok(self.ports.midi_ins.values().collect()) } fn midi_outs(&self) -> Usually>> { Ok(self.ports.midi_outs.values().collect()) } } impl JackDevice { /// Returns a locked mutex of the state's contents. pub fn state(&self) -> LockResult>>> { self.state.read() } /// Returns a locked mutex of the state's contents. pub fn state_mut(&self) -> LockResult>>> { self.state.write() } pub fn connect_midi_in(&self, index: usize, port: &Port) -> Usually<()> { Ok(self .client .as_client() .connect_ports(port, self.midi_ins()?[index])?) } pub fn connect_midi_out(&self, index: usize, port: &Port) -> Usually<()> { Ok(self .client .as_client() .connect_ports(self.midi_outs()?[index], port)?) } pub fn connect_audio_in(&self, index: usize, port: &Port) -> Usually<()> { Ok(self .client .as_client() .connect_ports(port, self.audio_ins()?[index])?) } pub fn connect_audio_out(&self, index: usize, port: &Port) -> Usually<()> { Ok(self .client .as_client() .connect_ports(self.audio_outs()?[index], port)?) } } /// 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) })? )? )?} }; }