diff --git a/jack/src/jack_port.rs b/jack/src/jack_port.rs index df267172..5b10f92c 100644 --- a/jack/src/jack_port.rs +++ b/jack/src/jack_port.rs @@ -164,6 +164,7 @@ pub enum PortConnectName { pub name: PortConnectName, pub scope: PortConnectScope, pub status: Arc, Arc, PortConnectStatus)>>>, + pub info: Arc, } impl PortConnect { pub fn collect (exact: &[impl AsRef], re: &[impl AsRef], re_all: &[impl AsRef]) @@ -177,16 +178,19 @@ impl PortConnect { } /// Connect to this exact port pub fn exact (name: impl AsRef) -> Self { + let info = format!("=:{}", name.as_ref()).into(); let name = Exact(name.as_ref().into()); - Self { name, scope: One, status: Arc::new(RwLock::new(vec![])) } + Self { name, scope: One, status: Arc::new(RwLock::new(vec![])), info } } pub fn regexp (name: impl AsRef) -> Self { + let info = format!("~:{}", name.as_ref()).into(); let name = RegExp(name.as_ref().into()); - Self { name, scope: One, status: Arc::new(RwLock::new(vec![])) } + Self { name, scope: One, status: Arc::new(RwLock::new(vec![])), info } } pub fn regexp_all (name: impl AsRef) -> Self { + let info = format!("+:{}", name.as_ref()).into(); let name = RegExp(name.as_ref().into()); - Self { name, scope: All, status: Arc::new(RwLock::new(vec![])) } + Self { name, scope: All, status: Arc::new(RwLock::new(vec![])), info } } pub fn info (&self) -> Arc { let status = { @@ -205,6 +209,6 @@ impl PortConnect { let name = match &self.name { Exact(name) => format!("= {name}"), RegExp(name) => format!("~ {name}"), }; - format!("({}) {} {}", status, scope, name).into() + format!(" ({}) {} {}", status, scope, name).into() } } diff --git a/midi/src/midi_player.rs b/midi/src/midi_player.rs index 6ee20261..241c240d 100644 --- a/midi/src/midi_player.rs +++ b/midi/src/midi_player.rs @@ -67,14 +67,14 @@ impl MidiPlayer { jack: &Jack, clock: Option<&Clock>, clip: Option<&Arc>>, - midi_from: &[PortConnect], - midi_to: &[PortConnect], + _midi_from: &[PortConnect], + _midi_to: &[PortConnect], ) -> Usually { let name = name.as_ref(); let clock = clock.cloned().unwrap_or_default(); Ok(Self { - midi_ins: vec![JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,], - midi_outs: vec![JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ], + midi_ins: vec![],//JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,], + midi_outs: vec![],//JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ], play_clip: clip.map(|clip|(Moment::zero(&clock.timebase), Some(clip.clone()))), clock, note_buf: vec![0;8], diff --git a/tek/src/cli.rs b/tek/src/cli.rs index b5055f93..f3321109 100644 --- a/tek/src/cli.rs +++ b/tek/src/cli.rs @@ -194,7 +194,7 @@ impl Tek { ..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)? }; arranger.scenes_add(scenes); - arranger.tracks_add(tracks, Some(track_width), midi_froms, midi_tos); + arranger.tracks_add(tracks, Some(track_width), &[], &[]); arranger.selected = Selection::Clip(1, 1); Ok(arranger) } diff --git a/tek/src/keys.edn b/tek/src/keys.edn index 11065f76..5e88586a 100644 --- a/tek/src/keys.edn +++ b/tek/src/keys.edn @@ -2,10 +2,10 @@ (@shift-u redo 1) (@space clock toggle) (@shift-space clock toggle 0) -(@ctrl-a scene add) -(@ctrl-t track add) (@t select :track 0) (@tab edit :clip) (@c color) (@shift-I input add) (@shift-O output add) +(@shift-S scene add) +(@shift-T track add) diff --git a/tek/src/view.rs b/tek/src/view.rs index 4b0fb3b6..46b17bd3 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -192,11 +192,7 @@ impl Tek { fn w (&self) -> u16 { self.size.w() as u16 } fn h (&self) -> u16 { self.size.h() as u16 } fn w_sidebar (&self) -> u16 { - let w = self.w() / 10; - let w = w.min(if w > 80 { 20 } else if w > 70 { 18 } else if w > 60 { 16 } - else if w > 50 { 14 } else if w > 40 { 12 } else { 10 }); - //let w = if self.is_editing() { w / 2 } else { w }; - w as u16 + self.w() / if self.is_editing() { 16 } else { 8 } as u16 } fn w_tracks (&self, editing: bool, bigger: usize) -> u16 { self.tracks_sizes(editing, bigger).last().map(|(_, _, _, x)|x as u16).unwrap_or(0) @@ -296,30 +292,59 @@ impl Tek { let styled = Tui::fg_bg(track.color.lightest.rgb, track.color.base.rgb, content); map_east(x1 as u16, width, Fixed::x(width, styled)) }) } fn io_heading <'a, T: PortsSizes<'a>> ( - &'a self, - key: &'a str, label: &str, count: usize, - fg: Color, bg: Color, - iter: impl Fn()->T + Send + Sync + 'a, + &'a self, key: &'a str, label: &'a str, count: usize, + fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a, ) -> impl Content + 'a { - self.heading(key, label, count, Map::new(iter, + self.heading(key, label, count, self.io_ports(fg, bg, iter)) + } + fn io_ports <'a, T: PortsSizes<'a>> ( + &'a self, fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a + ) -> impl Content + 'a { + Map::new(iter, move|(index, name, connections, y, y2), _|map_south(y as u16, (y2-y) as u16, Bsp::s( Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(Bsp::e(" 󰣲 ", name))))), Map::new(||connections.iter(), move|connect, index|map_south(index as u16, 1, - Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, connect.info())))))))))) } + Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, + &connect.info[..(self.w_sidebar() as usize).min(connect.info.len())])))))))))} + fn io_connections <'a, T: PortsSizes<'a>> ( + &'a self, fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a + ) -> impl Content + 'a { + Map::new(iter, + move|(index, name, connections, y, y2), _|map_south(y as u16, (y2-y) as u16, Bsp::s( + Fill::x(Tui::bold(true, Self::wrap(bg, fg, Fill::x(Align::w("▞▞▞▞ ▞▞▞▞"))))), + Map::new(||connections.iter(), move|connect, index|map_south(index as u16, 1, + Fill::x(Align::w(Tui::bold(false, Self::wrap(bg, fg, Fill::x(""))))))))))} fn heading <'a> ( - &'a self, key: &'a str, label: &str, count: usize, + &'a self, key: &'a str, label: &'a str, count: usize, content: impl Content + Send + Sync + 'a ) -> impl Content + 'a { - Fill::xy(Align::nw(Bsp::s( - self.button(key, format!("{:10} ({})", label, count)), - content))) } - fn button <'a, K: Content + 'a, L: Content + 'a> (&'a self, key: K, label: L) - -> impl Content + 'a - { - let compact = !self.is_editing(); + let count = format!("{count}"); + Fill::xy(Align::w(Bsp::s(Fill::x(Align::w(self.button3(key, label, count))), content))) + } + fn button2 <'a, K, L> (&'a self, key: K, label: L) -> impl Content + 'a + where K: Content + 'a, L: Content + 'a { Tui::bold(true, Bsp::e( Tui::fg_bg(Tui::g(0), Tui::orange(), Bsp::e(key, Tui::fg(Tui::g(96), "▐"))), - When::new(compact, Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label))))) } + When::new(!self.is_editing(), Tui::fg_bg(Tui::g(255), Tui::g(96), label)))) } + fn button3 <'a, K, L, V> (&'a self, key: K, label: L, value: V) -> impl Content + 'a + where K: Content + 'a, L: Content + 'a, V: Content + 'a { + let editing = self.is_editing(); + Tui::bold(true, Bsp::e( + Tui::fg_bg(Tui::g(0), Tui::orange(), Bsp::e(key, Tui::fg(if editing { + Tui::g(128) + } else { + Tui::g(96) + }, "▐"))), + Bsp::e( + When::new(!editing, Bsp::e( + Tui::fg_bg(Tui::g(255), Tui::g(96), label), + Tui::fg_bg(Tui::g(128), Tui::g(96), "▐"), + )), + Bsp::e( + Tui::fg_bg(Tui::g(224), Tui::g(128), value), + Tui::fg_bg(Tui::g(128), Reset, "▌"), + ) + ))) } fn wrap (bg: Color, fg: Color, content: impl Content) -> impl Content { Bsp::e(Tui::fg_bg(bg, Reset, "▐"), Bsp::w(Tui::fg_bg(bg, Reset, "▌"), Tui::fg_bg(fg, bg, content))) } @@ -344,74 +369,92 @@ impl Tek { let data = (self.selected.track().unwrap_or(0), self.tracks().len()); self.fmtd.write().unwrap().trks.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1)); - self.button(" T", Bsp::e("track ", self.fmtd.read().unwrap().trks.view.clone())) + self.button3(" T", "track", self.fmtd.read().unwrap().trks.view.clone()) } // ACTIVE CLIPS /////////////////////////////////////////////////////////////////////////////// fn view_clips_into (&self) -> impl Content + use<'_> { - let heading = "Into:"; - let content = self.per_track_top(|_, _|" --- "); + let heading = Align::e("Into:"); + let content = self.per_track_top(|_, _|Tui::bg(Reset, " --- ")); self.row_top(self.w(), 1, heading, content) } fn view_clips_from (&self) -> impl Content + use<'_> { - let heading = "From:"; - let content = self.per_track_top(|_, _|" --- "); + let heading = Align::e("From:"); + let content = self.per_track_top(|_, _|Tui::bg(Reset, " --- ")); self.row_top(self.w(), 1, heading, content) } fn view_clips_next (&self) -> impl Content + use<'_> { - let heading = "Next:"; - let content = self.per_track_top(|_, _|" --- "); + let heading = Align::e("Next:"); + let content = self.per_track_top(|_, _|Tui::bg(Reset, " --- ")); self.row_top(self.w(), 1, heading, content) } // INPUTS ///////////////////////////////////////////////////////////////////////////////////// fn view_inputs (&self) -> impl Content + use<'_> { - let fg = Tui::g(224); - let heading = self.io_heading(" I", "midi ins", self.midi_ins.len(), fg, Tui::g(64), - ||self.inputs_sizes()); - let content = self.per_track_top(move|t, track|{ + let fg = Tui::g(224); + let key = " I"; + let label = "midi ins"; + let count = self.midi_ins.len(); + let heading = Fill::x(Align::w(self.button3(key, label, format!("{count}")))); + let rows = self.io_ports(fg, Tui::g(32), ||self.inputs_sizes()); + let columns = self.per_track_top(move|t, track|{ let rec = track.player.recording; let mon = track.player.monitoring; let rec = if rec { White } else { track.color.darkest.rgb }; let mon = if mon { White } else { track.color.darkest.rgb }; let bg = if self.selected().track() == Some(t+1) { track.color.light.rgb } else { track.color.base.rgb }; let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; - let headers = Self::wrap(bg, fg, Tui::bold(true, - Bsp::e(Tui::fg_bg(rec, bg, "Rec "), Tui::fg_bg(rec, bg, "Mon ")))); - let toggles = Map::new(||self.inputs_sizes(), move|(index, name, conn, y, y2), _| - map_south(y as u16, (y2-y) as u16, Self::wrap(bg, fg, - Bsp::e(Tui::fg_bg(rec, bg, "R▞▞▞▞"), Tui::fg_bg(mon, bg, "M▞▞▞▞"))))); - Bsp::a(Fill::y(Align::n(headers)), toggles)}); - - let row = self.row_top(self.w(), self.h_inputs(), heading, content); - - row - //Bsp::s(Tui::bg(Black, row), self.view_clips_into()) + Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e( + Tui::fg_bg(rec, bg, "Rec "), + Tui::fg_bg(rec, bg, "Mon ")))))}); + let toggles = self.per_track_top(move|t, track|self.io_connections( + track.color.dark.rgb, track.color.darker.rgb, ||self.inputs_sizes())); + //let toggles = Map::new(||self.inputs_sizes(), move|(index, name, conn, y, y2), _| + //map_south(y as u16, (y2-y) as u16, Self::wrap(bg, fg, Fill::x(Bsp::e( + //Tui::fg_bg(rec, bg, format!("{y} {y2} {name}")), + //Tui::fg_bg(mon, bg, "M▞▞▞▞")))))); + //Bsp::a(Fill::y(Align::n(headers)), Fill::y(Align::n(toggles)))}); + Bsp::s( + Bsp::s( + self.row_top(self.w(), 1, heading, columns), + self.row_top(self.w(), self.h_inputs() - 1, rows, toggles), + ), + self.view_clips_into() + ) } // OUTPUTS //////////////////////////////////////////////////////////////////////////////////// fn view_outputs (&self) -> impl Content + use<'_> { - let fg = Tui::g(224); - let heading = self.io_heading(" O", "midi outs", self.midi_outs.len(), fg, Tui::g(64), - ||self.outputs_sizes()); - let content = self.per_track_top(move|t, track|{ + let fg = Tui::g(224); + let key = " O"; + let label = "midi outs"; + let count = self.midi_outs.len(); + let heading = Fill::x(Align::w(self.button3(key, label, format!("{count}")))); + let rows = self.io_ports(fg, Tui::g(32), ||self.outputs_sizes()); + let columns = self.per_track_top(move|t, track|{ let mute = false; let solo = false; let mute = if mute { White } else { track.color.darkest.rgb }; let solo = if solo { White } else { track.color.darkest.rgb }; let bg = if self.selected().track() == Some(t+1) { track.color.light.rgb } else { track.color.base.rgb }; let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; - let headers = Self::wrap(bg, fg, Tui::bold(true, - Bsp::e(Tui::fg_bg(mute, bg, "Play "), Tui::fg_bg(solo, bg, "Solo ")))); - let toggles = Map::new(||self.outputs_sizes(), move|(index, name, conn, y, y2), _| - map_south(y as u16, (y2-y) as u16, Self::wrap(bg, fg, - Bsp::e(Tui::fg_bg(mute, bg, "P▞▞▞▞"), Tui::fg_bg(solo, bg, "S▞▞▞▞"))))); - Bsp::a(Fill::y(Align::n(headers)), toggles)}); - - let row = self.row_top(self.w(), self.h_outputs(), heading, content); - - row - //Bsp::s(Bsp::s(self.view_clips_next(), self.view_clips_from()), Tui::bg(Black, row)) + Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e( + Tui::fg_bg(mute, bg, "Play "), + Tui::fg_bg(solo, bg, "Solo ")))))}); + let toggles = self.per_track_top(move|t, track|self.io_connections( + track.color.dark.rgb, track.color.darker.rgb, ||self.outputs_sizes())); + //Map::new(||self.outputs_sizes(), move|(index, name, conn, y, y2), _| + //map_south(y as u16, (y2-y) as u16, Self::wrap(bg, fg, Fill::x(Bsp::e( + //Tui::fg_bg(mute, bg, "P▞▞▞▞"), + //Tui::fg_bg(solo, bg, "S▞▞▞▞")))))); + //Bsp::a(Fill::y(Align::n(headers)), Fill::y(Align::n(toggles)))}); + Align::n(Bsp::s( + Bsp::s(self.view_clips_next(), self.view_clips_from()), + Bsp::s( + self.row_top(self.w(), 1, heading, columns), + self.row_top(self.w(), self.h_outputs() - 1, rows, toggles), + ) + )) } // SCENES ///////////////////////////////////////////////////////////////////////////////////// @@ -482,10 +525,8 @@ impl Tek { } fn view_scene_add (&self) -> impl Content + use<'_> { let data = (self.selected().scene().unwrap_or(0), self.scenes().len()); - self.fmtd.write().unwrap().scns.update(Some(data), - rewrite!(buf, "({}/{})", data.0, data.1)); - self.button(" C-a ", Bsp::e(" add scene ", - self.fmtd.read().unwrap().scns.view.clone())) + self.fmtd.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1)); + self.button3(" S", "add scene", self.fmtd.read().unwrap().scns.view.clone()) } } fn colors ( diff --git a/tek/src/view_arranger.edn b/tek/src/view_arranger.edn index f32c92e9..6daf21a7 100644 --- a/tek/src/view_arranger.edn +++ b/tek/src/view_arranger.edn @@ -1,6 +1,6 @@ (bsp/s - (max/y 1 :toolbar) - (fill/x (align/c (bsp/a (fill/xy (align/e :pool)) + (fixed/y 1 :toolbar) + (fill/xy (align/c (bsp/a (fill/xy (align/e :pool)) (bsp/a (fill/xy (align/n (bsp/s :tracks :inputs))) (bsp/a