clone ports as unowned and pass outwards

This commit is contained in:
🪞👃🪞 2024-07-04 01:35:02 +03:00
parent 394355331d
commit ddaf870271
9 changed files with 123 additions and 112 deletions

View file

@ -24,14 +24,14 @@ pub trait Component: Render + Handle {}
impl<T: Render + Handle> Component for T {} impl<T: Render + Handle> Component for T {}
/// A UI component that may have presence on the JACK grap. /// 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<dyn Device> where Self: Sized + 'static { fn boxed (self) -> Box<dyn Device> where Self: Sized + 'static {
Box::new(self) Box::new(self)
} }
} }
/// All things that implement the required traits can be treated as `Device`. /// All things that implement the required traits can be treated as `Device`.
impl<T: Render + Handle + Process + Ports + Send + Sync> Device for T {} impl<T: Render + Handle + Process + Send + Sync> Device for T {}
// Reexport macros: // Reexport macros:
pub use crate::{ pub use crate::{

View file

@ -1,24 +1,22 @@
use crate::core::*; use crate::core::*;
pub struct Jack { pub struct Jack {
pub client: JackClient, pub client: Client,
pub ports: JackPorts, pub ports: JackPorts,
} }
pub struct JackDevice {
pub client: DynamicAsyncClient,
pub state: Arc<Mutex<Box<dyn Device>>>,
pub ports: UnownedJackPorts,
}
pub enum JackClient { pub enum JackClient {
Active(DynamicAsyncClient), Active(DynamicAsyncClient),
Inactive(Client), Inactive(Client),
} }
#[derive(Default)] ports!(JackDevice {
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>>,
}
ports!(Jack {
audio: { audio: {
ins: |s|Ok(s.ports.audio_ins.values().collect()), ins: |s|Ok(s.ports.audio_ins.values().collect()),
outs: |s|Ok(s.ports.audio_outs.values().collect()), outs: |s|Ok(s.ports.audio_outs.values().collect()),
@ -32,7 +30,33 @@ ports!(Jack {
impl Jack { impl Jack {
pub fn new (name: &str) -> Usually<Self> { pub fn new (name: &str) -> Usually<Self> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; 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 <T: Device + Process + Sized + 'static> (
mut self, state: impl FnOnce(JackPorts)->Box<T>
)
-> Usually<JackDevice>
{
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<dyn Device>));
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(AppEvent) + Send + Sync>),
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<Self> { pub fn ports_from_lv2 (self, plugin: &::livi::Plugin) -> Usually<Self> {
let counts = plugin.port_counts(); let counts = plugin.port_counts();
@ -52,61 +76,25 @@ impl Jack {
Ok(jack) Ok(jack)
} }
pub fn register_midi_out (mut self, name: &str) -> Usually<Self> { pub fn register_midi_out (mut self, name: &str) -> Usually<Self> {
self.ports.midi_outs.insert(name.to_string(), match &self.client { let port = self.client.register_port(name, MidiOut::default())?;
JackClient::Inactive(c) => c.register_port(name, MidiOut::default())?, self.ports.midi_outs.insert(name.to_string(), port);
JackClient::Active(c) => c.as_client().register_port(name, MidiOut::default())?,
});
Ok(self) Ok(self)
} }
pub fn register_midi_in (mut self, name: &str) -> Usually<Self> { pub fn register_midi_in (mut self, name: &str) -> Usually<Self> {
self.ports.midi_ins.insert(name.to_string(), match &self.client { let port = self.client.register_port(name, MidiIn::default())?;
JackClient::Inactive(c) => c.register_port(name, MidiIn::default())?, self.ports.midi_ins.insert(name.to_string(), port);
JackClient::Active(c) => c.as_client().register_port(name, MidiIn::default())?,
});
Ok(self) Ok(self)
} }
pub fn register_audio_out (mut self, name: &str) -> Usually<Self> { pub fn register_audio_out (mut self, name: &str) -> Usually<Self> {
self.ports.audio_outs.insert(name.to_string(), match &self.client { let port = self.client.register_port(name, AudioOut::default())?;
JackClient::Inactive(c) => c.register_port(name, AudioOut::default())?, self.ports.audio_outs.insert(name.to_string(), port);
JackClient::Active(c) => c.as_client().register_port(name, AudioOut::default())?,
});
Ok(self) Ok(self)
} }
pub fn register_audio_in (mut self, name: &str) -> Usually<Self> { pub fn register_audio_in (mut self, name: &str) -> Usually<Self> {
self.ports.audio_ins.insert(name.to_string(), match &self.client { let port = self.client.register_port(name, AudioIn::default())?;
JackClient::Inactive(c) => c.register_port(name, AudioIn::default())?, self.ports.audio_ins.insert(name.to_string(), port);
JackClient::Active(c) => c.as_client().register_port(name, AudioIn::default())?,
});
Ok(self) Ok(self)
} }
pub fn run <T> (
mut self, state: impl FnOnce(JackPorts)->Box<T>
) -> Usually<Arc<Mutex<Box<dyn Device>>>> 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<dyn Device>));
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<dyn Fn(AppEvent) + Send + Sync>),
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 <T> (name: &str, app: &Arc<Mutex<T>>) -> Usually<DynamicAsyncClient> pub fn jack_run <T> (name: &str, app: &Arc<Mutex<T>>) -> Usually<DynamicAsyncClient>
@ -192,7 +180,8 @@ pub use ::jack::{
RawMidi, RawMidi,
Transport, Transport,
TransportState, TransportState,
TransportStatePosition TransportStatePosition,
Unowned
}; };
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,17 +1,60 @@
use crate::core::*; use crate::core::*;
#[derive(Default)]
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>>,
}
#[derive(Default)]
pub struct UnownedJackPorts {
pub audio_ins: BTreeMap<String, Port<::jack::Unowned>>,
pub midi_ins: BTreeMap<String, Port<::jack::Unowned>>,
pub audio_outs: BTreeMap<String, Port<::jack::Unowned>>,
pub midi_outs: BTreeMap<String, Port<::jack::Unowned>>,
}
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. /// Trait for things that may expose JACK ports.
pub trait Ports { pub trait Ports {
fn audio_ins <'a> (&'a self) -> Usually<Vec<&'a Port<AudioIn>>> { fn audio_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
Ok(vec![]) Ok(vec![])
} }
fn audio_outs <'a> (&'a self) -> Usually<Vec<&'a Port<AudioOut>>> { fn audio_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
Ok(vec![]) Ok(vec![])
} }
fn midi_ins <'a> (&'a self) -> Usually<Vec<&'a Port<MidiIn>>> { fn midi_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
Ok(vec![]) Ok(vec![])
} }
fn midi_outs <'a> (&'a self) -> Usually<Vec<&'a Port<MidiOut>>> { fn midi_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
Ok(vec![]) Ok(vec![])
} }
} }
@ -26,26 +69,26 @@ pub trait Ports {
})?})?) => { })?})?) => {
impl Ports for $T {$( impl Ports for $T {$(
$( $(
$(fn audio_ins <'a> (&'a self) -> Usually<Vec<&'a Port<AudioIn>>> { $(fn audio_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
let cb = |$ai_arg:&'a Self|$ai_impl; let cb = |$ai_arg:&'a Self|$ai_impl;
cb(self) cb(self)
})? })?
)? )?
$( $(
$(fn audio_outs <'a> (&'a self) -> Usually<Vec<&'a Port<AudioOut>>> { $(fn audio_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
let cb = (|$ao_arg:&'a Self|$ao_impl); let cb = (|$ao_arg:&'a Self|$ao_impl);
cb(self) cb(self)
})? })?
)? )?
)? $( )? $(
$( $(
$(fn midi_ins <'a> (&'a self) -> Usually<Vec<&'a Port<MidiIn>>> { $(fn midi_ins <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
let cb = (|$mi_arg:&'a Self|$mi_impl); let cb = (|$mi_arg:&'a Self|$mi_impl);
cb(self) cb(self)
})? })?
)? )?
$( $(
$(fn midi_outs <'a> (&'a self) -> Usually<Vec<&'a Port<MidiOut>>> { $(fn midi_outs <'a> (&'a self) -> Usually<Vec<&'a Port<Unowned>>> {
let cb = (|$mo_arg:&'a Self|$mo_impl); let cb = (|$mo_arg:&'a Self|$mo_impl);
cb(self) cb(self)
})? })?

