diff --git a/Cargo.lock b/Cargo.lock index 404aba1e..86ca1f2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2551,7 +2551,6 @@ name = "tek_test" version = "0.1.0" dependencies = [ "tek_core", - "tek_mixer", "tek_sequencer", ] diff --git a/crates/tek_core/src/engine/collect.rs b/crates/tek_core/src/engine/collect.rs index 1d912773..7da4d05d 100644 --- a/crates/tek_core/src/engine/collect.rs +++ b/crates/tek_core/src/engine/collect.rs @@ -52,7 +52,7 @@ impl<'a, E: Engine> Collect<'a, E> for Collection<'a, E> { } } -pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Widget]); +pub struct Layers<'a, E: Engine, const N: usize>(pub [&'a dyn Widget;N]); // this actually works, except for the type inference //pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator>>( diff --git a/crates/tek_core/src/engine/component.rs b/crates/tek_core/src/engine/component.rs index adb76b43..1198261d 100644 --- a/crates/tek_core/src/engine/component.rs +++ b/crates/tek_core/src/engine/component.rs @@ -1,6 +1,6 @@ use crate::*; -pub trait Widget { +pub trait Widget: Send + Sync { type Engine: Engine; fn layout (&self, to: <::Engine as Engine>::Area) -> Perhaps<<::Engine as Engine>::Area>; @@ -71,7 +71,7 @@ impl> Widget for Option { } } -pub trait Content { +pub trait Content: Send + Sync { type Engine: Engine; fn content (&self) -> impl Widget::Engine>; } diff --git a/crates/tek_core/src/tui/tui_layout.rs b/crates/tek_core/src/tui/tui_layout.rs index 6a33a606..98164271 100644 --- a/crates/tek_core/src/tui/tui_layout.rs +++ b/crates/tek_core/src/tui/tui_layout.rs @@ -53,7 +53,7 @@ impl<'a> Split<'a, Tui> { } } -impl<'a> Widget for Layers<'a, Tui> { +impl<'a, const N: usize> Widget for Layers<'a, Tui, N> { type Engine = Tui; fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { let [x, y, ..] = area; diff --git a/crates/tek_mixer/src/lib.rs b/crates/tek_mixer/src/lib.rs index 8d1f5dd4..411a172d 100644 --- a/crates/tek_mixer/src/lib.rs +++ b/crates/tek_mixer/src/lib.rs @@ -13,19 +13,21 @@ submod! { mixer mixer_cli mixer_handle - track - track_view - track_handle plugin + plugin_handle plugin_lv2 plugin_lv2_gui + plugin_view plugin_vst2 plugin_vst3 sample sample_add sampler sampler_edn - sampler_view sampler_handle + sampler_view + track + track_handle + track_view voice } diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index f1f15a47..5b014c6c 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -1,7 +1,8 @@ use crate::*; /// A plugin device. -pub struct Plugin { +pub struct Plugin { + _engine: PhantomData, pub name: String, pub path: Option, pub plugin: Option, @@ -9,63 +10,13 @@ pub struct Plugin { pub mapping: bool, pub ports: JackPorts, } -impl Handle for Plugin { - fn handle (&mut self, from: &Tui) -> Perhaps { - handle_keymap(self, &from.event(), KEYMAP_PLUGIN).map(|x|Some(x)) - } -} -process!(Plugin = Plugin::process); -impl Render for Plugin { - fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - let area = to.area(); - let [x, y, _, height] = area; - let mut width = 20u16; - match &self.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => { - let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1)); - let end = start + height as usize - 2; - //draw_box(buf, Rect { x, y, width, height }); - for i in start..end { - if let Some(port) = port_list.get(i) { - let value = if let Some(value) = instance.control_input(port.index) { - value - } else { - port.default_value - }; - //let label = &format!("C·· M·· {:25} = {value:.03}", port.name); - let label = &format!("{:25} = {value:.03}", port.name); - width = width.max(label.len() as u16 + 4); - let style = if i == self.selected { - Some(Style::default().green()) - } else { - None - } ; - to.blit(&label, x + 2, y + 1 + i as u16 - start as u16, style)?; - } else { - break - } - } - }, - _ => {} - }; - draw_header(self, to, x, y, width)?; - Ok(Some([x, y, width, height])) - } -} -/// Supported plugin formats. -pub enum PluginKind { - LV2(LV2Plugin), - VST2 { - instance: ::vst::host::PluginInstance - }, - VST3, -} -impl Plugin { +impl Plugin { pub fn new_lv2 (name: &str, path: &str) -> Usually> { let plugin = LV2Plugin::new(path)?; jack_from_lv2(name, &plugin.plugin)? .run(|ports|Box::new(Self { + _engine: Default::default(), name: name.into(), path: Some(String::from(path)), plugin: Some(PluginKind::LV2(plugin)), @@ -77,6 +28,7 @@ impl Plugin { /// Create a plugin host device. pub fn new (name: &str) -> Usually { Ok(Self { + _engine: Default::default(), name: name.into(), path: None, plugin: None, @@ -85,7 +37,10 @@ impl Plugin { ports: JackPorts::default() }) } - pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { +} + +impl Process for Plugin { + fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { match self.plugin.as_mut() { Some(PluginKind::LV2(LV2Plugin { features, @@ -141,79 +96,12 @@ impl Plugin { Control::Continue } } -fn draw_header (state: &Plugin, to: &mut Tui, x: u16, y: u16, w: u16) -> Usually { - let style = Style::default().gray(); - let label1 = format!(" {}", state.name); - to.blit(&label1, x + 1, y, Some(style.white().bold()))?; - if let Some(ref path) = state.path { - let label2 = format!("{}…", &path[..((w as usize - 10).min(path.len()))]); - to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()))?; - } - Ok(Rect { x, y, width: w, height: 1 }) -} -/// Key bindings for plugin device. -pub const KEYMAP_PLUGIN: &'static [KeyBinding] = keymap!(Plugin { - [Up, NONE, "/plugin/cursor_up", "move cursor up", |s: &mut Plugin|{ - s.selected = s.selected.saturating_sub(1); - Ok(true) - }], - [Down, NONE, "/plugin/cursor_down", "move cursor down", |s: &mut Plugin|{ - s.selected = (s.selected + 1).min(match &s.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, - _ => unimplemented!() - }); - Ok(true) - }], - [PageUp, NONE, "/plugin/cursor_page_up", "move cursor up", |s: &mut Plugin|{ - s.selected = s.selected.saturating_sub(8); - Ok(true) - }], - [PageDown, NONE, "/plugin/cursor_page_down", "move cursor down", |s: &mut Plugin|{ - s.selected = (s.selected + 10).min(match &s.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, - _ => unimplemented!() - }); - Ok(true) - }], - [Char(','), NONE, "/plugin/decrement", "decrement value", |s: &mut Plugin|{ - match s.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[s.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value - 0.01); - } - }, - _ => {} - } - Ok(true) - }], - [Char('.'), NONE, "/plugin/decrement", "increment value", |s: &mut Plugin|{ - match s.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[s.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value + 0.01); - } - }, - _ => {} - } - Ok(true) - }], - [Char('g'), NONE, "/plugin/gui_toggle", "toggle plugin UI", |s: &mut Plugin|{ - match s.plugin { - Some(PluginKind::LV2(ref mut plugin)) => { - plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); - }, - Some(_) => unreachable!(), - None => {} - } - Ok(true) - }], -}); - -impl Layout for Plugin { - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } +/// Supported plugin formats. +pub enum PluginKind { + LV2(LV2Plugin), + VST2 { + instance: ::vst::host::PluginInstance + }, + VST3, } diff --git a/crates/tek_mixer/src/plugin_handle.rs b/crates/tek_mixer/src/plugin_handle.rs new file mode 100644 index 00000000..6f5a1e0a --- /dev/null +++ b/crates/tek_mixer/src/plugin_handle.rs @@ -0,0 +1,67 @@ +use crate::*; + +impl Handle for Plugin { + fn handle (&mut self, from: &Tui) -> Perhaps { + handle_keymap(self, &from.event(), KEYMAP_PLUGIN).map(|x|Some(x)) + } +} + +/// Key bindings for plugin device. +pub const KEYMAP_PLUGIN: &'static [KeyBinding] = keymap!(Plugin { + [Up, NONE, "/plugin/cursor_up", "move cursor up", |s: &mut Plugin|{ + s.selected = s.selected.saturating_sub(1); + Ok(true) + }], + [Down, NONE, "/plugin/cursor_down", "move cursor down", |s: &mut Plugin|{ + s.selected = (s.selected + 1).min(match &s.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, + _ => unimplemented!() + }); + Ok(true) + }], + [PageUp, NONE, "/plugin/cursor_page_up", "move cursor up", |s: &mut Plugin|{ + s.selected = s.selected.saturating_sub(8); + Ok(true) + }], + [PageDown, NONE, "/plugin/cursor_page_down", "move cursor down", |s: &mut Plugin|{ + s.selected = (s.selected + 10).min(match &s.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, + _ => unimplemented!() + }); + Ok(true) + }], + [Char(','), NONE, "/plugin/decrement", "decrement value", |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value - 0.01); + } + }, + _ => {} + } + Ok(true) + }], + [Char('.'), NONE, "/plugin/decrement", "increment value", |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value + 0.01); + } + }, + _ => {} + } + Ok(true) + }], + [Char('g'), NONE, "/plugin/gui_toggle", "toggle plugin UI", |s: &mut Plugin|{ + match s.plugin { + Some(PluginKind::LV2(ref mut plugin)) => { + plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); + }, + Some(_) => unreachable!(), + None => {} + } + Ok(true) + }], +}); diff --git a/crates/tek_mixer/src/plugin_view.rs b/crates/tek_mixer/src/plugin_view.rs new file mode 100644 index 00000000..18529cf8 --- /dev/null +++ b/crates/tek_mixer/src/plugin_view.rs @@ -0,0 +1,54 @@ +use crate::*; + +impl Widget for Plugin { + type Engine = Tui; + fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> { + Ok(Some(to)) + } + fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { + let area = to.area(); + let [x, y, _, height] = area; + let mut width = 20u16; + match &self.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => { + let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1)); + let end = start + height as usize - 2; + //draw_box(buf, Rect { x, y, width, height }); + for i in start..end { + if let Some(port) = port_list.get(i) { + let value = if let Some(value) = instance.control_input(port.index) { + value + } else { + port.default_value + }; + //let label = &format!("C·· M·· {:25} = {value:.03}", port.name); + let label = &format!("{:25} = {value:.03}", port.name); + width = width.max(label.len() as u16 + 4); + let style = if i == self.selected { + Some(Style::default().green()) + } else { + None + } ; + to.blit(&label, x + 2, y + 1 + i as u16 - start as u16, style)?; + } else { + break + } + } + }, + _ => {} + }; + draw_header(self, to, x, y, width)?; + Ok(Some([x, y, width, height])) + } +} + +fn draw_header (state: &Plugin, to: &mut Tui, x: u16, y: u16, w: u16) -> Usually { + let style = Style::default().gray(); + let label1 = format!(" {}", state.name); + to.blit(&label1, x + 1, y, Some(style.white().bold()))?; + if let Some(ref path) = state.path { + let label2 = format!("{}…", &path[..((w as usize - 10).min(path.len()))]); + to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()))?; + } + Ok(Rect { x, y, width: w, height: 1 }) +} diff --git a/crates/tek_sequencer/src/arranger_main.rs b/crates/tek_sequencer/src/arranger_main.rs index 5bc06855..b87a5a76 100644 --- a/crates/tek_sequencer/src/arranger_main.rs +++ b/crates/tek_sequencer/src/arranger_main.rs @@ -9,13 +9,13 @@ pub fn main () -> Usually<()> { struct ArrangerStandalone { /// Contains all the sequencers. - arranger: Arranger, + arranger: Arranger, /// Controls the JACK transport. - transport: Option, + transport: Option>, /// This allows the sequencer view to be moved or hidden. - show_sequencer: Option, + show_sequencer: Option, /// - focus: usize, + focus: usize, } #[derive(Debug, Parser)] @@ -38,7 +38,7 @@ pub struct ArrangerCli { scenes: usize, } -impl ArrangerStandalone { +impl ArrangerStandalone { pub fn from_args () -> Usually { let args = ArrangerCli::parse(); let mut arranger = Arranger::new(""); @@ -73,10 +73,15 @@ impl ArrangerStandalone { } } -impl Render for ArrangerStandalone { +impl Widget for ArrangerStandalone { + type Engine = Tui; + fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> { + Ok(Some(to)) + } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); - let sequencer = self.arranger.sequencer(); + let sequencer = self.arranger.sequencer() + .map(|t|t as &dyn Widget); let result = Split::down() .add_ref(&self.transport) .add_ref(&self.arranger) diff --git a/crates/tek_sequencer/src/arranger_view_h.rs b/crates/tek_sequencer/src/arranger_view_h.rs index fdf47da2..13ba6114 100644 --- a/crates/tek_sequencer/src/arranger_view_h.rs +++ b/crates/tek_sequencer/src/arranger_view_h.rs @@ -4,7 +4,7 @@ pub fn draw (state: &Arranger, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)]; let tracks = state.tracks.as_slice(); - Layers(&[ + Layers([ &state.focused.then_some(FillBg(COLOR_BG0)), &Split::right() .add(TrackNameColumn(tracks, state.selected)) diff --git a/crates/tek_sequencer/src/arranger_view_v.rs b/crates/tek_sequencer/src/arranger_view_v.rs index 7921f0d4..9a71a777 100644 --- a/crates/tek_sequencer/src/arranger_view_v.rs +++ b/crates/tek_sequencer/src/arranger_view_v.rs @@ -38,15 +38,15 @@ pub fn draw <'a, 'b> ( let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16; let tracks = state.tracks.as_ref(); let scenes = state.scenes.as_ref(); - Layered::new() + Layers([ //.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered))) - .add_ref(&ColumnSeparators(offset, cols)) - .add_ref(&RowSeparators(rows)) - .add_ref(&CursorFocus(state.selected, offset, cols, rows)) - .add_ref(&Split::down() + &ColumnSeparators(offset, cols), + &RowSeparators(rows), + &CursorFocus(state.selected, offset, cols, rows), + &Split::down() .add_ref(&TracksHeader(offset, cols, tracks)) - .add_ref(&SceneRows(offset, cols, rows, tracks, scenes))) - .render(to) + .add_ref(&SceneRows(offset, cols, rows, tracks, scenes)) + ]).render(to.with_rect(area)) } struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]); diff --git a/crates/tek_sequencer/src/sequencer_view_h.rs b/crates/tek_sequencer/src/sequencer_view_h.rs index d346eb45..cfef4ad3 100644 --- a/crates/tek_sequencer/src/sequencer_view_h.rs +++ b/crates/tek_sequencer/src/sequencer_view_h.rs @@ -15,13 +15,13 @@ impl Sequencer { let area = [area.x() + 10, area.y(), area.w().saturating_sub(10), area.h().min(66)]; Lozenge(Style::default().fg(Nord::BG2)).draw(to.with_rect(area))?; let area = [area.x() + 1, area.y(), area.w().saturating_sub(1), area.h()]; - Layered::new() - .add_ref(&SequenceKeys(&self)) - .add_ref(&self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone()))) - .add_ref(&SequenceNotes(&self)) - .add_ref(&SequenceCursor(&self)) - .add_ref(&SequenceZoom(&self)) - .render(to.with_rect(area))?; + Layers([ + &SequenceKeys(&self), + &self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone())), + &SequenceNotes(&self), + &SequenceCursor(&self), + &SequenceZoom(&self), + ]).render(to.with_rect(area))?; Ok(()) } diff --git a/crates/tek_sequencer/src/transport.rs b/crates/tek_sequencer/src/transport.rs index 2bfa5da6..fcb9d1b4 100644 --- a/crates/tek_sequencer/src/transport.rs +++ b/crates/tek_sequencer/src/transport.rs @@ -23,7 +23,7 @@ pub struct TransportToolbar { pub clock: TransportClock, } impl TransportToolbar { - pub fn standalone () -> Usually>> { + pub fn standalone () -> Usually>> where Self: 'static { let mut transport = Self::new(None); transport.focused = true; let jack = JackClient::Inactive( diff --git a/crates/tek_sequencer/src/transport_view.rs b/crates/tek_sequencer/src/transport_view.rs index 03bee34c..90343611 100644 --- a/crates/tek_sequencer/src/transport_view.rs +++ b/crates/tek_sequencer/src/transport_view.rs @@ -22,7 +22,7 @@ impl Widget for TransportPlayPauseButton { } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let Self { value, focused, .. } = &self; - Layers(&[ + Layers([ &focused.then_some(CORNERS), &Inset::W(1, Styled(match value { Some(TransportState::Stopped) => Some(GRAY_DIM.bold()), diff --git a/crates/tek_test/Cargo.toml b/crates/tek_test/Cargo.toml index 47ccc134..ba94a840 100644 --- a/crates/tek_test/Cargo.toml +++ b/crates/tek_test/Cargo.toml @@ -5,5 +5,5 @@ version = "0.1.0" [dependencies] tek_core = { path = "../tek_core" } -tek_mixer = { path = "../tek_mixer" } +#tek_mixer = { path = "../tek_mixer" } tek_sequencer = { path = "../tek_sequencer" } diff --git a/crates/tek_test/src/main.rs b/crates/tek_test/src/main.rs index af5f21dc..e4baf6ac 100644 --- a/crates/tek_test/src/main.rs +++ b/crates/tek_test/src/main.rs @@ -8,17 +8,19 @@ pub fn main () -> Usually<()> { pub struct Demo { index: usize, - items: Vec>> + items: Vec>> } impl Demo { fn new () -> Self { - let mut items: Vec>> = vec![]; + let mut items: Vec>> = vec![]; items.push(Box::new(tek_sequencer::TransportPlayPauseButton { + _engine: Default::default(), value: Some(TransportState::Stopped), focused: true })); items.push(Box::new(tek_sequencer::TransportPlayPauseButton { + _engine: Default::default(), value: Some(TransportState::Rolling), focused: false })); @@ -26,15 +28,16 @@ impl Demo { } } -impl Content for Demo { - type Engine = E; - fn content (&self) -> impl Widget { - Align::Center(Layers(&[ +impl Content for Demo { + type Engine = Tui; + fn content (&self) -> impl Widget { + Align::Center(Layers([ &Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))), - &Layers(&[ - &"---------", - &Align::Center("...") - ]) + &self.items[self.index] + //&Layers(&[ + //&"---------", + //&Align::Center("...") + //]) ])) } }