diff --git a/crates/tek/src/cli/cli_arranger.rs b/crates/tek/src/cli/cli_arranger.rs index dde92c12..dc5f589b 100644 --- a/crates/tek/src/cli/cli_arranger.rs +++ b/crates/tek/src/cli/cli_arranger.rs @@ -9,24 +9,32 @@ pub fn main () -> Usually<()> { #[command(version, about, long_about = None)] pub struct ArrangerCli { /// Name of JACK client - #[arg(short, long)] name: Option, + #[arg(short, long)] + name: Option, + /// Whether to include a transport toolbar (default: true) - #[arg(short, long, default_value_t = true)] transport: bool, + #[arg(short, long, default_value_t = true)] + transport: bool, + /// Number of tracks - #[arg(short = 'x', long, default_value_t = 8)] tracks: usize, + #[arg(short = 'x', long, default_value_t = 8)] + tracks: usize, + /// Number of scenes - #[arg(short, long, default_value_t = 8)] scenes: usize, + #[arg(short, long, default_value_t = 8)] + scenes: usize, } impl ArrangerCli { /// Run the arranger TUI from CLI arguments. fn run (&self) -> Usually<()> { - Tui::run(JackClient::new("tek_arranger")?.activate_with(|jack|{ + let mut client_name = String::from("tek_arranger"); + if let Some(name) = self.name.as_ref() { + client_name = name.clone(); + } + Tui::run(JackClient::new(client_name.as_str())?.activate_with(|jack|{ let mut app = ArrangerTui::try_from(jack)?; app.color = ItemPalette::random(); - if let Some(name) = self.name.as_ref() { - *app.name.write().unwrap() = name.clone(); - } let track_color_1 = ItemColor::random(); let track_color_2 = ItemColor::random(); for i in 0..self.tracks { diff --git a/crates/tek/src/core/color.rs b/crates/tek/src/core/color.rs index 48d10fec..81041984 100644 --- a/crates/tek/src/core/color.rs +++ b/crates/tek/src/core/color.rs @@ -61,18 +61,18 @@ impl From for ItemPalette { impl From for ItemPalette { fn from (base: ItemColor) -> Self { let mut light = base.okhsl.clone(); - light.lightness = (light.lightness * 4. / 3.).min(Okhsl::::max_lightness()); + light.lightness = (light.lightness * 1.3).min(Okhsl::::max_lightness()); let mut lighter = light.clone(); - lighter.lightness = (lighter.lightness * 5. / 3.).min(Okhsl::::max_lightness()); + lighter.lightness = (lighter.lightness * 1.3).min(Okhsl::::max_lightness()); let mut lightest = lighter.clone(); - lightest.lightness = (lightest.lightness * 4. / 3.).min(Okhsl::::max_lightness()); + lightest.lightness = (lightest.lightness * 1.3).min(Okhsl::::max_lightness()); let mut dark = base.okhsl.clone(); - dark.lightness = (dark.lightness * 0.75).max(Okhsl::::min_lightness()); - dark.saturation = (dark.saturation * 0.75).max(Okhsl::::min_saturation()); + dark.lightness = (dark.lightness * 0.75).max(Okhsl::::min_lightness()); + dark.saturation = (dark.saturation * 0.75).max(Okhsl::::min_saturation()); let mut darker = dark.clone(); - darker.lightness = (darker.lightness * 0.66).max(Okhsl::::min_lightness()); - darker.saturation = (darker.saturation * 0.66).max(Okhsl::::min_saturation()); + darker.lightness = (darker.lightness * 0.66).max(Okhsl::::min_lightness()); + darker.saturation = (darker.saturation * 0.66).max(Okhsl::::min_saturation()); let mut darkest = darker.clone(); darkest.lightness = (darkest.lightness * 0.50).max(Okhsl::::min_lightness()); darkest.saturation = (darkest.saturation * 0.50).max(Okhsl::::min_saturation()); diff --git a/crates/tek/src/plugin/lv2_gui.rs b/crates/tek/src/plugin/lv2_gui.rs index e69de29b..8b94a94c 100644 --- a/crates/tek/src/plugin/lv2_gui.rs +++ b/crates/tek/src/plugin/lv2_gui.rs @@ -0,0 +1,59 @@ +use crate::*; +use std::thread::{spawn, JoinHandle}; +use ::winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, + window::{Window, WindowId}, + platform::x11::EventLoopBuilderExtX11 +}; + +//pub struct LV2PluginUI { + //write: (), + //controller: (), + //widget: (), + //features: (), + //transfer: (), +//} + +pub fn run_lv2_ui (mut ui: LV2PluginUI) -> Usually> { + Ok(spawn(move||{ + let event_loop = EventLoop::builder().with_x11().with_any_thread(true).build().unwrap(); + event_loop.set_control_flow(ControlFlow::Wait); + event_loop.run_app(&mut ui).unwrap() + })) +} + +/// A LV2 plugin's X11 UI. +pub struct LV2PluginUI { + pub window: Option +} + +impl LV2PluginUI { + pub fn new () -> Usually { + Ok(Self { window: None }) + } +} + +impl ApplicationHandler for LV2PluginUI { + fn resumed (&mut self, event_loop: &ActiveEventLoop) { + self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap()); + } + fn window_event (&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + self.window.as_ref().unwrap().set_visible(false); + event_loop.exit(); + }, + WindowEvent::RedrawRequested => { + self.window.as_ref().unwrap().request_redraw(); + } + _ => (), + } + } +} + +fn lv2_ui_instantiate (kind: &str) { + //let host = Suil +} + diff --git a/crates/tek/src/plugin/lv2_tui.rs b/crates/tek/src/plugin/lv2_tui.rs index e69de29b..4ec242b0 100644 --- a/crates/tek/src/plugin/lv2_tui.rs +++ b/crates/tek/src/plugin/lv2_tui.rs @@ -0,0 +1,47 @@ + +use super::*; +use ::livi::{ + World, + Instance, + Plugin as LiviPlugin, + Features, + FeaturesBuilder, + Port, + event::LV2AtomSequence, +}; +use std::thread::JoinHandle; + +/// A LV2 plugin. +pub struct LV2Plugin { + pub world: World, + pub instance: Instance, + pub plugin: LiviPlugin, + pub features: Arc, + pub port_list: Vec, + pub input_buffer: Vec, + pub ui_thread: Option>, +} + +impl LV2Plugin { + const INPUT_BUFFER: usize = 1024; + pub fn new (uri: &str) -> Usually { + // Get 1st plugin at URI + let world = World::with_load_bundle(&uri); + let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 }; + let features = world.build_features(features); + let mut plugin = None; + if let Some(p) = world.iter_plugins().next() { plugin = Some(p); } + let plugin = plugin.expect("plugin not found"); + let err = &format!("init {uri}"); + let instance = unsafe { plugin.instantiate(features.clone(), 48000.0).expect(&err) }; + let mut port_list = vec![]; + for port in plugin.ports() { + port_list.push(port); + } + let input_buffer = Vec::with_capacity(Self::INPUT_BUFFER); + // Instantiate + Ok(Self { + world, instance, port_list, plugin, features, input_buffer, ui_thread: None + }) + } +} diff --git a/crates/tek/src/plugin/vst2_tui.rs b/crates/tek/src/plugin/vst2_tui.rs index e69de29b..6f50cf96 100644 --- a/crates/tek/src/plugin/vst2_tui.rs +++ b/crates/tek/src/plugin/vst2_tui.rs @@ -0,0 +1,14 @@ +use crate::*; + +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"), + host.clone() + )?; + Ok(PluginKind::VST2 { + instance: loader.instance()? + }) +} + diff --git a/crates/tek/src/plugin/vst3_tui.rs b/crates/tek/src/plugin/vst3_tui.rs index e69de29b..0f3ed08a 100644 --- a/crates/tek/src/plugin/vst3_tui.rs +++ b/crates/tek/src/plugin/vst3_tui.rs @@ -0,0 +1,2 @@ +//! TODO + diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 9c617157..b86e44c9 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -1,48 +1,45 @@ use crate::*; use ClockCommand::{Play, Pause}; -use KeyCode::{Char, Down, Right, Delete}; +use KeyCode::{Char, Delete}; /// Root view for standalone `tek_arranger` pub struct ArrangerTui { - pub jack: Arc>, + jack: Arc>, pub clock: ClockModel, pub phrases: PhraseListModel, pub tracks: Vec, pub scenes: Vec, - pub name: Arc>, pub splits: [u16;2], pub selected: ArrangerSelection, pub mode: ArrangerMode, pub color: ItemPalette, pub size: Measure, - pub cursor: (usize, usize), - pub menu_bar: Option>, - pub status_bar: Option, - pub history: Vec, pub note_buf: Vec, pub midi_buf: Vec>>, pub editor: PhraseEditorModel, pub perf: PerfModel, } -from_jack!(|jack| ArrangerTui Self { - jack: jack.clone(), - clock: ClockModel::from(jack), - phrases: PhraseListModel::default(), - editor: PhraseEditorModel::default(), - selected: ArrangerSelection::Clip(0, 0), - scenes: vec![], - tracks: vec![], - color: TuiTheme::bg().into(), - history: vec![], - mode: ArrangerMode::V(1), - name: Arc::new(RwLock::new(String::new())), - size: Measure::new(), - cursor: (0, 0), - splits: [16, 20], - menu_bar: None, - status_bar: None, - midi_buf: vec![vec![];65536], - note_buf: vec![], - perf: PerfModel::default(), +from_jack!(|jack| ArrangerTui { + let clock = ClockModel::from(jack); + let phrase = Arc::new(RwLock::new(Phrase::new( + "New", true, 4 * clock.timebase.ppq.get() as usize, + None, Some(ItemColor::random().into()) + ))); + Self { + clock, + phrases: PhraseListModel::from(&phrase), + editor: PhraseEditorModel::from(&phrase), + selected: ArrangerSelection::Clip(0, 0), + scenes: vec![], + tracks: vec![], + color: TuiTheme::bg().into(), + mode: ArrangerMode::V(1), + size: Measure::new(), + splits: [16, 20], + midi_buf: vec![vec![];65536], + note_buf: vec![], + perf: PerfModel::default(), + jack: jack.clone(), + } }); render!(|self: ArrangerTui|{ let arranger = ||lay!(|add|{ @@ -67,11 +64,11 @@ render!(|self: ArrangerTui|{ ])), } }); - let with_pool = |x|Split::right(false, self.splits[1], PhraseListView(&self.phrases), x); + let with_pool = |x|Split::left(false, self.splits[1], PhraseListView(&self.phrases), x); let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling())); let transport = TransportView::from((self, None, true)); let with_transport = |x|col!([row!(![&play, &transport]), &x]); - with_transport(col!([Fixed::h(self.splits[0], arranger()), with_pool(&self.editor),])) + with_transport(with_pool(col!([Fixed::h(self.splits[0], arranger()), &self.editor]))) }); audio!(|self: ArrangerTui, client, scope|{ // Start profiling cycle diff --git a/crates/tek/src/tui/arranger_mode_v.rs b/crates/tek/src/tui/arranger_mode_v.rs index 227d982f..0b09fc5e 100644 --- a/crates/tek/src/tui/arranger_mode_v.rs +++ b/crates/tek/src/tui/arranger_mode_v.rs @@ -162,8 +162,7 @@ render!(|self: ArrangerVHeader<'a>|row!( // name and width of track let name = track.name().read().unwrap(); let max_w = w.saturating_sub(1).min(name.len()).max(2); - let name = format!("▎{}", &name[0..max_w]); - let name = Tui::bold(true, Tui::fg(track.color.lightest.rgb, name)); + let name = Tui::bold(true, Tui::fg(track.color.lightest.rgb, format!("▎{}", &name[0..max_w]))); // beats elapsed let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { let length = phrase.read().unwrap().length;