View file

@ -17,16 +17,6 @@ pub struct Plugin {
render!(Plugin = crate::view::plugin::render); render!(Plugin = crate::view::plugin::render);
handle!(Plugin = crate::control::plugin::handle); handle!(Plugin = crate::control::plugin::handle);
process!(Plugin = Plugin::process); 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 { pub enum PluginKind {
LV2(LV2Plugin), LV2(LV2Plugin),

View file

@ -41,7 +41,7 @@ impl LV2Plugin {
} }
impl super::Plugin { impl super::Plugin {
pub fn lv2 (name: &str, path: &str) -> Usually<Arc<Mutex<Box<dyn Device>>>> { pub fn lv2 (name: &str, path: &str) -> Usually<JackDevice> {
let plugin = LV2Plugin::new(path)?; let plugin = LV2Plugin::new(path)?;
Jack::new(name)? Jack::new(name)?
.ports_from_lv2(&plugin.plugin)? .ports_from_lv2(&plugin.plugin)?

View file

@ -74,21 +74,21 @@ handle!(Sampler = crate::control::sampler::handle);
//} //}
//}); //});
process!(Sampler = Sampler::process); process!(Sampler = Sampler::process);
ports!(Sampler { //ports!(Sampler {
audio: { //audio: {
ins: |s|Ok(s.ports.audio_ins.values().collect()), //ins: |s|Ok(s.ports.audio_ins.values().collect()),
outs: |s|Ok(s.ports.audio_outs.values().collect()), //outs: |s|Ok(s.ports.audio_outs.values().collect()),
} //}
midi: { //midi: {
ins: |s|Ok(s.ports.midi_ins.values().collect()), //ins: |s|Ok(s.ports.midi_ins.values().collect()),
outs: |s|Ok(s.ports.midi_outs.values().collect()), //outs: |s|Ok(s.ports.midi_outs.values().collect()),
} //}
}); //});
impl Sampler { impl Sampler {
pub fn new ( pub fn new (
name: &str, samples: Option<BTreeMap<u7, Arc<Sample>>>, name: &str, samples: Option<BTreeMap<u7, Arc<Sample>>>,
) -> Usually<Arc<Mutex<Box<dyn Device>>>> { ) -> Usually<JackDevice> {
Jack::new(name)? Jack::new(name)?
.register_midi_in("midi")? .register_midi_in("midi")?
.register_audio_in("recL")? .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. // Output buffer: this will be copied to the audio outs.
let channel_count = self.ports.audio_outs.len(); let channel_count = self.ports.audio_outs.len();
let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count]; let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count];

View file

@ -18,7 +18,7 @@ pub struct Track {
/// Red keys on piano roll. /// Red keys on piano roll.
pub notes_on: Vec<bool>, pub notes_on: Vec<bool>,
/// Device chain /// Device chain
pub devices: Vec<Arc<Mutex<Box<dyn Device>>>>, pub devices: Vec<JackDevice>,
/// Device selector /// Device selector
pub device: usize, pub device: usize,
} }
@ -28,7 +28,7 @@ impl Track {
name: &str, name: &str,
jack: &Client, jack: &Client,
phrases: Option<Vec<Phrase>>, phrases: Option<Vec<Phrase>>,
devices: Option<Vec<Arc<Mutex<Box<dyn Device>>>>>, devices: Option<Vec<JackDevice>>,
) -> Usually<Self> { ) -> Usually<Self> {
Ok(Self { Ok(Self {
name: name.to_string(), name: name.to_string(),
@ -44,7 +44,7 @@ impl Track {
}) })
} }
pub fn device (&self, i: usize) -> Option<MutexGuard<Box<dyn Device>>> { pub fn device (&self, i: usize) -> Option<MutexGuard<Box<dyn Device>>> {
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<MutexGuard<Box<dyn Device>>> { pub fn active_device (&self) -> Option<MutexGuard<Box<dyn Device>>> {
self.device(self.device) self.device(self.device)
@ -73,22 +73,10 @@ impl Track {
ports!(Track { ports!(Track {
audio: { audio: {
outs: |track|{ outs: |_t|Ok(vec![]),
if let Some(device) = track.last_device() {
device.audio_outs()
} else {
Ok(vec![])
}
},
} }
midi: { midi: {
ins: |track|if let Some(device) = track.first_device() { ins: |_t|Ok(vec![]),
device.midi_ins() outs: |_t|Ok(vec![]),
} else {
Ok(vec![])
},
outs: |track|Ok(vec![
&track.midi_out
]),
} }
}); });

View file

@ -64,7 +64,8 @@ impl<'a> ChainView<'a> {
//y2 = y2 + 1; //y2 = y2 + 1;
//} //}
let width = width.saturating_sub(x).saturating_sub(x2); 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 { let style = if i == track.device {
Some(if self.focused { Some(if self.focused {
Style::default().green().bold().not_dim() Style::default().green().bold().not_dim()
@ -145,13 +146,13 @@ pub fn draw_as_column (
let mut w = 0u16; let mut w = 0u16;
let mut frames = vec![]; let mut frames = vec![];
for device in state.devices.iter() { 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_ins = device.midi_ins()?;
let midi_outs = device.midi_outs()?; let midi_outs = device.midi_outs()?;
let audio_ins = device.audio_ins()?; let audio_ins = device.audio_ins()?;
let audio_outs = device.audio_outs()?; 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; y = y + midi_ins.len() as u16;
let frame = device.render(buf, Rect { let frame = device.render(buf, Rect {
x, y, width, height: height.saturating_sub(y) x, y, width, height: height.saturating_sub(y)

View file

@ -44,7 +44,7 @@ impl<'a> SceneGridView<'a> {
} }
pub fn draw_horizontal (&mut self) -> Usually<Rect> { pub fn draw_horizontal (&mut self) -> Usually<Rect> {
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.x = self.area.x + self.area.width.saturating_sub(w) / 2;
self.area.height = self.tracks.len() as u16 + 2; self.area.height = self.tracks.len() as u16 + 2;
//self.area.width = w; //self.area.width = w;