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"
dependencies = [
"tek_core",
"tek_mixer",
"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
//pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator<Item = &'a dyn Render<E>>>(

View file

@ -1,6 +1,6 @@
use crate::*;
pub trait Widget {
pub trait Widget: Send + Sync {
type Engine: Engine;
fn layout (&self, to: <<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;
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;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
let [x, y, ..] = area;

View file

@ -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
}

View file

@ -1,7 +1,8 @@
use crate::*;
/// A plugin device.
pub struct Plugin {
pub struct Plugin<E> {
_engine: PhantomData<E>,
pub name: String,
pub path: Option<String>,
pub plugin: Option<PluginKind>,
@ -9,63 +10,13 @@ pub struct Plugin {
pub mapping: bool,
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.
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
impl Plugin {
impl<E> Plugin<E> {
pub fn new_lv2 (name: &str, path: &str) -> Usually<JackDevice<Tui>> {
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<Self> {
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<E> Process for Plugin<E> {
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<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.
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);
}
/// Supported plugin formats.
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
_ => {}
}
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!()
}
VST3,
}

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

@ -11,7 +11,7 @@ struct ArrangerStandalone<E: Engine> {
/// Contains all the sequencers.
arranger: Arranger<E>,
/// Controls the JACK transport.
transport: Option<TransportToolbar>,
transport: Option<TransportToolbar<E>>,
/// This allows the sequencer view to be moved or hidden.
show_sequencer: Option<tek_core::Direction>,
///
@ -38,7 +38,7 @@ pub struct ArrangerCli {
scenes: usize,
}
impl<E: Engine> ArrangerStandalone<E> {
impl ArrangerStandalone<Tui> {
pub fn from_args () -> Usually<Self> {
let args = ArrangerCli::parse();
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]> {
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()
.add_ref(&self.transport)
.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 = [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))

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 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)]);

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)];
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(())
}

View file

@ -23,7 +23,7 @@ pub struct TransportToolbar<E: Engine> {
pub clock: TransportClock<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);
transport.focused = true;
let jack = JackClient::Inactive(

View file

@ -22,7 +22,7 @@ impl Widget for TransportPlayPauseButton<Tui> {
}
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()),

View file

@ -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" }

View file

@ -8,17 +8,19 @@ pub fn main () -> Usually<()> {
pub struct Demo<E: Engine> {
index: usize,
items: Vec<Box<dyn Component<E>>>
items: Vec<Box<dyn Widget<Engine = E>>>
}
impl Demo<Tui> {
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 {
_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<Tui> {
}
}
impl<E: Engine> Content for Demo<E> {
type Engine = E;
fn content (&self) -> impl Widget<Engine = E> {
Align::Center(Layers(&[
impl Content for Demo<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
Align::Center(Layers([
&Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))),
&Layers(&[
&"---------",
&Align::Center("...")
])
&self.items[self.index]
//&Layers(&[
//&"---------",
//&Align::Center("...")
//])
]))
}
}