wip: tek_test again

This commit is contained in:
🪞👃🪞 2024-09-09 22:57:00 +03:00
parent fa8282a9d5
commit 4c23aed40a
16 changed files with 190 additions and 172 deletions

1
Cargo.lock generated
View file

@ -2551,7 +2551,6 @@ name = "tek_test"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"tek_core", "tek_core",
"tek_mixer",
"tek_sequencer", "tek_sequencer",
] ]

View file

@ -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<Engine = E>]); pub struct Layers<'a, E: Engine, const N: usize>(pub [&'a dyn Widget<Engine = E>;N]);
// this actually works, except for the type inference // this actually works, except for the type inference
//pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator<Item = &'a dyn Render<E>>>( //pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator<Item = &'a dyn Render<E>>>(

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
pub trait Widget { pub trait Widget: Send + Sync {
type Engine: Engine; type Engine: Engine;
fn layout (&self, to: <<Self as Widget>::Engine as Engine>::Area) -> fn layout (&self, to: <<Self as Widget>::Engine as Engine>::Area) ->
Perhaps<<<Self as Widget>::Engine as Engine>::Area>; Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
@ -71,7 +71,7 @@ impl<E: Engine, W: Widget<Engine = E>> Widget for Option<W> {
} }
} }
pub trait Content { pub trait Content: Send + Sync {
type Engine: Engine; type Engine: Engine;
fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>; fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>;
} }

View file

@ -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; type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
let [x, y, ..] = area; let [x, y, ..] = area;

View file

@ -13,19 +13,21 @@ submod! {
mixer mixer
mixer_cli mixer_cli
mixer_handle mixer_handle
track
track_view
track_handle
plugin plugin
plugin_handle
plugin_lv2 plugin_lv2
plugin_lv2_gui plugin_lv2_gui
plugin_view
plugin_vst2 plugin_vst2
plugin_vst3 plugin_vst3
sample sample
sample_add sample_add
sampler sampler
sampler_edn sampler_edn
sampler_view
sampler_handle sampler_handle
sampler_view
track
track_handle
track_view
voice voice
} }

View file

