use crate::core::*; pub struct Jack { pub client: Client, pub ports: JackPorts, } pub struct JackDevice { pub client: DynamicAsyncClient, pub state: Arc>>, pub ports: UnownedJackPorts, } pub enum JackClient { Active(DynamicAsyncClient), Inactive(Client), } ports!(JackDevice { audio: { ins: |s|Ok(s.ports.audio_ins.values().collect()), outs: |s|Ok(s.ports.audio_outs.values().collect()), } midi: { ins: |s|Ok(s.ports.midi_ins.values().collect()), outs: |s|Ok(s.ports.midi_outs.values().collect()), } }); impl Jack { pub fn new (name: &str) -> Usually { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; Ok(Self { client, ports: JackPorts::default() }) } pub fn run ( mut self, state: impl FnOnce(JackPorts)->Box ) -> Usually { let mut owned_ports = JackPorts::default(); std::mem::swap(&mut self.ports, &mut owned_ports); let unowned_ports = owned_ports.clone_unowned(&self.client); let state = Arc::new(Mutex::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), ClosureProcessHandler::new(Box::new({ let state = state.clone(); move|c: &Client, s: &ProcessScope|{ state.lock().unwrap().process(c, s) } }) as BoxedProcessHandler) )?; Ok(JackDevice { client, state, ports: unowned_ports }) } pub fn ports_from_lv2 (self, plugin: &::livi::Plugin) -> Usually { let counts = plugin.port_counts(); let mut jack = self; for i in 0..counts.atom_sequence_inputs { jack = jack.register_midi_in(&format!("midi-in-{i}"))? } for i in 0..counts.atom_sequence_outputs { jack = jack.register_midi_out(&format!("midi-out-{i}"))?; } for i in 0..counts.audio_inputs { jack = jack.register_audio_in(&format!("audio-in-{i}"))? } for i in 0..counts.audio_outputs { jack = jack.register_audio_out(&format!("audio-out-{i}"))?; } Ok(jack) } pub fn register_midi_out (mut self, name: &str) -> Usually { let port = self.client.register_port(name, MidiOut::default())?; self.ports.midi_outs.insert(name.to_string(), port); Ok(self) } pub fn register_midi_in (mut self, name: &str) -> Usually { let port = self.client.register_port(name, MidiIn::default())?; self.ports.midi_ins.insert(name.to_string(), port); Ok(self) } pub fn register_audio_out (mut self, name: &str) -> Usually { let port = self.client.register_port(name, AudioOut::default())?; self.ports.audio_outs.insert(name.to_string(), port); Ok(self) } pub fn register_audio_in (mut self, name: &str) -> Usually { let port = self.client.register_port(name, AudioIn::default())?; self.ports.audio_ins.insert(name.to_string(), port); Ok(self) } } pub fn jack_run (name: &str, app: &Arc>) -> Usually where T: Handle + Process + Send + 'static { let options = ClientOptions::NO_START_SERVER; let (client, _status) = Client::new(name, options)?; Ok(client.activate_async( Notifications(Box::new({ let _app = app.clone(); move|_event|{ // FIXME: this deadlocks //app.lock().unwrap().handle(&event).unwrap(); } }) as Box), ClosureProcessHandler::new(Box::new({ let app = app.clone(); move|c: &Client, s: &ProcessScope|{ app.lock().unwrap().process(c, s) //Control::Continue } }) as BoxedProcessHandler) )?) } pub trait Process { fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } } #[macro_export] macro_rules! process { ($T:ty) => { impl Process for $T {} }; ($T:ty |$self:ident, $c:ident, $s:ident|$block:tt) => { impl Process for $T { fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { $block } } }; ($T:ty = $process:path) => { impl Process for $T { fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { $process(self, c, s) } } } } pub type DynamicAsyncClient = AsyncClient; pub type DynamicNotifications = Notifications>; pub type DynamicProcessHandler = ClosureProcessHandler; pub type BoxedProcessHandler = Box Control + Send>; pub use ::jack::{ AsyncClient, AudioIn, AudioOut, Client, ClientOptions, ClientStatus, ClosureProcessHandler, Control, Frames, MidiIn, MidiOut, NotificationHandler, Port, PortFlags, PortId, PortSpec, ProcessHandler, ProcessScope, RawMidi, Transport, TransportState, TransportStatePosition, Unowned }; #[derive(Debug)] 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, } pub struct Notifications(pub T); impl NotificationHandler for Notifications { fn thread_init (&self, _: &Client) { self.0(AppEvent::Jack(JackEvent::ThreadInit)); } fn shutdown (&mut self, status: ClientStatus, reason: &str) { self.0(AppEvent::Jack(JackEvent::Shutdown(status, reason.into()))); } fn freewheel (&mut self, _: &Client, enabled: bool) { self.0(AppEvent::Jack(JackEvent::Freewheel(enabled))); } fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { self.0(AppEvent::Jack(JackEvent::SampleRate(frames))); Control::Quit } fn client_registration (&mut self, _: &Client, name: &str, reg: bool) { self.0(AppEvent::Jack(JackEvent::ClientRegistration(name.into(), reg))); } fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) { self.0(AppEvent::Jack(JackEvent::PortRegistration(id, reg))); } fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { self.0(AppEvent::Jack(JackEvent::PortRename(id, old.into(), new.into()))); Control::Continue } fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) { self.0(AppEvent::Jack(JackEvent::PortsConnected(a, b, are))); } fn graph_reorder (&mut self, _: &Client) -> Control { self.0(AppEvent::Jack(JackEvent::GraphReorder)); Control::Continue } fn xrun (&mut self, _: &Client) -> Control { self.0(AppEvent::Jack(JackEvent::XRun)); Control::Continue } } /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut MIDIChunk) { output[0] = Some(vec![]); if let Some(Some(frame)) = output.get_mut(0) { let mut buf = vec![]; let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() }; let evt = LiveEvent::Midi { channel: 0.into(), message: msg }; evt.write(&mut buf).unwrap(); frame.push(buf); } } /// Write to JACK port from output buffer (containing notes from sequence and/or monitor) pub fn write_output (writer: &mut ::jack::MidiWriter, output: &mut MIDIChunk, frames: usize) { for time in 0..frames { if let Some(Some(frame)) = output.get_mut(time ) { for event in frame.iter() { writer.write(&::jack::RawMidi { time: time as u32, bytes: &event }) .expect(&format!("{event:?}")); } } } }