From ddaf8702714da020ae75023b51bcc1485ecd1d4d Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 4 Jul 2024 01:35:02 +0300 Subject: [PATCH] clone ports as unowned and pass outwards --- src/core.rs | 4 +- src/core/jack.rs | 101 ++++++++++++++++++---------------------- src/core/port.rs | 59 +++++++++++++++++++---- src/model/plugin.rs | 10 ---- src/model/plugin/lv2.rs | 2 +- src/model/sampler.rs | 24 +++++----- src/model/track.rs | 24 +++------- src/view/chain.rs | 9 ++-- src/view/grid.rs | 2 +- 9 files changed, 123 insertions(+), 112 deletions(-) diff --git a/src/core.rs b/src/core.rs index 0060133e..7a64b0b4 100644 --- a/src/core.rs +++ b/src/core.rs @@ -24,14 +24,14 @@ pub trait Component: Render + Handle {} impl Component for T {} /// A UI component that may have presence on the JACK grap. -pub trait Device: Render + Handle + Process + Ports + Send + Sync { +pub trait Device: Render + Handle + Process + Send + Sync { fn boxed (self) -> Box where Self: Sized + 'static { Box::new(self) } } /// All things that implement the required traits can be treated as `Device`. -impl Device for T {} +impl Device for T {} // Reexport macros: pub use crate::{ diff --git a/src/core/jack.rs b/src/core/jack.rs index 582413f8..8459264d 100644 --- a/src/core/jack.rs +++ b/src/core/jack.rs @@ -1,24 +1,22 @@ use crate::core::*; pub struct Jack { - pub client: JackClient, + pub client: Client, pub ports: JackPorts, } +pub struct JackDevice { + pub client: DynamicAsyncClient, + pub state: Arc>>, + pub ports: UnownedJackPorts, +} + pub enum JackClient { Active(DynamicAsyncClient), Inactive(Client), } -#[derive(Default)] -pub struct JackPorts { - pub audio_ins: BTreeMap>, - pub midi_ins: BTreeMap>, - pub audio_outs: BTreeMap>, - pub midi_outs: BTreeMap>, -} - -ports!(Jack { +ports!(JackDevice { audio: { ins: |s|Ok(s.ports.audio_ins.values().collect()), outs: |s|Ok(s.ports.audio_outs.values().collect()), @@ -32,7 +30,33 @@ ports!(Jack { impl Jack { pub fn new (name: &str) -> Usually { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; - Ok(Self { client: JackClient::Inactive(client), ports: JackPorts::default(), }) + Ok(Self { client, ports: JackPorts::default() }) + } + pub fn run ( + mut self, state: impl FnOnce(JackPorts)->Box + ) + -> Usually + { + let mut owned_ports = JackPorts::default(); + std::mem::swap(&mut self.ports, &mut owned_ports); + let unowned_ports = owned_ports.clone_unowned(&self.client); + let state = Arc::new(Mutex::new(state(owned_ports) as Box)); + 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), + ClosureProcessHandler::new(Box::new({ + let state = state.clone(); + move|c: &Client, s: &ProcessScope|{ + state.lock().unwrap().process(c, s) + } + }) as BoxedProcessHandler) + )?; + Ok(JackDevice { client, state, ports: unowned_ports }) } pub fn ports_from_lv2 (self, plugin: &::livi::Plugin) -> Usually { let counts = plugin.port_counts(); @@ -52,61 +76,25 @@ impl Jack { Ok(jack) } pub fn register_midi_out (mut self, name: &str) -> Usually { - self.ports.midi_outs.insert(name.to_string(), match &self.client { - JackClient::Inactive(c) => c.register_port(name, MidiOut::default())?, - JackClient::Active(c) => c.as_client().register_port(name, MidiOut::default())?, - }); + let port = self.client.register_port(name, MidiOut::default())?; + self.ports.midi_outs.insert(name.to_string(), port); Ok(self) } pub fn register_midi_in (mut self, name: &str) -> Usually { - self.ports.midi_ins.insert(name.to_string(), match &self.client { - JackClient::Inactive(c) => c.register_port(name, MidiIn::default())?, - JackClient::Active(c) => c.as_client().register_port(name, MidiIn::default())?, - }); + let port = self.client.register_port(name, MidiIn::default())?; + self.ports.midi_ins.insert(name.to_string(), port); Ok(self) } pub fn register_audio_out (mut self, name: &str) -> Usually { - self.ports.audio_outs.insert(name.to_string(), match &self.client { - JackClient::Inactive(c) => c.register_port(name, AudioOut::default())?, - JackClient::Active(c) => c.as_client().register_port(name, AudioOut::default())?, - }); + let port = self.client.register_port(name, AudioOut::default())?; + self.ports.audio_outs.insert(name.to_string(), port); Ok(self) } pub fn register_audio_in (mut self, name: &str) -> Usually { - self.ports.audio_ins.insert(name.to_string(), match &self.client { - JackClient::Inactive(c) => c.register_port(name, AudioIn::default())?, - JackClient::Active(c) => c.as_client().register_port(name, AudioIn::default())?, - }); + let port = self.client.register_port(name, AudioIn::default())?; + self.ports.audio_ins.insert(name.to_string(), port); Ok(self) } - pub fn run ( - mut self, state: impl FnOnce(JackPorts)->Box - ) -> Usually>>> where - T: Device + Process + Sized + 'static - { - let mut ports = JackPorts::default(); - std::mem::swap(&mut self.ports, &mut ports); - let state = Arc::new(Mutex::new(state(ports) as Box)); - self.client = match self.client { - JackClient::Active(_) => panic!("already running"), - JackClient::Inactive(client) => JackClient::Active(client.activate_async( - Notifications(Box::new({ - let _state = state.clone(); - move|_event|{ - // FIXME: this deadlocks - //state.lock().unwrap().handle(&event).unwrap(); - } - }) as Box), - ClosureProcessHandler::new(Box::new({ - let state = state.clone(); - move|c: &Client, s: &ProcessScope|{ - state.lock().unwrap().process(c, s) - } - }) as BoxedProcessHandler) - )?) - }; - Ok(state) - } } pub fn jack_run (name: &str, app: &Arc>) -> Usually @@ -192,7 +180,8 @@ pub use ::jack::{ RawMidi, Transport, TransportState, - TransportStatePosition + TransportStatePosition, + Unowned }; #[derive(Debug)] diff --git a/src/core/port.rs b/src/core/port.rs index a39c01c6..2c99228d 100644 --- a/src/core/port.rs +++ b/src/core/port.rs @@ -1,17 +1,60 @@ use crate::core::*; +#[derive(Default)] +pub struct JackPorts { + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, + pub audio_outs: BTreeMap>, + pub midi_outs: BTreeMap>, +} + +#[derive(Default)] +pub struct UnownedJackPorts { + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, + pub audio_outs: BTreeMap>, + pub midi_outs: BTreeMap>, +} + +impl JackPorts { + pub fn clone_unowned (&self, client: &Client) -> UnownedJackPorts { + let mut unowned = UnownedJackPorts::default(); + for (name, port) in self.midi_ins.iter() { + unowned.midi_ins.insert(name.clone(), unsafe { + Port::from_raw(::jack::Unowned, client.raw(), port.raw(), Arc::downgrade(&Arc::default())) + }); + } + for (name, port) in self.midi_outs.iter() { + unowned.midi_outs.insert(name.clone(), unsafe { + Port::from_raw(::jack::Unowned, client.raw(), port.raw(), Arc::downgrade(&Arc::default())) + }); + } + for (name, port) in self.audio_ins.iter() { + unowned.audio_ins.insert(name.clone(), unsafe { + Port::from_raw(::jack::Unowned, client.raw(), port.raw(), Arc::downgrade(&Arc::default())) + }); + } + for (name, port) in self.audio_outs.iter() { + unowned.audio_outs.insert(name.clone(), unsafe { + Port::from_raw(::jack::Unowned, client.raw(), port.raw(), Arc::downgrade(&Arc::default())) + }); + } + unowned + } +} + /// Trait for things that may expose JACK ports. pub trait Ports { - fn audio_ins <'a> (&'a self) -> Usually>> { + fn audio_ins <'a> (&'a self) -> Usually>> { Ok(vec![]) } - fn audio_outs <'a> (&'a self) -> Usually>> { + fn audio_outs <'a> (&'a self) -> Usually>> { Ok(vec![]) } - fn midi_ins <'a> (&'a self) -> Usually>> { + fn midi_ins <'a> (&'a self) -> Usually>> { Ok(vec![]) } - fn midi_outs <'a> (&'a self) -> Usually>> { + fn midi_outs <'a> (&'a self) -> Usually>> { Ok(vec![]) } } @@ -26,26 +69,26 @@ pub trait Ports { })?})?) => { impl Ports for $T {$( $( - $(fn audio_ins <'a> (&'a self) -> Usually>> { + $(fn audio_ins <'a> (&'a self) -> Usually>> { let cb = |$ai_arg:&'a Self|$ai_impl; cb(self) })? )? $( - $(fn audio_outs <'a> (&'a self) -> Usually>> { + $(fn audio_outs <'a> (&'a self) -> Usually>> { let cb = (|$ao_arg:&'a Self|$ao_impl); cb(self) })? )? )? $( $( - $(fn midi_ins <'a> (&'a self) -> Usually>> { + $(fn midi_ins <'a> (&'a self) -> Usually>> { let cb = (|$mi_arg:&'a Self|$mi_impl); cb(self) })? )? $( - $(fn midi_outs <'a> (&'a self) -> Usually>> { + $(fn midi_outs <'a> (&'a self) -> Usually>> { let cb = (|$mo_arg:&'a Self|$mo_impl); cb(self) })? diff --git a/src/model/plugin.rs b/src/model/plugin.rs index 4cd62de3..2554919b 100644 --- a/src/model/plugin.rs +++ b/src/model/plugin.rs @@ -17,16 +17,6 @@ pub struct Plugin { render!(Plugin = crate::view::plugin::render); handle!(Plugin = crate::control::plugin::handle); process!(Plugin = Plugin::process); -ports!(Plugin { - audio: { - ins: |p|Ok(p.ports.audio_ins.values().collect()), - outs: |p|Ok(p.ports.audio_outs.values().collect()), - } - midi: { - ins: |p|Ok(p.ports.midi_ins.values().collect()), - outs: |p|Ok(p.ports.midi_outs.values().collect()), - } -}); pub enum PluginKind { LV2(LV2Plugin), diff --git a/src/model/plugin/lv2.rs b/src/model/plugin/lv2.rs index 2914936b..4cd3638f 100644 --- a/src/model/plugin/lv2.rs +++ b/src/model/plugin/lv2.rs @@ -41,7 +41,7 @@ impl LV2Plugin { } impl super::Plugin { - pub fn lv2 (name: &str, path: &str) -> Usually>>> { + pub fn lv2 (name: &str, path: &str) -> Usually { let plugin = LV2Plugin::new(path)?; Jack::new(name)? .ports_from_lv2(&plugin.plugin)? diff --git a/src/model/sampler.rs b/src/model/sampler.rs index c5603477..67361243 100644 --- a/src/model/sampler.rs +++ b/src/model/sampler.rs @@ -74,21 +74,21 @@ handle!(Sampler = crate::control::sampler::handle); //} //}); process!(Sampler = Sampler::process); -ports!(Sampler { - audio: { - ins: |s|Ok(s.ports.audio_ins.values().collect()), - outs: |s|Ok(s.ports.audio_outs.values().collect()), - } - midi: { - ins: |s|Ok(s.ports.midi_ins.values().collect()), - outs: |s|Ok(s.ports.midi_outs.values().collect()), - } -}); +//ports!(Sampler { + //audio: { + //ins: |s|Ok(s.ports.audio_ins.values().collect()), + //outs: |s|Ok(s.ports.audio_outs.values().collect()), + //} + //midi: { + //ins: |s|Ok(s.ports.midi_ins.values().collect()), + //outs: |s|Ok(s.ports.midi_outs.values().collect()), + //} +//}); impl Sampler { pub fn new ( name: &str, samples: Option>>, - ) -> Usually>>> { + ) -> Usually { Jack::new(name)? .register_midi_in("midi")? .register_audio_in("recL")? @@ -104,7 +104,7 @@ impl Sampler { })) } - pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { // Output buffer: this will be copied to the audio outs. let channel_count = self.ports.audio_outs.len(); let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count]; diff --git a/src/model/track.rs b/src/model/track.rs index 7bf7712d..35881b9c 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -18,7 +18,7 @@ pub struct Track { /// Red keys on piano roll. pub notes_on: Vec, /// Device chain - pub devices: Vec>>>, + pub devices: Vec, /// Device selector pub device: usize, } @@ -28,7 +28,7 @@ impl Track { name: &str, jack: &Client, phrases: Option>, - devices: Option>>>>, + devices: Option>, ) -> Usually { Ok(Self { name: name.to_string(), @@ -44,7 +44,7 @@ impl Track { }) } pub fn device (&self, i: usize) -> Option>> { - self.devices.get(i).map(|d|d.lock().unwrap()) + self.devices.get(i).map(|d|d.state.lock().unwrap()) } pub fn active_device (&self) -> Option>> { self.device(self.device) @@ -73,22 +73,10 @@ impl Track { ports!(Track { audio: { - outs: |track|{ - if let Some(device) = track.last_device() { - device.audio_outs() - } else { - Ok(vec![]) - } - }, + outs: |_t|Ok(vec![]), } midi: { - ins: |track|if let Some(device) = track.first_device() { - device.midi_ins() - } else { - Ok(vec![]) - }, - outs: |track|Ok(vec![ - &track.midi_out - ]), + ins: |_t|Ok(vec![]), + outs: |_t|Ok(vec![]), } }); diff --git a/src/view/chain.rs b/src/view/chain.rs index ac85a95e..8358b2a0 100644 --- a/src/view/chain.rs +++ b/src/view/chain.rs @@ -64,7 +64,8 @@ impl<'a> ChainView<'a> { //y2 = y2 + 1; //} let width = width.saturating_sub(x).saturating_sub(x2); - let frame = device.render(buf, Rect { x: x + x2, y, width, height })?; + let frame = device.state.lock().unwrap() + .render(buf, Rect { x: x + x2, y, width, height })?; let style = if i == track.device { Some(if self.focused { Style::default().green().bold().not_dim() @@ -145,13 +146,13 @@ pub fn draw_as_column ( let mut w = 0u16; let mut frames = vec![]; for device in state.devices.iter() { - let device = device.lock().unwrap(); - let style_midi = Style::default().black().bold().on_green(); - let style_audio = Style::default().black().bold().on_red(); let midi_ins = device.midi_ins()?; let midi_outs = device.midi_outs()?; let audio_ins = device.audio_ins()?; let audio_outs = device.audio_outs()?; + let device = device.state.lock().unwrap(); + let style_midi = Style::default().black().bold().on_green(); + let style_audio = Style::default().black().bold().on_red(); y = y + midi_ins.len() as u16; let frame = device.render(buf, Rect { x, y, width, height: height.saturating_sub(y) diff --git a/src/view/grid.rs b/src/view/grid.rs index 69d875b5..da46497f 100644 --- a/src/view/grid.rs +++ b/src/view/grid.rs @@ -44,7 +44,7 @@ impl<'a> SceneGridView<'a> { } pub fn draw_horizontal (&mut self) -> Usually { - let (w, h) = self.size_horizontal(); + //let (w, h) = self.size_horizontal(); //self.area.x = self.area.x + self.area.width.saturating_sub(w) / 2; self.area.height = self.tracks.len() as u16 + 2; //self.area.width = w;