From 22b44f562b7c10f4dde729c375e4fdd555898780 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 26 Jun 2024 01:50:02 +0300 Subject: [PATCH] port connections; DevicePorts -> PortList --- src/device.rs | 12 +++--- src/device/chain.rs | 19 ++++++++- src/device/launcher.rs | 44 ++++++++++++++++++-- src/device/plugin.rs | 86 +++++++++++++++++---------------------- src/device/plugin/lv2.rs | 7 +--- src/device/plugin/vst2.rs | 2 + src/device/sequencer.rs | 22 +++++----- src/main.rs | 2 +- 8 files changed, 117 insertions(+), 77 deletions(-) diff --git a/src/device.rs b/src/device.rs index 1ee7cd1c..e16d5e96 100644 --- a/src/device.rs +++ b/src/device.rs @@ -21,7 +21,7 @@ pub use self::launcher::Launcher; use crossterm::event; use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client}; -pub trait Device: Render + Handle + DevicePorts + Send + Sync {} +pub trait Device: Render + Handle + PortList + Send + Sync {} pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box> { let device = Arc::new(Mutex::new(device)); @@ -92,7 +92,7 @@ pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box Device for T {} +impl Device for T {} pub trait Handle { // Handle an input event. @@ -125,7 +125,7 @@ impl> Blit for T { } } -pub trait DevicePorts { +pub trait PortList { fn audio_ins (&self) -> Usually> { Ok(vec![]) } @@ -196,7 +196,7 @@ pub struct DynamicDevice { pub render: MutexUsually + Send>>, pub handle: ArcUsually + Send>>>, pub process: ArcControl + Send>>>, - client: Option + pub client: Option } impl Handle for DynamicDevice { @@ -211,7 +211,7 @@ impl Render for DynamicDevice { } } -impl DevicePorts for DynamicDevice { +impl PortList for DynamicDevice { fn audio_ins (&self) -> Usually> { self.state().audio_ins() } @@ -244,7 +244,7 @@ impl DynamicDevice { client: None, } } - fn state (&self) -> std::sync::MutexGuard<'_, T> { + pub fn state (&self) -> std::sync::MutexGuard<'_, T> { self.state.lock().unwrap() } fn activate (mut self, client: Client) -> Usually { diff --git a/src/device/chain.rs b/src/device/chain.rs index 0a5d16ba..79db4507 100644 --- a/src/device/chain.rs +++ b/src/device/chain.rs @@ -27,6 +27,23 @@ impl Chain { } } +impl PortList for Chain { + fn midi_ins (&self) -> Usually> { + if let Some(device) = self.items.get(0) { + device.midi_ins() + } else { + Ok(vec![]) + } + } + fn audio_outs (&self) -> Usually> { + if let Some(device) = self.items.get(self.items.len().saturating_sub(1)) { + device.audio_outs() + } else { + Ok(vec![]) + } + } +} + pub fn process (_: &mut Chain, _: &Client, _: &ProcessScope) -> Control { Control::Continue } @@ -238,5 +255,3 @@ pub fn handle (state: &mut Chain, event: &AppEvent) -> Usually { |s: &mut Chain|s.handle_focus(&FocusEvent::Outward)] })) } - -impl DevicePorts for Chain {} diff --git a/src/device/launcher.rs b/src/device/launcher.rs index 81bba439..48717def 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher.rs @@ -9,8 +9,8 @@ pub struct Launcher { overdub: bool, position: usize, cursor: (usize, usize), - tracks: Vec>, - chains: Vec>, + pub tracks: Vec>, + pub chains: Vec>, scenes: Vec, show_help: bool, view: LauncherView, @@ -34,6 +34,44 @@ impl Scene { } } impl Launcher { + pub fn new_with_controller (name: &str, pattern: &str) + -> Result, Box> + { + let launcher = Self::new(name)?; + { + let state = &launcher.state(); + let (client, _status) = Client::new(&format!("{name}-init"), ClientOptions::NO_START_SERVER)?; + let controllers = client.ports(Some(pattern), None, ::jack::PortFlags::IS_OUTPUT); + for (i, sequencer) in state.tracks.iter().enumerate() { + for sequencer_midi_in in sequencer.midi_ins()?.iter() { + for controller in controllers.iter() { + client.connect_ports_by_name( + &controller, + &sequencer_midi_in + )?; + } + } + let chain: &DynamicDevice = &state.chains[i]; + for sequencer_midi_out in sequencer.midi_outs()?.iter() { + for chain_midi_in in chain.midi_ins()?.iter() { + client.connect_ports_by_name( + &sequencer_midi_out, + &chain_midi_in + )?; + } + } + client.connect_ports_by_name( + &chain.audio_outs()?[0], + "Komplete Audio 6 Analog Stereo 1/2:playback_FL" + ); + client.connect_ports_by_name( + &chain.audio_outs()?[1], + "Komplete Audio 6 Analog Stereo 1/2:playback_FR" + ); + } + } + Ok(launcher) + } pub fn new (name: &str,) -> Result, Box> { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; let transport = client.transport(); @@ -127,7 +165,7 @@ impl Launcher { } } } -impl DevicePorts for Launcher {} +impl PortList for Launcher {} pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { let transport = state.transport.query().unwrap(); state.playing = transport.state; diff --git a/src/device/plugin.rs b/src/device/plugin.rs index 6b4e1ae6..9a8ab11e 100644 --- a/src/device/plugin.rs +++ b/src/device/plugin.rs @@ -5,24 +5,22 @@ mod vst2; mod vst3; pub struct Plugin { - name: String, - input: ::jack::Port<::jack::MidiIn>, - outputs: [::jack::Port<::jack::AudioOut>;2], - path: String, - plugin: Option, - offset: usize, - selected: usize, - midi_map_enable: bool, + name: String, + path: String, + plugin: Option, + offset: usize, + selected: usize, + mapping: bool, + midi_in: ::jack::Port<::jack::MidiIn>, + audio_out: [::jack::Port<::jack::AudioOut>;2], } enum PluginKind { LV2 { - world: ::livi::World, - features: Arc<::livi::Features>, - input: ::livi::event::LV2AtomSequence, - portList: Vec<::livi::Port>, - instance: ::livi::Instance, - outputs: [Vec;2], + world: ::livi::World, + features: Arc<::livi::Features>, + portList: Vec<::livi::Port>, + instance: ::livi::Instance, }, VST2 { instance: ::vst::host::PluginInstance @@ -37,29 +35,27 @@ impl Plugin { let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; DynamicDevice::new(render, handle, Self::process, Self { name: name.into(), - input: client.register_port("in", MidiIn::default())?, - outputs: [ + path: HELM.into(), + plugin: Some(self::lv2::plug(HELM)?), + offset: 0, + selected: 0, + mapping: false, + midi_in: client.register_port("in", MidiIn::default())?, + audio_out: [ client.register_port("outL", AudioOut::default())?, client.register_port("outR", AudioOut::default())?, ], - path: HELM.into(), - plugin: Some(self::lv2::plug_in(HELM)?), - offset: 0, - selected: 0, - midi_map_enable: false }).activate(client) } pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { match self.plugin.as_mut() { - Some(PluginKind::LV2 { - features, input, ref mut instance, .. - }) => { + Some(PluginKind::LV2 { features, ref mut instance, .. }) => { let mut input = ::livi::event::LV2AtomSequence::new( &features, scope.n_frames() as usize ); let urid = features.midi_urid(); - for event in self.input.iter(scope) { + for event in self.midi_in.iter(scope) { match event.bytes.len() { 3 => input.push_midi_event::<3>( event.time as i64, @@ -70,18 +66,9 @@ impl Plugin { } } let ports = ::livi::EmptyPortConnections::new() - .with_atom_sequence_inputs( - std::iter::once(&input) - ) - .with_audio_outputs( - self.outputs.iter_mut().map(|o|o.as_mut_slice(scope)), - ); - unsafe { - instance.run( - scope.n_frames() as usize, - ports - ).unwrap() - }; + .with_atom_sequence_inputs(std::iter::once(&input)) + .with_audio_outputs(self.audio_out.iter_mut().map(|o|o.as_mut_slice(scope))); + unsafe { instance.run(scope.n_frames() as usize, ports).unwrap() }; }, _ => {} } @@ -89,7 +76,19 @@ impl Plugin { } } -impl ::vst::host::Host for Plugin {} +impl PortList for Plugin { + fn midi_ins (&self) -> Usually> { + Ok(vec![ + self.midi_in.name()? + ]) + } + fn audio_outs (&self) -> Usually> { + Ok(vec![ + self.audio_out[0].name()?, + self.audio_out[1].name()? + ]) + } +} pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) -> Usually @@ -150,17 +149,8 @@ pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually { }], [Char('m'), NONE, "toggle_midi_map", "toggle midi map mode", |s: &mut Plugin|{ - s.midi_map_enable = !s.midi_map_enable; + s.mapping = !s.mapping; Ok(true) }] })) } - -impl DevicePorts for Plugin { - fn midi_ins (&self) -> Usually> { - Ok(vec!["in".into()]) - } - fn audio_outs (&self) -> Usually> { - Ok(vec!["outL".into(), "outR".into()]) - } -} diff --git a/src/device/plugin/lv2.rs b/src/device/plugin/lv2.rs index 1f229acf..98c56deb 100644 --- a/src/device/plugin/lv2.rs +++ b/src/device/plugin/lv2.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use super::*; -pub fn plug_in (uri: &str) -> Usually { +pub fn plug (uri: &str) -> Usually { let world = ::livi::World::with_load_bundle(&uri); let features = world.build_features(::livi::FeaturesBuilder { min_block_length: 1, @@ -18,11 +18,6 @@ pub fn plug_in (uri: &str) -> Usually { portList.push(port); } Ok(PluginKind::LV2 { - input: ::livi::event::LV2AtomSequence::new(&features, 1024), - outputs: [ - vec![0.0; features.max_block_length()], - vec![0.0; features.max_block_length()], - ], instance: unsafe { plugin.instantiate(features.clone(), 48000.0).expect("boop") }, diff --git a/src/device/plugin/vst2.rs b/src/device/plugin/vst2.rs index b9e71c4c..e51801a6 100644 --- a/src/device/plugin/vst2.rs +++ b/src/device/plugin/vst2.rs @@ -1,6 +1,8 @@ use crate::prelude::*; use super::*; +impl ::vst::host::Host for Plugin {} + fn set_vst_plugin (host: &Arc>, path: &str) -> Usually { let mut loader = ::vst::host::PluginLoader::load( &std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"), diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 7e6b2a24..9d1e226e 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -20,13 +20,13 @@ impl Sequence { } pub struct Sequencer { - pub name: String, + pub name: String, /// JACK transport handle. - transport: ::jack::Transport, + transport: ::jack::Transport, /// JACK MIDI input port that will be created. - input_port: Port, + pub midi_in: Port, /// JACK MIDI output port that will be created. - output_port: Port, + pub midi_out: Port, /// Holds info about tempo timebase: Arc, @@ -79,8 +79,8 @@ impl Sequencer { let state = transport.query_state()?; DynamicDevice::new(render, handle, Self::process, Self { name: name.into(), - input_port: client.register_port("in", MidiIn::default())?, - output_port: client.register_port("out", MidiOut::default())?, + midi_in: client.register_port("in", MidiIn::default())?, + midi_out: client.register_port("out", MidiOut::default())?, timebase: timebase.clone(), steps: 64, @@ -155,7 +155,7 @@ impl Sequencer { } // Read from input, write inputs to sequence and/or output buffer - for event in self.input_port.iter(scope) { + for event in self.midi_in.iter(scope) { let tick = tick as u32; let msg = midly::live::LiveEvent::parse(event.bytes).unwrap(); match msg { @@ -208,7 +208,7 @@ impl Sequencer { // Write to port from output buffer // (containing notes from sequence and/or monitor) - let mut writer = self.output_port.writer(scope); + let mut writer = self.midi_out.writer(scope); for time in 0..scope.n_frames() { if let Some(Some(frame)) = output.get_mut(time as usize) { for event in frame.iter() { @@ -222,12 +222,12 @@ impl Sequencer { } } -impl DevicePorts for Sequencer { +impl PortList for Sequencer { fn midi_ins (&self) -> Usually> { - Ok(vec!["in".into()]) + Ok(vec![self.midi_in.name()?]) } fn midi_outs (&self) -> Usually> { - Ok(vec!["out".into()]) + Ok(vec![self.midi_out.name()?]) } } diff --git a/src/main.rs b/src/main.rs index 729cb73d..4e976381 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,5 +19,5 @@ fn main () -> Result<(), Box> { let _cli = cli::Cli::parse(); let xdg = microxdg::XdgApp::new("dawdle")?; crate::config::create_dirs(&xdg)?; - run(Launcher::new("Launcher#0")?) + run(Launcher::new_with_controller("Launcher#0", ".*nanoKEY.*")?) }