use crate::*; use JackState::*; /// Implement [Jack] constructor and methods impl<'j> Jack<'j> { /// Register new [Client] and wrap it for shared use. pub fn new_run + Audio + Send + Sync + 'static> ( name: &impl AsRef, init: impl FnOnce(Jack<'j>)->Usually ) -> Usually>> { Jack::new(name)?.run(init) } pub fn new (name: &impl AsRef) -> Usually { let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0; Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client))))) } pub fn run + Audio + Send + Sync + 'static> (self, init: impl FnOnce(Self)->Usually) -> Usually>> { let client_state = self.0.clone(); let app: Arc> = Arc::new(RwLock::new(init(self)?)); let mut state = Activating; std::mem::swap(&mut*client_state.write().unwrap(), &mut state); if let Inactive(client) = state { // This is the misc notifications handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [JackEvent], which is // one of the available misc notifications. let notify = JackNotify(Box::new({ let app = app.clone(); move|event|(&mut*app.write().unwrap()).handle(event) }) as BoxedJackEventHandler); // This is the main processing handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [Client] and [ProcessScope] // and passes them down to the `app`'s `process` callback, which in turn // implements audio and MIDI input and output on a realtime basis. let process = ClosureProcessHandler::new(Box::new({ let app = app.clone(); move|c: &_, s: &_|if let Ok(mut app) = app.write() { app.process(c, s) } else { Control::Quit } }) as BoxedAudioHandler); // Launch a client with the two handlers. *client_state.write().unwrap() = Active( client.activate_async(notify, process)? ); } else { unreachable!(); } Ok(app) } /// Run something with the client. pub fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { match &*self.0.read().unwrap() { Inert => panic!("jack client not activated"), Inactive(client) => op(client), Activating => panic!("jack client has not finished activation"), Active(client) => op(client.as_client()), } } } impl<'j> HasJack<'j> for Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } impl<'j> HasJack<'j> for &Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } impl NotificationHandler for JackNotify { 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 } } impl JackPerfModel for PerfModel { fn update_from_jack_scope (&self, t0: Option, scope: &ProcessScope) { if let Some(t0) = t0 { let t1 = self.clock.raw(); self.used.store( self.clock.delta_as_nanos(t0, t1) as f64, Relaxed, ); self.window.store( scope.cycle_times().unwrap().period_usecs as f64, Relaxed, ); } } }