use crate::*; /// This is a boxed realtime callback. pub type BoxedAudioHandler = Box Control + Send>; /// This is the notification handler wrapper for a boxed realtime callback. pub type DynamicAudioHandler = ClosureProcessHandler<(), BoxedAudioHandler>; /// This is a boxed [JackEvent] callback. pub type BoxedJackEventHandler = Box; /// This is the notification handler wrapper for a boxed [JackEvent] callback. pub type DynamicNotifications = 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 = AsyncClient; /// This is a connection which may be `Inactive`, `Activating`, or `Active`. /// In the `Active` and `Inactive` states, its `client` method returns a /// [Client] which you can use to talk to the JACK API. #[derive(Debug)] pub enum JackConnection { /// Before activation. Inactive(Client), /// During activation. Activating, /// After activation. Must not be dropped for JACK thread to persist. Active(DynamicAsyncClient), } impl From for Client { fn from (jack: JackConnection) -> Self { match jack { JackConnection::Inactive(client) => client, JackConnection::Activating => panic!("jack client still activating"), JackConnection::Active(_) => panic!("jack client already activated"), } } } impl JackConnection { pub fn new (name: &str) -> Usually { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; Ok(Self::Inactive(client)) } /// Return the internal [Client] handle that lets you call the JACK API. pub 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(), } } /// Activate a connection with an application. /// /// Consume a `JackConnection::Inactive`, /// binding a process callback and /// returning a `JackConnection::Active`. /// /// Needs work. Strange ownership situation between the callback /// and the host object. 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 = ClosureProcessHandler::new(frame as BoxedAudioHandler); *state.write().unwrap() = Self::Active(client.activate_async(events, frames)?); Ok(state) } /// Activate a connection with an application. /// /// * Wrap a [JackConnection::Inactive] into [Arc>]. /// * Pass it to the `init` callback /// * This allows user code to connect to JACK /// * While user code retains clone of the /// [Arc>] that is /// passed to `init`, the audio engine is running. pub fn activate_with ( self, init: impl FnOnce(&Arc>)->Usually ) -> Usually>> { // Wrap self for multiple ownership. let connection = Arc::new(RwLock::new(self)); // Run init callback. Return value is target. Target must retain clone of `connection`. let target = Arc::new(RwLock::new(init(&connection)?)); // Swap the `client` from the `JackConnection::Inactive` // for a `JackConnection::Activating`. let mut client = Self::Activating; std::mem::swap(&mut*connection.write().unwrap(), &mut client); // Replace the `JackConnection::Activating` with a // `JackConnection::Active` wrapping the [AsyncClient] // returned by the activation. *connection.write().unwrap() = Self::Active(Client::from(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 `target`'s `process` callback, which in turn // implements audio and MIDI input and output on a realtime basis. ClosureProcessHandler::new(Box::new({ let target = target.clone(); move|c: &_, s: &_|if let Ok(mut target) = target.write() { target.process(c, s) } else { Control::Quit } }) as BoxedAudioHandler), )?); Ok(target) } pub fn port_by_name (&self, name: &str) -> Option> { self.client().port_by_name(name) } pub fn register_port (&self, name: &str, spec: PS) -> Usually> { Ok(self.client().register_port(name, spec)?) } }