@ -1,7 +1,8 @@
use crate::*; use crate::*;
/// A plugin device. /// A plugin device.
pub struct Plugin { pub struct Plugin<E> {
_engine: PhantomData<E>,
pub name: String, pub name: String,
pub path: Option<String>, pub path: Option<String>,
pub plugin: Option<PluginKind>, pub plugin: Option<PluginKind>,
@ -9,63 +10,13 @@ pub struct Plugin {
pub mapping: bool, pub mapping: bool,
pub ports: JackPorts, pub ports: JackPorts,
} }
impl Handle<Tui> for Plugin {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
handle_keymap(self, &from.event(), KEYMAP_PLUGIN).map(|x|Some(x))
}
}
process!(Plugin = Plugin::process);
impl Render<Tui> 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. impl<E> Plugin<E> {
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
impl Plugin {
pub fn new_lv2 (name: &str, path: &str) -> Usually<JackDevice<Tui>> { pub fn new_lv2 (name: &str, path: &str) -> Usually<JackDevice<Tui>> {
let plugin = LV2Plugin::new(path)?; let plugin = LV2Plugin::new(path)?;
jack_from_lv2(name, &plugin.plugin)? jack_from_lv2(name, &plugin.plugin)?
.run(|ports|Box::new(Self { .run(|ports|Box::new(Self {
_engine: Default::default(),
name: name.into(), name: name.into(),
path: Some(String::from(path)), path: Some(String::from(path)),
plugin: Some(PluginKind::LV2(plugin)), plugin: Some(PluginKind::LV2(plugin)),
@ -77,6 +28,7 @@ impl Plugin {
/// Create a plugin host device. /// Create a plugin host device.
pub fn new (name: &str) -> Usually<Self> { pub fn new (name: &str) -> Usually<Self> {
Ok(Self { Ok(Self {
_engine: Default::default(),
name: name.into(), name: name.into(),
path: None, path: None,
plugin: None, plugin: None,
@ -85,7 +37,10 @@ impl Plugin {
ports: JackPorts::default() ports: JackPorts::default()
}) })
} }
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { }
impl<E> Process for Plugin<E> {
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
match self.plugin.as_mut() { match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { Some(PluginKind::LV2(LV2Plugin {
features, features,
@ -141,79 +96,12 @@ impl Plugin {
Control::Continue Control::Continue
} }
} }
fn draw_header (state: &Plugin, to: &mut Tui, x: u16, y: u16, w: u16) -> Usually<Rect> {
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. /// Supported plugin formats.
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin { pub enum PluginKind {
[Up, NONE, "/plugin/cursor_up", "move cursor up", |s: &mut Plugin|{ LV2(LV2Plugin),
s.selected = s.selected.saturating_sub(1); VST2 {
Ok(true) instance: ::vst::host::PluginInstance
}], },
[Down, NONE, "/plugin/cursor_down", "move cursor down", |s: &mut Plugin|{ VST3,
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<Tui> for Plugin {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
} }

View file

@ -0,0 +1,67 @@
use crate::*;
impl Handle<Tui> for Plugin<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
handle_keymap(self, &from.event(), KEYMAP_PLUGIN).map(|x|Some(x))
}
}
/// Key bindings for plugin device.
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = 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)
}],
});

View file

@ -0,0 +1,54 @@
use crate::*;
impl Widget for Plugin<Tui> {
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 <E> (state: &Plugin<E>, to: &mut Tui, x: u16, y: u16, w: u16) -> Usually<Rect> {
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 })
}

View file

@ -9,13 +9,13 @@ pub fn main () -> Usually<()> {
struct ArrangerStandalone<E: Engine> { struct ArrangerStandalone<E: Engine> {
/// Contains all the sequencers. /// Contains all the sequencers.
arranger: Arranger<E>, arranger: Arranger<E>,
/// Controls the JACK transport. /// Controls the JACK transport.
transport: Option<TransportToolbar>, transport: Option<TransportToolbar<E>>,
/// This allows the sequencer view to be moved or hidden. /// This allows the sequencer view to be moved or hidden.
show_sequencer: Option<tek_core::Direction>, show_sequencer: Option<tek_core::Direction>,
/// ///
focus: usize, focus: usize,
} }
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
@ -38,7 +38,7 @@ pub struct ArrangerCli {
scenes: usize, scenes: usize,
} }
impl<E: Engine> ArrangerStandalone<E> { impl ArrangerStandalone<Tui> {
pub fn from_args () -> Usually<Self> { pub fn from_args () -> Usually<Self> {
let args = ArrangerCli::parse(); let args = ArrangerCli::parse();
let mut arranger = Arranger::new(""); let mut arranger = Arranger::new("");
@ -73,10 +73,15 @@ impl<E: Engine> ArrangerStandalone<E> {
} }
} }
impl Render<Tui> for ArrangerStandalone<Tui> { impl Widget for ArrangerStandalone<Tui> {
type Engine = Tui;
fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> {
Ok(Some(to))
}
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let sequencer = self.arranger.sequencer(); let sequencer = self.arranger.sequencer()
.map(|t|t as &dyn Widget<Engine = Tui>);
let result = Split::down() let result = Split::down()
.add_ref(&self.transport) .add_ref(&self.transport)
.add_ref(&self.arranger) .add_ref(&self.arranger)

View file

@ -4,7 +4,7 @@ pub fn draw (state: &Arranger<Tui>, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)]; let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)];
let tracks = state.tracks.as_slice(); let tracks = state.tracks.as_slice();
Layers(&[ Layers([
&state.focused.then_some(FillBg(COLOR_BG0)), &state.focused.then_some(FillBg(COLOR_BG0)),
&Split::right() &Split::right()
.add(TrackNameColumn(tracks, state.selected)) .add(TrackNameColumn(tracks, state.selected))

View file

@ -38,15 +38,15 @@ pub fn draw <'a, 'b> (
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16; let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
let tracks = state.tracks.as_ref(); let tracks = state.tracks.as_ref();
let scenes = state.scenes.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(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered)))
.add_ref(&ColumnSeparators(offset, cols)) &ColumnSeparators(offset, cols),
.add_ref(&RowSeparators(rows)) &RowSeparators(rows),
.add_ref(&CursorFocus(state.selected, offset, cols, rows)) &CursorFocus(state.selected, offset, cols, rows),
.add_ref(&Split::down() &Split::down()
.add_ref(&TracksHeader(offset, cols, tracks)) .add_ref(&TracksHeader(offset, cols, tracks))
.add_ref(&SceneRows(offset, cols, rows, tracks, scenes))) .add_ref(&SceneRows(offset, cols, rows, tracks, scenes))
.render(to) ]).render(to.with_rect(area))
} }
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]); struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);

View file

@ -15,13 +15,13 @@ impl Sequencer<Tui> {
let area = [area.x() + 10, area.y(), area.w().saturating_sub(10), area.h().min(66)]; 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))?; 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()]; let area = [area.x() + 1, area.y(), area.w().saturating_sub(1), area.h()];
Layered::new() Layers([
.add_ref(&SequenceKeys(&self)) &SequenceKeys(&self),
.add_ref(&self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone()))) &self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone())),
.add_ref(&SequenceNotes(&self)) &SequenceNotes(&self),
.add_ref(&SequenceCursor(&self)) &SequenceCursor(&self),
.add_ref(&SequenceZoom(&self)) &SequenceZoom(&self),
.render(to.with_rect(area))?; ]).render(to.with_rect(area))?;
Ok(()) Ok(())
} }

View file

@ -23,7 +23,7 @@ pub struct TransportToolbar<E: Engine> {
pub clock: TransportClock<E>, pub clock: TransportClock<E>,
} }
impl<E: Engine> TransportToolbar<E> { impl<E: Engine> TransportToolbar<E> {
pub fn standalone () -> Usually<Arc<RwLock<Self>>> { pub fn standalone () -> Usually<Arc<RwLock<Self>>> where Self: 'static {
let mut transport = Self::new(None); let mut transport = Self::new(None);
transport.focused = true; transport.focused = true;
let jack = JackClient::Inactive( let jack = JackClient::Inactive(

View file

@ -22,7 +22,7 @@ impl Widget for TransportPlayPauseButton<Tui> {
} }
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let Self { value, focused, .. } = &self; let Self { value, focused, .. } = &self;
Layers(&[ Layers([
&focused.then_some(CORNERS), &focused.then_some(CORNERS),
&Inset::W(1, Styled(match value { &Inset::W(1, Styled(match value {
Some(TransportState::Stopped) => Some(GRAY_DIM.bold()), Some(TransportState::Stopped) => Some(GRAY_DIM.bold()),

View file

@ -5,5 +5,5 @@ version = "0.1.0"
[dependencies] [dependencies]
tek_core = { path = "../tek_core" } tek_core = { path = "../tek_core" }
tek_mixer = { path = "../tek_mixer" } #tek_mixer = { path = "../tek_mixer" }
tek_sequencer = { path = "../tek_sequencer" } tek_sequencer = { path = "../tek_sequencer" }

View file

@ -8,17 +8,19 @@ pub fn main () -> Usually<()> {
pub struct Demo<E: Engine> { pub struct Demo<E: Engine> {
index: usize, index: usize,
items: Vec<Box<dyn Component<E>>> items: Vec<Box<dyn Widget<Engine = E>>>
} }
impl Demo<Tui> { impl Demo<Tui> {
fn new () -> Self { fn new () -> Self {
let mut items: Vec<Box<dyn Component<Tui>>> = vec![]; let mut items: Vec<Box<dyn Widget<Engine = Tui>>> = vec![];
items.push(Box::new(tek_sequencer::TransportPlayPauseButton { items.push(Box::new(tek_sequencer::TransportPlayPauseButton {
_engine: Default::default(),
value: Some(TransportState::Stopped), value: Some(TransportState::Stopped),
focused: true focused: true
})); }));
items.push(Box::new(tek_sequencer::TransportPlayPauseButton { items.push(Box::new(tek_sequencer::TransportPlayPauseButton {
_engine: Default::default(),
value: Some(TransportState::Rolling), value: Some(TransportState::Rolling),
focused: false focused: false
})); }));
@ -26,15 +28,16 @@ impl Demo<Tui> {
} }
} }
impl<E: Engine> Content for Demo<E> { impl Content for Demo<Tui> {
type Engine = E; type Engine = Tui;
fn content (&self) -> impl Widget<Engine = E> { fn content (&self) -> impl Widget<Engine = Tui> {
Align::Center(Layers(&[ Align::Center(Layers([
&Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))), &Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))),
&Layers(&[ &self.items[self.index]
&"---------", //&Layers(&[
&Align::Center("...") //&"---------",
]) //&Align::Center("...")
//])
])) ]))
} }
} }