mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: compiles again, after extensive jack rework
Some checks are pending
/ build (push) Waiting to run
Some checks are pending
/ build (push) Waiting to run
This commit is contained in:
parent
cb7e4f7a95
commit
0192d85a19
18 changed files with 526 additions and 525 deletions
|
|
@ -1,12 +1,130 @@
|
|||
use crate::*;
|
||||
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
||||
pub(crate) use std::sync::{Arc, RwLock};
|
||||
/// Wraps [JackState] and through it [jack::Client] when connected.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Jack<'j>(Arc<RwLock<JackState<'j>>>);
|
||||
/// 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 {
|
||||
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({
|
||||
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.
|
||||
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),
|
||||
)?;
|
||||
*client_state.write().unwrap() = Active(client);
|
||||
} 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(ref client) => op(client),
|
||||
Activating => panic!("jack client has not finished activation"),
|
||||
Active(ref 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
|
||||
}
|
||||
}
|
||||
/// 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)]
|
||||
pub enum JackState<'j> {
|
||||
/// Unused
|
||||
#[default] Inert,
|
||||
/// Before activation.
|
||||
Inactive(Client),
|
||||
/// During activation.
|
||||
Activating,
|
||||
/// After activation. Must not be dropped for JACK thread to persist.
|
||||
Active(DynamicAsyncClient<'j>),
|
||||
}
|
||||
/// Things that can provide a [jack::Client] reference.
|
||||
pub trait HasJack<'j>: Send + Sync {
|
||||
/// Return the internal [jack::Client] handle
|
||||
/// that lets you call the JACK API.
|
||||
fn jack (&self) -> &Jack<'j>;
|
||||
fn with_client <T> (&self, op: impl FnOnce(&Client)->T) -> T {
|
||||
self.jack().with_client(op)
|
||||
}
|
||||
fn port_by_name (&self, name: &str) -> Option<Port<Unowned>> {
|
||||
self.with_client(|client|client.port_by_name(name))
|
||||
}
|
||||
fn port_by_id (&self, id: u32) -> Option<Port<Unowned>> {
|
||||
self.with_client(|c|c.port_by_id(id))
|
||||
}
|
||||
fn register_port <PS: PortSpec + Default> (&self, name: impl AsRef<str>) -> Usually<Port<PS>> {
|
||||
self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?))
|
||||
}
|
||||
fn sync_lead (&self, enable: bool, callback: impl Fn(TimebaseInfo)->Position) -> Usually<()> {
|
||||
if enable {
|
||||
self.with_client(|client|match client.register_timebase_callback(false, callback) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e)
|
||||
})?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn sync_follow (&self, _enable: bool) -> Usually<()> {
|
||||
// TODO: sync follow
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Trait for thing that has a JACK process callback.
|
||||
pub trait Audio {
|
||||
/// Handle a JACK event.
|
||||
fn handle (&mut self, _event: JackEvent) {}
|
||||
/// Projecss a JACK chunk.
|
||||
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
/// The JACK process callback function passed to the server.
|
||||
fn callback (
|
||||
state: &Arc<RwLock<Self>>, client: &Client, scope: &ProcessScope
|
||||
) -> Control where Self: Sized {
|
||||
|
|
@ -17,7 +135,6 @@ pub trait Audio {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement [Audio]: provide JACK callbacks.
|
||||
#[macro_export] macro_rules! audio {
|
||||
(|
|
||||
|
|
@ -30,7 +147,6 @@ pub trait Audio {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Event enum for JACK events.
|
||||
#[derive(Debug, Clone, PartialEq)] pub enum JackEvent {
|
||||
ThreadInit,
|
||||
|
|
@ -44,7 +160,6 @@ pub trait Audio {
|
|||
GraphReorder,
|
||||
XRun,
|
||||
}
|
||||
|
||||
/// Generic notification handler that emits [JackEvent]
|
||||
pub struct Notifications<T: Fn(JackEvent) + Send>(pub T);
|
||||
|
||||
|
|
@ -105,121 +220,3 @@ pub type BoxedJackEventHandler<'j> =
|
|||
Box<dyn Fn(JackEvent) + Send + Sync + 'j>;
|
||||
use self::JackState::*;
|
||||
|
||||
impl<'j, T: Has<Jack<'j>>> HasJack<'j> for T {
|
||||
fn jack (&self) -> &Jack<'j> { self.get() }
|
||||
}
|
||||
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 }
|
||||
}
|
||||
|
||||
/// Things that can provide a [jack::Client] reference.
|
||||
pub trait HasJack<'j> {
|
||||
/// Return the internal [jack::Client] handle
|
||||
/// that lets you call the JACK API.
|
||||
fn jack (&self) -> &Jack<'j>;
|
||||
/// Run the JACK thread.
|
||||
fn run <T: Audio + Send + Sync + 'static> (
|
||||
&self, callback: impl FnOnce(&Jack)->Usually<T>
|
||||
) -> Usually<Arc<RwLock<T>>> {
|
||||
let jack = self.jack();
|
||||
let app = Arc::new(RwLock::new(callback(jack)?));
|
||||
let mut state = Activating;
|
||||
std::mem::swap(&mut*jack.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({
|
||||
let app = app.clone();
|
||||
move|event|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.
|
||||
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),
|
||||
)?;
|
||||
*jack.state.write().unwrap() = Active(client);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
Ok(app)
|
||||
}
|
||||
/// Run something with the client.
|
||||
fn with_client <T> (&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<Port<Unowned>> {
|
||||
self.with_client(|client|client.port_by_name(name))
|
||||
}
|
||||
fn port_by_id (&self, id: u32) -> Option<Port<Unowned>> {
|
||||
self.with_client(|c|c.port_by_id(id))
|
||||
}
|
||||
fn register_port <PS: PortSpec + Default> (&self, name: impl AsRef<str>) -> Usually<Port<PS>> {
|
||||
self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?))
|
||||
}
|
||||
fn sync_lead (&self, enable: bool, callback: impl Fn(TimebaseInfo)->Position) -> Usually<()> {
|
||||
if enable {
|
||||
self.with_client(|client|match client.register_timebase_callback(false, callback) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e)
|
||||
})?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn sync_follow (&self, _enable: bool) -> Usually<()> {
|
||||
// TODO: sync follow
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps [JackState] and through it [jack::Client].
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Jack<'j> {
|
||||
pub state: Arc<RwLock<JackState<'j>>>
|
||||
}
|
||||
|
||||
impl<'j> Jack<'j> {
|
||||
pub fn new (name: &str) -> Usually<Self> {
|
||||
Ok(Self {
|
||||
state: JackState::new(Client::new(name, ClientOptions::NO_START_SERVER)?.0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub enum JackState<'j> {
|
||||
/// Unused
|
||||
#[default] Inert,
|
||||
/// Before activation.
|
||||
Inactive(Client),
|
||||
/// During activation.
|
||||
Activating,
|
||||
/// After activation. Must not be dropped for JACK thread to persist.
|
||||
Active(DynamicAsyncClient<'j>),
|
||||
}
|
||||
|
||||
impl<'j> JackState<'j> {
|
||||
fn new (client: Client) -> Arc<RwLock<Self>> {
|
||||
Arc::new(RwLock::new(Self::Inactive(client)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ mod note; pub use self::note::*;
|
|||
pub mod jack; pub use self::jack::*;
|
||||
pub mod midi; pub use self::midi::*;
|
||||
|
||||
pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering::Relaxed}};
|
||||
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, AtomicBool, Ordering::Relaxed}};
|
||||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::ops::{Add, Sub, Mul, Div, Rem};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue