diff --git a/Cargo.lock b/Cargo.lock index 996ad770..0e617a4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1094,11 +1094,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jack" -version = "0.10.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce722655a29b13bb98ec7e8ba9dc65d670b9b37c7b1c09775c7f7516811c5a36" +checksum = "78a4ae24f4ee29676aef8330fed1104e72f314cab16643dbeb61bfd99b4a8273" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "jack-sys", "lazy_static", "libc", @@ -1107,14 +1107,15 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d70559ff166d148ccb750ddd77702af760718f3a752c731add168c22c16a9f" +checksum = "6013b7619b95a22b576dfb43296faa4ecbe40abbdb97dfd22ead520775fc86ab" dependencies = [ "bitflags 1.3.2", "lazy_static", "libc", "libloading 0.7.4", + "log", "pkg-config", ] diff --git a/crates/tek/src/app.rs b/crates/tek/src/app.rs index e7adcf5e..8a525d8a 100644 --- a/crates/tek/src/app.rs +++ b/crates/tek/src/app.rs @@ -1,92 +1,92 @@ use crate::*; use tek_core::Direction; -use tek_sequencer::{TransportToolbar, Arranger}; use tek_mixer::Mixer; +use tek_sequencer::{Arranger, TransportToolbar}; /// Root of application state. pub struct App { /// Whether the currently focused section has input priority - pub entered: bool, + pub entered: bool, /// Currently focused section - pub section: AppFocus, + pub section: AppFocus, /// Transport model and view. - pub transport: TransportToolbar, + pub transport: TransportToolbar, /// Arranger (contains sequencers) - pub arranger: Arranger, + pub arranger: Arranger, /// Mixer (contains tracks) - pub mixer: Mixer, + pub mixer: Mixer, /// Main JACK client. - pub jack: Option, + pub jack: Option, /// Map of external MIDI outs in the jack graph /// to internal MIDI ins of this app. - pub midi_in: Option>>, + pub midi_in: Option>>, /// Names of ports to connect to main MIDI IN. - pub midi_ins: Vec, + pub midi_ins: Vec, /// Display mode of chain section pub chain_mode: bool, /// Main audio outputs. pub audio_outs: Vec>>, /// Number of frames requested by process callback - chunk_size: usize, + chunk_size: usize, /// Paths to user directories - _xdg: Option>, + _xdg: Option>, } impl App { - pub fn new () -> Usually { + pub fn new() -> Usually { let xdg = Arc::new(XdgApp::new("tek")?); let first_run = AppPaths::new(&xdg)?.should_create(); - let jack = JackClient::Inactive( - Client::new("tek", ClientOptions::NO_START_SERVER)?.0 - ); - *MODAL.lock().unwrap() = first_run.then(||{ - ExitableComponent::boxed(SetupModal(Some(xdg.clone()), false)) - }); + let jack = JackClient::Inactive(Client::new("tek", ClientOptions::NO_START_SERVER)?.0); + *MODAL.lock().unwrap() = + first_run.then(|| ExitableComponent::boxed(SetupModal(Some(xdg.clone()), false))); Ok(Self { - entered: true, - section: AppFocus::default(), - transport: TransportToolbar::new(Some(jack.transport())), - arranger: Arranger::new(""), - mixer: Mixer::new("")?, - jack: Some(jack), + entered: true, + section: AppFocus::default(), + transport: TransportToolbar::new(Some(jack.transport())), + arranger: Arranger::new(""), + mixer: Mixer::new("")?, + jack: Some(jack), audio_outs: vec![], chain_mode: false, chunk_size: 0, - midi_in: None, - midi_ins: vec![], - _xdg: Some(xdg), + midi_in: None, + midi_ins: vec![], + _xdg: Some(xdg), }) } - pub fn client (&self) -> &Client { + pub fn client(&self) -> &Client { self.jack.as_ref().unwrap().client() } - pub fn audio_out (&self, index: usize) -> Option>> { - self.audio_outs.get(index).map(|x|x.clone()) + pub fn audio_out(&self, index: usize) -> Option>> { + self.audio_outs.get(index).map(|x| x.clone()) } - pub fn with_midi_ins (mut self, names: &[&str]) -> Usually { - self.midi_ins = names.iter().map(|x|x.to_string()).collect(); + pub fn with_midi_ins(mut self, names: &[&str]) -> Usually { + self.midi_ins = names.iter().map(|x| x.to_string()).collect(); Ok(self) } - pub fn with_audio_outs (mut self, names: &[&str]) -> Usually { + pub fn with_audio_outs(mut self, names: &[&str]) -> Usually { let client = self.client(); self.audio_outs = names .iter() - .map(|name|client - .ports(Some(name), None, PortFlags::empty()) - .get(0) - .map(|name|client.port_by_name(name))) + .map(|name| { + client + .ports(Some(name), None, PortFlags::empty()) + .get(0) + .map(|name| client.port_by_name(name)) + }) .flatten() - .filter_map(|x|x) + .filter_map(|x| x) .map(Arc::new) .collect(); Ok(self) } - pub fn activate ( - mut self, init: Option>)->Usually<()>> + pub fn activate( + mut self, + init: Option>) -> Usually<()>>, ) -> Usually>> { let jack = self.jack.take().expect("no jack client"); let app = Arc::new(RwLock::new(self)); - app.write().unwrap().jack = Some(jack.activate(&app.clone(), |state, client, scope|{ + app.write().unwrap().jack = Some(jack.activate(&app.clone(), |state, client, scope| { state.write().unwrap().process(client, scope) })?); if let Some(init) = init { @@ -96,44 +96,56 @@ impl App { } } -render!(App |self, buf, area| { - Split::down() - .add_ref(&self.transport) - .add_ref(&self.arranger) - .add(If(self.arranger.selected.is_clip(), &Split::right() - .add(tek_mixer::TrackView { - direction: Direction::Down, - entered: self.entered, - focused: self.section == AppFocus::Chain, - chain: self.mixer.track() - }) - .add_ref(&self.arranger.sequencer()) - )) - .render(buf, area)?; - if let Some(ref modal) = *MODAL.lock().unwrap() { - modal.render(buf, area)?; +render!( + App | self, + buf, + area | { + Split::down() + .add_ref(&self.transport) + .add_ref(&self.arranger) + .add(If( + self.arranger.selected.is_clip(), + &Split::right() + .add(tek_mixer::TrackView { + direction: Direction::Down, + entered: self.entered, + focused: self.section == AppFocus::Chain, + chain: self.mixer.track(), + }) + .add_ref(&self.arranger.sequencer()), + )) + .render(buf, area)?; + if let Some(ref modal) = *MODAL.lock().unwrap() { + modal.render(buf, area)?; + } + Ok(area) } - Ok(area) -}); +); -process!(App |self, _client, scope| { - let ( - reset, current_frames, chunk_size, current_usecs, next_usecs, period_usecs - ) = self.transport.update(&scope); - self.chunk_size = chunk_size; - for track in self.arranger.tracks.iter_mut() { - track.process( - self.midi_in.as_ref().map(|p|p.iter(&scope)), - &self.transport.timebase, - self.transport.playing, - self.transport.started, - self.transport.quant as usize, - reset, - &scope, - (current_frames as usize, self.chunk_size), - (current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize), - period_usecs as f64 - ); +process!( + App | self, + _client, + scope | { + let (reset, current_frames, chunk_size, current_usecs, next_usecs, period_usecs) = + self.transport.update(&scope); + self.chunk_size = chunk_size; + for track in self.arranger.tracks.iter_mut() { + track.process( + self.midi_in.as_ref().map(|p| p.iter(&scope)), + &self.transport.timebase, + self.transport.playing, + self.transport.started, + self.transport.quant as usize, + reset, + &scope, + (current_frames as usize, self.chunk_size), + ( + current_usecs as usize, + next_usecs.saturating_sub(current_usecs) as usize, + ), + period_usecs as f64, + ); + } + Control::Continue } - Control::Continue -}); +); diff --git a/crates/tek_core/Cargo.toml b/crates/tek_core/Cargo.toml index 64b41621..5770eb02 100644 --- a/crates/tek_core/Cargo.toml +++ b/crates/tek_core/Cargo.toml @@ -10,7 +10,7 @@ better-panic = "0.3.0" clap = { version = "4.5.4", features = [ "derive" ] } clojure-reader = "0.1.0" crossterm = "0.27" -jack = "0.10" +jack = "0.13" midly = "0.5" once_cell = "1.19.0" ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] } diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index 7838cdc0..8fd1081f 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -1,25 +1,25 @@ -use jack::*; use crate::*; +use jack::*; /// Trait for things that have a JACK process callback. pub trait Audio { - fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { + fn process(&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } } /// Trait for things that may expose JACK ports. pub trait Ports { - fn audio_ins (&self) -> Usually>> { + fn audio_ins(&self) -> Usually>> { Ok(vec![]) } - fn audio_outs (&self) -> Usually>> { + fn audio_outs(&self) -> Usually>> { Ok(vec![]) } - fn midi_ins (&self) -> Usually>> { + fn midi_ins(&self) -> Usually>> { Ok(vec![]) } - fn midi_outs (&self) -> Usually>> { + fn midi_outs(&self) -> Usually>> { Ok(vec![]) } } @@ -27,7 +27,10 @@ pub trait Ports { /// A UI component that may be associated with a JACK client by the `Jack` factory. pub trait AudioComponent: Component + Audio { /// Perform type erasure for collecting heterogeneous devices. - fn boxed (self) -> Box> where Self: Sized + 'static { + fn boxed(self) -> Box> + where + Self: Sized + 'static, + { Box::new(self) } } @@ -42,177 +45,185 @@ pub enum JackClient { } impl JackClient { - pub fn client (&self) -> &Client { + pub fn client(&self) -> &Client { match self { - Self::Inactive(ref client) => - client, - Self::Active(ref client) => - client.as_client(), + Self::Inactive(ref client) => client, + Self::Active(ref client) => client.as_client(), } } - pub fn transport (&self) -> Transport { + pub fn transport(&self) -> Transport { self.client().transport() } - pub fn port_by_name (&self, name: &str) -> Option> { + 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> { + pub fn register_port(&self, name: &str, spec: PS) -> Usually> { Ok(self.client().register_port(name, spec)?) } - pub fn activate ( + pub fn activate( self, state: &Arc>, - mut process: impl FnMut(&Arc>, &Client, &ProcessScope)->Control + Send + 'static + mut process: impl FnMut(&Arc>, &Client, &ProcessScope) -> Control + Send + 'static, ) -> Usually { Ok(match self { Self::Active(_) => self, Self::Inactive(client) => Self::Active(client.activate_async( - Notifications(Box::new(move|_|{/*TODO*/}) - as Box), - ClosureProcessHandler::new(Box::new({ + Notifications( + Box::new(move |_| { /*TODO*/ }) as Box + ), + contrib::ClosureProcessHandler::new(Box::new({ let state = state.clone(); - move|c: &Client, s: &ProcessScope|process(&state, c, s) - }) as BoxedAudioHandler) - )?) + move |c: &Client, s: &ProcessScope| process(&state, c, s) + }) as BoxedAudioHandler), + )?), }) } } -pub type DynamicAsyncClient = - AsyncClient; +pub type DynamicAsyncClient = AsyncClient; -type DynamicAudioHandler = - ClosureProcessHandler; +type DynamicAudioHandler = contrib::ClosureProcessHandler<(), BoxedAudioHandler>; -pub type BoxedAudioHandler = - Box Control + Send>; +pub type BoxedAudioHandler = Box Control + Send>; /// Just run thing with JACK. Returns the activated client. -pub fn jack_run (name: &str, app: &Arc>) -> Usually - where T: Handle + Audio + Send + Sync + 'static +pub fn jack_run(name: &str, app: &Arc>) -> Usually +where + T: Handle + Audio + Send + Sync + 'static, { let options = ClientOptions::NO_START_SERVER; let (client, _status) = Client::new(name, options)?; Ok(client.activate_async( Notifications(Box::new({ let _app = app.clone(); - move|_event|{ + move |_event| { // FIXME: this deadlocks //app.lock().unwrap().handle(&event).unwrap(); } }) as Box), - ClosureProcessHandler::new(Box::new({ + contrib::ClosureProcessHandler::new(Box::new({ let app = app.clone(); - move|c: &Client, s: &ProcessScope|{ + move |c: &Client, s: &ProcessScope| { app.write().unwrap().process(c, s) //Control::Continue } - }) as BoxedAudioHandler) + }) as BoxedAudioHandler), )?) } /// `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, - pub audio_ins: Vec, - pub midi_outs: Vec, + pub client: Client, + pub midi_ins: Vec, + pub audio_ins: Vec, + pub midi_outs: Vec, pub audio_outs: Vec, } impl Jack { - pub fn new (name: &str) -> Usually { + pub fn new(name: &str) -> Usually { Ok(Self { - midi_ins: vec![], - audio_ins: vec![], - midi_outs: vec![], + midi_ins: vec![], + audio_ins: vec![], + midi_outs: vec![], audio_outs: vec![], - client: Client::new( - name, - ClientOptions::NO_START_SERVER - )?.0, + client: Client::new(name, ClientOptions::NO_START_SERVER)?.0, }) } - pub fn run <'a: 'static, D, E> ( - self, state: impl FnOnce(JackPorts)->Box + pub fn run<'a: 'static, D, E>( + self, + state: impl FnOnce(JackPorts) -> Box, ) -> Usually> - where D: AudioComponent + Sized + 'static, - E: Engine + 'static, + where + D: AudioComponent + Sized + 'static, + E: Engine + 'static, { let owned_ports = JackPorts { - audio_ins: register_ports(&self.client, self.audio_ins, AudioIn)?, - audio_outs: register_ports(&self.client, self.audio_outs, AudioOut)?, - midi_ins: register_ports(&self.client, self.midi_ins, MidiIn)?, - midi_outs: register_ports(&self.client, self.midi_outs, MidiOut)?, + 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::>>()?; - let midi_ins = owned_ports.midi_ins.values() - .map(|p|Ok(p.name()?)).collect::>>()?; - let audio_outs = owned_ports.audio_outs.values() - .map(|p|Ok(p.name()?)).collect::>>()?; - let audio_ins = owned_ports.audio_ins.values() - .map(|p|Ok(p.name()?)).collect::>>()?; + let midi_outs = owned_ports + .midi_outs + .values() + .map(|p| Ok(p.name()?)) + .collect::>>()?; + let midi_ins = owned_ports + .midi_ins + .values() + .map(|p| Ok(p.name()?)) + .collect::>>()?; + let audio_outs = owned_ports + .audio_outs + .values() + .map(|p| Ok(p.name()?)) + .collect::>>()?; + let audio_ins = owned_ports + .audio_ins + .values() + .map(|p| Ok(p.name()?)) + .collect::>>()?; let state = Arc::new(RwLock::new(state(owned_ports) as Box>)); let client = self.client.activate_async( Notifications(Box::new({ let _state = state.clone(); - move|_event|{ + move |_event| { // FIXME: this deadlocks //state.lock().unwrap().handle(&event).unwrap(); } }) as Box), - ClosureProcessHandler::new(Box::new({ + contrib::ClosureProcessHandler::new(Box::new({ let state = state.clone(); - move|c: &Client, s: &ProcessScope|{ - state.write().unwrap().process(c, s) - } - }) as BoxedAudioHandler) + 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_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), + 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 { + 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 { + 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 { + 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 { + pub fn midi_out(mut self, name: &str) -> Self { self.midi_outs.push(name.to_string()); self } } -fn register_ports ( - client: &Client, names: Vec, spec: T +fn register_ports( + client: &Client, + names: Vec, + spec: T, ) -> Usually>> { - names.into_iter().try_fold(BTreeMap::new(), |mut ports, name|{ - let port = client.register_port(&name, spec)?; - ports.insert(name, port); - Ok(ports) - }) + 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 -) -> BTreeMap> { - names.into_iter().fold(BTreeMap::new(), |mut ports, name|{ +fn query_ports(client: &Client, names: Vec) -> BTreeMap> { + names.into_iter().fold(BTreeMap::new(), |mut ports, name| { let port = client.port_by_name(&name).unwrap(); ports.insert(name, port); ports @@ -221,8 +232,7 @@ fn query_ports ( /// Notification handler used by the [Jack] factory /// when constructing [JackDevice]s. -pub type DynamicNotifications = - Notifications>; +pub type DynamicNotifications = Notifications>; #[derive(Debug)] /// Event enum for JACK events. @@ -243,46 +253,46 @@ pub enum JackEvent { pub struct Notifications(pub T); impl NotificationHandler for Notifications { - fn thread_init (&self, _: &Client) { + fn thread_init(&self, _: &Client) { self.0(JackEvent::ThreadInit); } - fn shutdown (&mut self, status: ClientStatus, reason: &str) { + unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) { self.0(JackEvent::Shutdown(status, reason.into())); } - fn freewheel (&mut self, _: &Client, enabled: bool) { + fn freewheel(&mut self, _: &Client, enabled: bool) { self.0(JackEvent::Freewheel(enabled)); } - fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { + 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) { + 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) { + 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 { + 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) { + 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 { + fn graph_reorder(&mut self, _: &Client) -> Control { self.0(JackEvent::GraphReorder); Control::Continue } - fn xrun (&mut self, _: &Client) -> Control { + fn xrun(&mut self, _: &Client) -> Control { self.0(JackEvent::XRun); Control::Continue } @@ -293,92 +303,106 @@ pub struct JackDevice { /// The active JACK client of this device. pub client: DynamicAsyncClient, /// The device state, encapsulated for sharing between threads. - pub state: Arc>>>, + pub state: Arc>>>, /// 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, + pub ports: UnownedJackPorts, } impl std::fmt::Debug for JackDevice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("JackDevice").field("ports", &self.ports).finish() + f.debug_struct("JackDevice") + .field("ports", &self.ports) + .finish() } } impl Widget for JackDevice { type Engine = E; - fn layout (&self, to: E::Size) -> Perhaps { + fn layout(&self, to: E::Size) -> Perhaps { self.state.read().unwrap().layout(to) } - fn render (&self, to: &mut E::Output) -> Usually<()> { + fn render(&self, to: &mut E::Output) -> Usually<()> { self.state.read().unwrap().render(to) } } impl Handle for JackDevice { - fn handle (&mut self, from: &E::Input) -> Perhaps { + fn handle(&mut self, from: &E::Input) -> Perhaps { self.state.write().unwrap().handle(from) } } impl Ports for JackDevice { - fn audio_ins (&self) -> Usually>> { + fn audio_ins(&self) -> Usually>> { Ok(self.ports.audio_ins.values().collect()) } - fn audio_outs (&self) -> Usually>> { + fn audio_outs(&self) -> Usually>> { Ok(self.ports.audio_outs.values().collect()) } - fn midi_ins (&self) -> Usually>> { + fn midi_ins(&self) -> Usually>> { Ok(self.ports.midi_ins.values().collect()) } - fn midi_outs (&self) -> Usually>> { + fn midi_outs(&self) -> Usually>> { Ok(self.ports.midi_outs.values().collect()) } } impl JackDevice { /// Returns a locked mutex of the state's contents. - pub fn state (&self) -> LockResult>>> { + pub fn state(&self) -> LockResult>>> { self.state.read() } /// Returns a locked mutex of the state's contents. - pub fn state_mut (&self) -> LockResult>>> { + pub fn state_mut(&self) -> LockResult>>> { self.state.write() } - pub fn connect_midi_in (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(port, self.midi_ins()?[index])?) + pub fn connect_midi_in(&self, index: usize, port: &Port) -> Usually<()> { + Ok(self + .client + .as_client() + .connect_ports(port, self.midi_ins()?[index])?) } - pub fn connect_midi_out (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(self.midi_outs()?[index], port)?) + pub fn connect_midi_out(&self, index: usize, port: &Port) -> Usually<()> { + Ok(self + .client + .as_client() + .connect_ports(self.midi_outs()?[index], port)?) } - pub fn connect_audio_in (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(port, self.audio_ins()?[index])?) + pub fn connect_audio_in(&self, index: usize, port: &Port) -> Usually<()> { + Ok(self + .client + .as_client() + .connect_ports(port, self.audio_ins()?[index])?) } - pub fn connect_audio_out (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(self.audio_outs()?[index], port)?) + pub fn connect_audio_out(&self, index: usize, port: &Port) -> 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>, - pub midi_ins: BTreeMap>, + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, pub audio_outs: BTreeMap>, - pub midi_outs: BTreeMap>, + pub midi_outs: BTreeMap>, } /// Collection of JACK ports as [Unowned]. #[derive(Default, Debug)] pub struct UnownedJackPorts { - pub audio_ins: BTreeMap>, - pub midi_ins: BTreeMap>, + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, pub audio_outs: BTreeMap>, - pub midi_outs: BTreeMap>, + pub midi_outs: BTreeMap>, } impl JackPorts { - pub fn clone_unowned (&self) -> UnownedJackPorts { + 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()); @@ -390,14 +414,17 @@ impl JackPorts { 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 + .audio_outs + .insert(name.clone(), port.clone_unowned()); } unowned } } /// Implement the `Ports` trait. -#[macro_export] macro_rules! ports { +#[macro_export] +macro_rules! ports { ($T:ty $({ $(audio: { $(ins: |$ai_arg:ident|$ai_impl:expr,)? $(outs: |$ao_arg:ident|$ao_impl:expr,)?