mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-02-21 16:29:04 +01:00
131 lines
4.7 KiB
Rust
131 lines
4.7 KiB
Rust
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 <T: HasJack<'j> + Audio + Send + Sync + 'static> (
|
|
name: &impl AsRef<str>,
|
|
init: impl FnOnce(Jack<'j>)->Usually<T>
|
|
) -> Usually<Arc<RwLock<T>>> {
|
|
Jack::new(name)?.run(init)
|
|
}
|
|
|
|
pub fn new (name: &impl AsRef<str>) -> Usually<Self> {
|
|
let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0;
|
|
Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client)))))
|
|
}
|
|
|
|
pub fn run <T: HasJack<'j> + Audio + Send + Sync + 'static>
|
|
(self, init: impl FnOnce(Self)->Usually<T>) -> Usually<Arc<RwLock<T>>>
|
|
{
|
|
let client_state = self.0.clone();
|
|
let app: Arc<RwLock<T>> = 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 <T> (&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<T: Fn(JackEvent) + Send> NotificationHandler for JackNotify<T> {
|
|
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<u64>, 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,
|
|
);
|
|
}
|
|
}
|
|
}
|