use crate::*; use ::jack::contrib::*; use self::JackState::*; /// Things that can provide a [jack::Client] reference. pub trait HasJack { /// Return the internal [jack::Client] handle /// that lets you call the JACK API. fn jack (&self) -> &Jack; /// Run something with the client. fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { match &*self.jack().state.read().unwrap() { Inert => panic!("jack client not activated"), Inactive(ref client) => op(client), Activating => panic!("jack client has not finished activation"), Active(ref client) => op(client.as_client()), } } fn port_by_name (&self, name: &str) -> Option> { self.with_client(|client|client.port_by_name(name)) } fn register_port (&self, name: impl AsRef) -> Usually> { self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?)) } fn sync_lead (&self, enable: bool, cb: impl Fn(TimebaseInfo)->Position) -> Usually<()> { if enable { self.with_client(|client|match client.register_timebase_callback(false, cb) { Ok(_) => Ok(()), Err(e) => Err(e) })? } Ok(()) } fn sync_follow (&self, enable: bool) -> Usually<()> { // TODO: sync follow Ok(()) } } impl HasJack for Jack { fn jack (&self) -> &Jack { self } } impl HasJack for &Jack { fn jack (&self) -> &Jack { self } } /// Wraps [JackState] and through it [jack::Client]. #[derive(Clone, Debug, Default)] pub struct Jack { state: Arc> } impl Jack { pub fn new (name: &str) -> Usually { Ok(Self { state: JackState::new(Client::new(name, ClientOptions::NO_START_SERVER)?.0) }) } pub fn run <'j: 'static, T: Audio + 'j> ( &self, cb: impl FnOnce(&Jack)->Usually ) -> Usually>> { let app = Arc::new(RwLock::new(cb(self)?)); let mut state = Activating; std::mem::swap(&mut*self.state.write().unwrap(), &mut state); if let Inactive(client) = state { let client = client.activate_async( // 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. Notifications(Box::new(move|_|{/*TODO*/}) 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. 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<'j>), )?; *self.state.write().unwrap() = Active(client); } else { unreachable!(); } Ok(app) } } /// This is a connection which may be [Inactive], [Activating], or [Active]. /// In the [Active] and [Inactive] states, [JackState::client] returns a /// [jack::Client], which you can use to talk to the JACK API. #[derive(Debug, Default)] enum JackState { /// Unused #[default] Inert, /// Before activation. Inactive(Client), /// During activation. Activating, /// After activation. Must not be dropped for JACK thread to persist. Active(DynamicAsyncClient<'static>), } impl JackState { fn new (client: Client) -> Arc> { Arc::new(RwLock::new(Self::Inactive(client))) } } //has_jack_client!(|self: JackState|match self { //Inert => panic!("jack client not activated"), //Inactive(ref client) => client, //Activating => panic!("jack client has not finished activation"), //Active(ref client) => client.as_client(), //}); /// This is a boxed realtime callback. pub type BoxedAudioHandler<'j> = Box Control + Send + 'j>; /// This is the notification handler wrapper for a boxed realtime callback. pub type DynamicAudioHandler<'j> = ClosureProcessHandler<(), BoxedAudioHandler<'j>>; /// This is a boxed [JackEvent] callback. pub type BoxedJackEventHandler<'j> = Box; /// This is the notification handler wrapper for a boxed [JackEvent] callback. pub type DynamicNotifications<'j> = Notifications>; /// This is a running JACK [AsyncClient] with maximum type erasure. /// It has one [Box] containing a function that handles [JackEvent]s, /// and another [Box] containing a function that handles realtime IO, /// and that's all it knows about them. pub type DynamicAsyncClient<'j> = AsyncClient, DynamicAudioHandler<'j>>; /// Implement [Audio]: provide JACK callbacks. #[macro_export] macro_rules! audio { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? { #[inline] fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { $cb } } } } /// Trait for thing that has 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 } } }