mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
624 lines
23 KiB
Rust
624 lines
23 KiB
Rust
use crate::*;
|
|
pub use ::jack as libjack;
|
|
pub use ::jack::{
|
|
contrib::ClosureProcessHandler, NotificationHandler,
|
|
Client, AsyncClient, ClientOptions, ClientStatus,
|
|
ProcessScope, Control, CycleTimes, Frames,
|
|
Port, PortId, PortSpec, Unowned, MidiIn, MidiOut, AudioIn, AudioOut,
|
|
Transport, TransportState, MidiIter, MidiWriter, RawMidi,
|
|
};
|
|
|
|
/// Implement [TryFrom<&Arc<RwLock<JackConnection>>>]: create app state from wrapped JACK handle.
|
|
#[macro_export] macro_rules! from_jack {
|
|
(|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => {
|
|
impl $(<$($L),*$($T $(: $U)?),*>)? TryFrom<&Arc<RwLock<JackConnection>>> for $Struct $(<$($L),*$($T),*>)? {
|
|
type Error = Box<dyn std::error::Error>;
|
|
fn try_from ($jack: &Arc<RwLock<JackConnection>>) -> Usually<Self> {
|
|
Ok($cb)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// 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<RwLock<Self>>, client: &Client, scope: &ProcessScope
|
|
) -> Control where Self: Sized {
|
|
if let Ok(mut state) = state.write() {
|
|
state.process(client, scope)
|
|
} else {
|
|
Control::Quit
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This is a boxed realtime callback.
|
|
pub type BoxedAudioHandler = Box<dyn FnMut(&Client, &ProcessScope) -> 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<dyn Fn(JackEvent) + Send + Sync>;
|
|
|
|
/// This is the notification handler wrapper for a boxed [JackEvent] callback.
|
|
pub type DynamicNotifications = Notifications<BoxedJackEventHandler>;
|
|
|
|
/// 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<DynamicNotifications, DynamicAudioHandler>;
|
|
|
|
/// 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),
|
|
}
|
|
|
|
from!(|jack: JackConnection|Client = 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<Self> {
|
|
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<RwLock<Self>>, &Client, &ProcessScope) -> Control + Send + 'static,
|
|
) -> Usually<Arc<RwLock<Self>>>
|
|
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<dyn Fn(JackEvent) + Send + Sync>;
|
|
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<RwLock<_>>].
|
|
/// * Pass it to the `init` callback
|
|
/// * This allows user code to connect to JACK
|
|
/// * While user code retains clone of the
|
|
/// [Arc<RwLock<JackConnection>>] that is
|
|
/// passed to `init`, the audio engine is running.
|
|
pub fn activate_with <T: Audio + 'static> (
|
|
self,
|
|
init: impl FnOnce(&Arc<RwLock<JackConnection>>)->Usually<T>
|
|
)
|
|
-> Usually<Arc<RwLock<T>>>
|
|
{
|
|
// 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<Port<Unowned>> {
|
|
self.client().port_by_name(name)
|
|
}
|
|
pub fn register_port <PS: PortSpec> (&self, name: &str, spec: PS) -> Usually<Port<PS>> {
|
|
Ok(self.client().register_port(name, spec)?)
|
|
}
|
|
}
|
|
|
|
/// This is a utility trait for things that may register or connect [Port]s.
|
|
/// It contains shorthand methods to this purpose. It's implemented for
|
|
/// `Arc<RwLock<JackConnection>>` for terse port registration in the
|
|
/// `init` callback of [JackClient::activate_with].
|
|
pub trait RegisterPort {
|
|
fn midi_in (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<MidiIn>>;
|
|
fn midi_out (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<MidiOut>>;
|
|
fn audio_in (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<AudioIn>>;
|
|
fn audio_out (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<AudioOut>>;
|
|
}
|
|
|
|
impl RegisterPort for Arc<RwLock<JackConnection>> {
|
|
fn midi_in (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<MidiIn>> {
|
|
let jack = self.read().unwrap();
|
|
let input = jack.client().register_port(name.as_ref(), MidiIn::default())?;
|
|
for port in connect.iter() {
|
|
let port = port.as_ref();
|
|
if let Some(output) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(output, &input)?;
|
|
} else {
|
|
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
}
|
|
Ok(input)
|
|
}
|
|
fn midi_out (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<MidiOut>> {
|
|
let jack = self.read().unwrap();
|
|
let output = jack.client().register_port(name.as_ref(), MidiOut::default())?;
|
|
for port in connect.iter() {
|
|
let port = port.as_ref();
|
|
if let Some(input) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(&output, input)?;
|
|
} else {
|
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
}
|
|
Ok(output)
|
|
}
|
|
fn audio_in (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<AudioIn>> {
|
|
let jack = self.read().unwrap();
|
|
let input = jack.client().register_port(name.as_ref(), AudioIn::default())?;
|
|
for port in connect.iter() {
|
|
let port = port.as_ref();
|
|
if let Some(output) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(output, &input)?;
|
|
} else {
|
|
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
}
|
|
Ok(input)
|
|
}
|
|
fn audio_out (&self, name: impl AsRef<str>, connect: &[impl AsRef<str>]) -> Usually<Port<AudioOut>> {
|
|
let jack = self.read().unwrap();
|
|
let output = jack.client().register_port(name.as_ref(), AudioOut::default())?;
|
|
for port in connect.iter() {
|
|
let port = port.as_ref();
|
|
if let Some(input) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(&output, input)?;
|
|
} else {
|
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
}
|
|
Ok(output)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// Event enum for JACK events.
|
|
pub enum JackEvent {
|
|
ThreadInit,
|
|
Shutdown(ClientStatus, String),
|
|
Freewheel(bool),
|
|
SampleRate(Frames),
|
|
ClientRegistration(String, bool),
|
|
PortRegistration(PortId, bool),
|
|
PortRename(PortId, String, String),
|
|
PortsConnected(PortId, PortId, bool),
|
|
GraphReorder,
|
|
XRun,
|
|
}
|
|
|
|
/// Generic notification handler that emits [JackEvent]
|
|
pub struct Notifications<T: Fn(JackEvent) + Send>(pub T);
|
|
|
|
impl<T: Fn(JackEvent) + Send> NotificationHandler for Notifications<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
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///// A [AudioComponent] bound to a JACK client and a set of ports.
|
|
//pub struct JackDevice<E: Engine> {
|
|
///// The active JACK client of this device.
|
|
//pub client: DynamicAsyncClient,
|
|
///// The device state, encapsulated for sharing between threads.
|
|
//pub state: Arc<RwLock<Box<dyn AudioComponent<E>>>>,
|
|
///// Unowned copies of the device's JACK ports, for connecting to the device.
|
|
///// The "real" readable/writable `Port`s are owned by the `state`.
|
|
//pub ports: UnownedJackPorts,
|
|
//}
|
|
|
|
//impl<E: Engine> std::fmt::Debug for JackDevice<E> {
|
|
//fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
//f.debug_struct("JackDevice")
|
|
//.field("ports", &self.ports)
|
|
//.finish()
|
|
//}
|
|
//}
|
|
|
|
//impl<E: Engine> Render for JackDevice<E> {
|
|
//type Engine = E;
|
|
//fn min_size(&self, to: E::Size) -> Perhaps<E::Size> {
|
|
//self.state.read().unwrap().layout(to)
|
|
//}
|
|
//fn render(&self, to: &mut E::Output) -> Usually<()> {
|
|
//self.state.read().unwrap().render(to)
|
|
//}
|
|
//}
|
|
|
|
//impl<E: Engine> Handle<E> for JackDevice<E> {
|
|
//fn handle(&mut self, from: &E::Input) -> Perhaps<E::Handled> {
|
|
//self.state.write().unwrap().handle(from)
|
|
//}
|
|
//}
|
|
|
|
//impl<E: Engine> Ports for JackDevice<E> {
|
|
//fn audio_ins(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
//Ok(self.ports.audio_ins.values().collect())
|
|
//}
|
|
//fn audio_outs(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
//Ok(self.ports.audio_outs.values().collect())
|
|
//}
|
|
//fn midi_ins(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
//Ok(self.ports.midi_ins.values().collect())
|
|
//}
|
|
//fn midi_outs(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
//Ok(self.ports.midi_outs.values().collect())
|
|
//}
|
|
//}
|
|
|
|
//impl<E: Engine> JackDevice<E> {
|
|
///// Returns a locked mutex of the state's contents.
|
|
//pub fn state(&self) -> LockResult<RwLockReadGuard<Box<dyn AudioComponent<E>>>> {
|
|
//self.state.read()
|
|
//}
|
|
///// Returns a locked mutex of the state's contents.
|
|
//pub fn state_mut(&self) -> LockResult<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
|
|
//self.state.write()
|
|
//}
|
|
//pub fn connect_midi_in(&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
|
//Ok(self
|
|
//.client
|
|
//.as_client()
|
|
//.connect_ports(port, self.midi_ins()?[index])?)
|
|
//}
|
|
//pub fn connect_midi_out(&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
|
//Ok(self
|
|
//.client
|
|
//.as_client()
|
|
//.connect_ports(self.midi_outs()?[index], port)?)
|
|
//}
|
|
//pub fn connect_audio_in(&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
|
//Ok(self
|
|
//.client
|
|
//.as_client()
|
|
//.connect_ports(port, self.audio_ins()?[index])?)
|
|
//}
|
|
//pub fn connect_audio_out(&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
|
//Ok(self
|
|
//.client
|
|
//.as_client()
|
|
//.connect_ports(self.audio_outs()?[index], port)?)
|
|
//}
|
|
//}
|
|
|
|
///// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut].
|
|
//#[derive(Default, Debug)]
|
|
//pub struct JackPorts {
|
|
//pub audio_ins: BTreeMap<String, Port<AudioIn>>,
|
|
//pub midi_ins: BTreeMap<String, Port<MidiIn>>,
|
|
//pub audio_outs: BTreeMap<String, Port<AudioOut>>,
|
|
//pub midi_outs: BTreeMap<String, Port<MidiOut>>,
|
|
//}
|
|
|
|
///// Collection of JACK ports as [Unowned].
|
|
//#[derive(Default, Debug)]
|
|
//pub struct UnownedJackPorts {
|
|
//pub audio_ins: BTreeMap<String, Port<Unowned>>,
|
|
//pub midi_ins: BTreeMap<String, Port<Unowned>>,
|
|
//pub audio_outs: BTreeMap<String, Port<Unowned>>,
|
|
//pub midi_outs: BTreeMap<String, Port<Unowned>>,
|
|
//}
|
|
|
|
//impl JackPorts {
|
|
//pub fn clone_unowned(&self) -> UnownedJackPorts {
|
|
//let mut unowned = UnownedJackPorts::default();
|
|
//for (name, port) in self.midi_ins.iter() {
|
|
//unowned.midi_ins.insert(name.clone(), port.clone_unowned());
|
|
//}
|
|
//for (name, port) in self.midi_outs.iter() {
|
|
//unowned.midi_outs.insert(name.clone(), port.clone_unowned());
|
|
//}
|
|
//for (name, port) in self.audio_ins.iter() {
|
|
//unowned.audio_ins.insert(name.clone(), port.clone_unowned());
|
|
//}
|
|
//for (name, port) in self.audio_outs.iter() {
|
|
//unowned
|
|
//.audio_outs
|
|
//.insert(name.clone(), port.clone_unowned());
|
|
//}
|
|
//unowned
|
|
//}
|
|
//}
|
|
|
|
///// Implement the `Ports` trait.
|
|
//#[macro_export]
|
|
//macro_rules! ports {
|
|
//($T:ty $({ $(audio: {
|
|
//$(ins: |$ai_arg:ident|$ai_impl:expr,)?
|
|
//$(outs: |$ao_arg:ident|$ao_impl:expr,)?
|
|
//})? $(midi: {
|
|
//$(ins: |$mi_arg:ident|$mi_impl:expr,)?
|
|
//$(outs: |$mo_arg:ident|$mo_impl:expr,)?
|
|
//})?})?) => {
|
|
//impl Ports for $T {$(
|
|
//$(
|
|
//$(fn audio_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
|
|
//let cb = |$ai_arg:&'a Self|$ai_impl;
|
|
//cb(self)
|
|
//})?
|
|
//)?
|
|
//$(
|
|
//$(fn audio_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
|
|
//let cb = (|$ao_arg:&'a Self|$ao_impl);
|
|
//cb(self)
|
|
//})?
|
|
//)?
|
|
//)? $(
|
|
//$(
|
|
//$(fn midi_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
|
|
//let cb = (|$mi_arg:&'a Self|$mi_impl);
|
|
//cb(self)
|
|
//})?
|
|
//)?
|
|
//$(
|
|
//$(fn midi_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
|
|
//let cb = (|$mo_arg:&'a Self|$mo_impl);
|
|
//cb(self)
|
|
//})?
|
|
//)?
|
|
//)?}
|
|
//};
|
|
//}
|
|
|
|
///// `JackDevice` factory. Creates JACK `Client`s, performs port registration
|
|
///// and activation, and encapsulates a `AudioComponent` into a `JackDevice`.
|
|
//pub struct Jack {
|
|
//pub client: Client,
|
|
//pub midi_ins: Vec<String>,
|
|
//pub audio_ins: Vec<String>,
|
|
//pub midi_outs: Vec<String>,
|
|
//pub audio_outs: Vec<String>,
|
|
//}
|
|
|
|
//impl Jack {
|
|
//pub fn new(name: &str) -> Usually<Self> {
|
|
//Ok(Self {
|
|
//midi_ins: vec![],
|
|
//audio_ins: vec![],
|
|
//midi_outs: vec![],
|
|
//audio_outs: vec![],
|
|
//client: Client::new(name, ClientOptions::NO_START_SERVER)?.0,
|
|
//})
|
|
//}
|
|
//pub fn run<'a: 'static, D, E>(
|
|
//self,
|
|
//state: impl FnOnce(JackPorts) -> Box<D>,
|
|
//) -> Usually<JackDevice<E>>
|
|
//where
|
|
//D: AudioComponent<E> + Sized + 'static,
|
|
//E: Engine + 'static,
|
|
//{
|
|
//let owned_ports = JackPorts {
|
|
//audio_ins: register_ports(&self.client, self.audio_ins, AudioIn::default())?,
|
|
//audio_outs: register_ports(&self.client, self.audio_outs, AudioOut::default())?,
|
|
//midi_ins: register_ports(&self.client, self.midi_ins, MidiIn::default())?,
|
|
//midi_outs: register_ports(&self.client, self.midi_outs, MidiOut::default())?,
|
|
//};
|
|
//let midi_outs = owned_ports
|
|
//.midi_outs
|
|
//.values()
|
|
//.map(|p| Ok(p.name()?))
|
|
//.collect::<Usually<Vec<_>>>()?;
|
|
//let midi_ins = owned_ports
|
|
//.midi_ins
|
|
//.values()
|
|
//.map(|p| Ok(p.name()?))
|
|
//.collect::<Usually<Vec<_>>>()?;
|
|
//let audio_outs = owned_ports
|
|
//.audio_outs
|
|
//.values()
|
|
//.map(|p| Ok(p.name()?))
|
|
//.collect::<Usually<Vec<_>>>()?;
|
|
//let audio_ins = owned_ports
|
|
//.audio_ins
|
|
//.values()
|
|
//.map(|p| Ok(p.name()?))
|
|
//.collect::<Usually<Vec<_>>>()?;
|
|
//let state = Arc::new(RwLock::new(state(owned_ports) as Box<dyn AudioComponent<E>>));
|
|
//let client = self.client.activate_async(
|
|
//Notifications(Box::new({
|
|
//let _state = state.clone();
|
|
//move |_event| {
|
|
//// FIXME: this deadlocks
|
|
////state.lock().unwrap().handle(&event).unwrap();
|
|
//}
|
|
//}) as Box<dyn Fn(JackEvent) + Send + Sync>),
|
|
//ClosureProcessHandler::new(Box::new({
|
|
//let state = state.clone();
|
|
//move |c: &Client, s: &ProcessScope| state.write().unwrap().process(c, s)
|
|
//}) as BoxedAudioHandler),
|
|
//)?;
|
|
//Ok(JackDevice {
|
|
//ports: UnownedJackPorts {
|
|
//audio_ins: query_ports(&client.as_client(), audio_ins),
|
|
//audio_outs: query_ports(&client.as_client(), audio_outs),
|
|
//midi_ins: query_ports(&client.as_client(), midi_ins),
|
|
//midi_outs: query_ports(&client.as_client(), midi_outs),
|
|
//},
|
|
//client,
|
|
//state,
|
|
//})
|
|
//}
|
|
//pub fn audio_in(mut self, name: &str) -> Self {
|
|
//self.audio_ins.push(name.to_string());
|
|
//self
|
|
//}
|
|
//pub fn audio_out(mut self, name: &str) -> Self {
|
|
//self.audio_outs.push(name.to_string());
|
|
//self
|
|
//}
|
|
//pub fn midi_in(mut self, name: &str) -> Self {
|
|
//self.midi_ins.push(name.to_string());
|
|
//self
|
|
//}
|
|
//pub fn midi_out(mut self, name: &str) -> Self {
|
|
//self.midi_outs.push(name.to_string());
|
|
//self
|
|
//}
|
|
//}
|
|
|
|
///// A UI component that may be associated with a JACK client by the `Jack` factory.
|
|
//pub trait AudioComponent<E: Engine>: Component<E> + Audio {
|
|
///// Perform type erasure for collecting heterogeneous devices.
|
|
//fn boxed(self) -> Box<dyn AudioComponent<E>>
|
|
//where
|
|
//Self: Sized + 'static,
|
|
//{
|
|
//Box::new(self)
|
|
//}
|
|
//}
|
|
|
|
///// All things that implement the required traits can be treated as `AudioComponent`.
|
|
//impl<E: Engine, W: Component<E> + Audio> AudioComponent<E> for W {}
|
|
|
|
/////////
|
|
|
|
/*
|
|
|
|
/// Trait for things that may expose JACK ports.
|
|
pub trait Ports {
|
|
fn audio_ins(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
Ok(vec![])
|
|
}
|
|
fn audio_outs(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
Ok(vec![])
|
|
}
|
|
fn midi_ins(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
Ok(vec![])
|
|
}
|
|
fn midi_outs(&self) -> Usually<Vec<&Port<Unowned>>> {
|
|
Ok(vec![])
|
|
}
|
|
}
|
|
|
|
fn register_ports<T: PortSpec + Copy>(
|
|
client: &Client,
|
|
names: Vec<String>,
|
|
spec: T,
|
|
) -> Usually<BTreeMap<String, Port<T>>> {
|
|
names
|
|
.into_iter()
|
|
.try_fold(BTreeMap::new(), |mut ports, name| {
|
|
let port = client.register_port(&name, spec)?;
|
|
ports.insert(name, port);
|
|
Ok(ports)
|
|
})
|
|
}
|
|
|
|
fn query_ports(client: &Client, names: Vec<String>) -> BTreeMap<String, Port<Unowned>> {
|
|
names.into_iter().fold(BTreeMap::new(), |mut ports, name| {
|
|
let port = client.port_by_name(&name).unwrap();
|
|
ports.insert(name, port);
|
|
ports
|
|
})
|
|
}
|
|
|
|
*/
|