From 2ea6a7bd8b5e5d3b7191a06083e5e131805282c4 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 21 Jan 2025 15:51:27 +0100 Subject: [PATCH] move string format results to ViewCache --- tek/src/cli.rs | 7 -- tek/src/lib.rs | 1 - tek/src/model.rs | 8 +- tek/src/view.rs | 255 ++++++++++++++++++++++++----------------------- 4 files changed, 133 insertions(+), 138 deletions(-) diff --git a/tek/src/cli.rs b/tek/src/cli.rs index 18fce22a..8c5b847d 100644 --- a/tek/src/cli.rs +++ b/tek/src/cli.rs @@ -110,13 +110,6 @@ impl Tek { keys_track: SourceIter(KEYS_TRACK), keys_scene: SourceIter(KEYS_SCENE), keys_mix: SourceIter(KEYS_MIX), - fmtd_beat: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_time: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_bpm: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_sr: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_buf: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_lat: Arc::new(RwLock::new(String::with_capacity(16))), - fmtd_stop: "⏹".into(), ..Default::default() }; tek.sync_lead(sync_lead); diff --git a/tek/src/lib.rs b/tek/src/lib.rs index d022eecf..0d309fd5 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -27,4 +27,3 @@ mod model; pub use self::model::*; mod view; pub use self::view::*; mod keys; pub use self::keys::*; mod audio; pub use self::audio::*; -use std::fmt::Write; diff --git a/tek/src/model.rs b/tek/src/model.rs index 27c79890..945a0d3d 100644 --- a/tek/src/model.rs +++ b/tek/src/model.rs @@ -34,13 +34,7 @@ use crate::*; pub keys_scene: SourceIter<'static>, pub keys_mix: SourceIter<'static>, - pub fmtd_beat: Arc>, - pub fmtd_time: Arc>, - pub fmtd_bpm: Arc>, - pub fmtd_sr: Arc>, - pub fmtd_buf: Arc>, - pub fmtd_lat: Arc>, - pub fmtd_stop: Arc, + pub fmtd: ViewCache, } has_size!(|self: Tek|&self.size); has_clock!(|self: Tek|self.clock); diff --git a/tek/src/view.rs b/tek/src/view.rs index 1b9886af..7394ea63 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -1,4 +1,27 @@ use crate::*; +use std::fmt::Write; +#[derive(Debug)] pub(crate) struct ViewCache { + beat: Arc>, + time: Arc>, + bpm: Arc>, + sr: Arc>, + buf: Arc>, + lat: Arc>, + stop: Arc, +} +impl Default for ViewCache { + fn default () -> Self { + Self { + beat: Arc::new(RwLock::new(String::with_capacity(16))), + time: Arc::new(RwLock::new(String::with_capacity(16))), + bpm: Arc::new(RwLock::new(String::with_capacity(16))), + sr: Arc::new(RwLock::new(String::with_capacity(16))), + buf: Arc::new(RwLock::new(String::with_capacity(16))), + lat: Arc::new(RwLock::new(String::with_capacity(16))), + stop: "⏹".into(), + } + } +} view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":editor" => (&self.editor).boxed(), ":pool" => self.view_pool().boxed(), @@ -15,7 +38,7 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { self.scenes().len()))))).boxed(), }); provide_num!(u16: |self: Tek| { - ":sidebar-w" => self.sidebar_w(), + ":sidebar-w" => self.w_sidebar(), ":sample-h" => if self.is_editing() { 0 } else { 5 }, ":samples-w" => if self.is_editing() { 4 } else { 11 }, ":samples-y" => if self.is_editing() { 1 } else { 0 }, @@ -34,6 +57,14 @@ macro_rules! per_track { }))).into() }} } +macro_rules! io_header { + ($self:ident, $key:expr, $label:expr, $count:expr, $content:expr) => { + (move||{ + let button = $self.button($key, format!("{:10} ({})", $label, $count)); + Bsp::s(Fill::x(Align::w(button)), $content).boxed() + }).into() + } +} impl Tek { fn view_clock (&self) -> impl Content + use<'_> { Outer(false, Style::default().fg(Tui::g(0))).enclose(row!( @@ -46,9 +77,9 @@ impl Tek { let compact = self.size.w() > 80; let clock = self.clock(); let delta = |start: &Moment|clock.global.usec.get() - start.usec.get(); - let mut fmtd_beat = self.fmtd_beat.write().unwrap(); - let mut fmtd_time = self.fmtd_time.write().unwrap(); - let mut fmtd_bpm = self.fmtd_bpm.write().unwrap(); + let mut fmtd_beat = self.fmtd.beat.write().unwrap(); + let mut fmtd_time = self.fmtd.time.write().unwrap(); + let mut fmtd_bpm = self.fmtd.bpm.write().unwrap(); fmtd_beat.clear(); fmtd_time.clear(); fmtd_bpm.clear(); @@ -63,21 +94,21 @@ impl Tek { } let theme = ItemPalette::G[128]; Thunk::new(move||Either::new(compact, - row!(FieldH(theme, "BPM", self.fmtd_bpm.clone()), - FieldH(theme, "Beat", self.fmtd_beat.clone()), - FieldH(theme, "Time", self.fmtd_time.clone())), - row!(FieldV(theme, "BPM", self.fmtd_bpm.clone()), - FieldV(theme, "Beat", self.fmtd_beat.clone()), - FieldV(theme, "Time", self.fmtd_time.clone())))) + row!(FieldH(theme, "BPM", self.fmtd.bpm.clone()), + FieldH(theme, "Beat", self.fmtd.beat.clone()), + FieldH(theme, "Time", self.fmtd.time.clone())), + row!(FieldV(theme, "BPM", self.fmtd.bpm.clone()), + FieldV(theme, "Beat", self.fmtd.beat.clone()), + FieldV(theme, "Time", self.fmtd.time.clone())))) } fn view_engine_stats (&self) -> impl Content + use<'_> { let compact = self.size.w() > 80; let clock = self.clock(); let rate = clock.timebase.sr.get(); let chunk = clock.chunk.load(Relaxed); - let mut fmtd_sr = self.fmtd_sr.write().unwrap(); - let mut fmtd_buf = self.fmtd_buf.write().unwrap(); - let mut fmtd_lat = self.fmtd_lat.write().unwrap(); + let mut fmtd_sr = self.fmtd.sr.write().unwrap(); + let mut fmtd_buf = self.fmtd.buf.write().unwrap(); + let mut fmtd_lat = self.fmtd.lat.write().unwrap(); fmtd_sr.clear(); write!(&mut fmtd_sr, "{}", if compact {format!("{:.1}kHz", rate / 1000.)} else {format!("{:.0}Hz", rate)}); fmtd_buf.clear(); @@ -86,12 +117,12 @@ impl Tek { write!(&mut fmtd_lat, "{:.1}ms", chunk as f64 / rate * 1000.); let theme = ItemPalette::G[128]; Either::new(compact, - row!(FieldH(theme, "SR", self.fmtd_sr.clone()), - FieldH(theme, "Buf", self.fmtd_buf.clone()), - FieldH(theme, "Lat", self.fmtd_lat.clone())), - row!(FieldV(theme, "SR", self.fmtd_sr.clone()), - FieldV(theme, "Buf", self.fmtd_buf.clone()), - FieldV(theme, "Lat", self.fmtd_lat.clone()))) + row!(FieldH(theme, "SR", self.fmtd.sr.clone()), + FieldH(theme, "Buf", self.fmtd.buf.clone()), + FieldH(theme, "Lat", self.fmtd.lat.clone())), + row!(FieldV(theme, "SR", self.fmtd.sr.clone()), + FieldV(theme, "Buf", self.fmtd.buf.clone()), + FieldV(theme, "Lat", self.fmtd.lat.clone()))) } fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content + 'a { col!( @@ -114,7 +145,7 @@ impl Tek { else { Green }, ()))) } fn view_meters (&self, values: &[f32;2]) -> impl Content + use<'_> { - col!( + Bsp::s( format!("L/{:>+9.3}", values[0]), format!("R/{:>+9.3}", values[1]), ) @@ -143,30 +174,82 @@ impl Tek { self.selected().scene().unwrap_or(0), self.scenes().len())) } - fn view_tracks (&self) -> impl Content + use<'_> { - let h = 1; - self.view_row(self.w(), 1, self.track_header(), self.track_cells()) + fn view_row <'a> ( + &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a + ) -> impl Content + 'a { + Fixed::y(h, Bsp::e( + Fixed::x(self.w_sidebar() as u16, a), + Fill::x(Align::c(Fixed::xy(w, h, b))) + )) } fn view_inputs (&self) -> impl Content + use<'_> { - let h = 1 + self.midi_ins.len() as u16; - self.view_row(self.w(), h, self.input_header(), self.input_cells()) + let fg = Tui::g(224); + let bg = Tui::g(64); + let h = 1 + self.midi_ins.len() as u16; + let header: ThunkBox<_> = io_header!(self, " I ", " midi ins", self.midi_ins.len(), self.midi_ins().get(0).map( + move|input: &JackPort|Bsp::s( + Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(input.name.clone())))), + input.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, + Tui::fg_bg(fg, bg, connect.info())))))))); + let rec = false; + let mon = false; + let cells: ThunkBox<_> = per_track!(|self, track, _t|Bsp::s(Tui::bold(true, row!( + Tui::fg_bg(if rec { White } else { track.color.light.rgb }, track.color.dark.rgb, "Rcrd"), + Tui::fg_bg(if rec { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), + Tui::fg_bg(if mon { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mntr"), + )), row!( + Tui::fg_bg(if rec { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), + Tui::fg_bg(if rec { White } else { track.color.dark.rgb }, track.color.darker.rgb, "▐"), + Tui::fg_bg(if mon { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), + ))); + self.view_row(self.w(), h, header, cells) } fn view_outputs (&self) -> impl Content + use<'_> { - let h = 1 + self.midi_outs.len(); - self.view_row(self.w(), h as u16, - self.output_header(), - self.output_cells()) + let fg = Tui::g(224); + let bg = Tui::g(64); + let h = 1 + self.midi_outs.len() as u16; + let header: ThunkBox<_> = io_header!(self, " O ", " midi outs", self.midi_outs.len(), self.midi_outs().get(0).map( + move|output: &JackPort|Bsp::s( + Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(output.name.clone())))), + output.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, + Tui::fg_bg(fg, bg, connect.info())))))))); + let mute = false; + let solo = false; + let cells: ThunkBox<_> = per_track!(|self, track, _t|Bsp::s(Tui::bold(true, row!( + Tui::fg_bg(if mute { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mute"), + Tui::fg_bg(if mute { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), + Tui::fg_bg(if solo { White } else { track.color.light.rgb }, track.color.dark.rgb, "Solo"), + )), row!( + Tui::fg_bg(if mute { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), + Tui::fg_bg(if mute { White } else { track.color.darker.rgb }, track.color.darker.rgb, "▐"), + Tui::fg_bg(if solo { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), + ))); + self.view_row(self.w(), h, header, cells) + } + fn view_tracks (&self) -> impl Content + use<'_> { + let h = 1; + let add_track = ||self.button(" C-t ", format!(" add track ({}/{})", + self.selected.track().unwrap_or(0), + self.tracks().len())); + let header: ThunkBox<_> = + (move||Tui::bg(Tui::g(32), Fill::x(Align::w(add_track()))).boxed()).into(); + let cells: ThunkBox<_> = per_track!(|self, track, t|{ + let active = self.selected().track() == Some(t+1); + let name = &track.name; + let fg = track.color.lightest.rgb; + let bg = if active { track.color.light.rgb } else { track.color.base.rgb }; + let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; + let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; + let bs = Style::default().fg(bfg).bg(bg); + let cell = Bsp::e( + Tui::fg_bg(bg, bg2, "▐"), + Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::nw(name))))); + Outer(active, bs).enclose(cell) + }); + self.view_row(self.w(), 1, header, cells) } fn view_scenes (&self) -> impl Content + use<'_> { - Outer(false, Style::default().fg(Tui::g(0))).enclose_bg({ - let w = self.w(); - let d = 6 + self.midi_ins.len() + self.midi_outs.len(); - let h = self.size.h().saturating_sub(d) as u16; - self.view_row(w, h, self.scene_header(), self.clip_columns()) - }) - } - fn scene_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - (move||{ + let header: ThunkBox<_> = (move||{ let last_color = Arc::new(RwLock::new(ItemPalette::G[0])); let iter = ||self.scenes_sizes(self.is_editing(), 2, 15); Map::new(iter, move|(_, scene, y1, y2), i| { @@ -185,9 +268,7 @@ impl Tek { map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Outer(false, Style::default().fg(Tui::g(0))).enclose(cell)))) }).boxed() - }).into() - } - fn clip_columns <'a> (&'a self) -> ThunkBox<'a, TuiOut> { + }).into(); let editing = self.is_editing(); let tracks = move||self.tracks_sizes(editing, self.editor_w()); let scenes = move||self.scenes_sizes(editing, 2, 15); @@ -195,9 +276,9 @@ impl Tek { let selected_scene = self.selected().scene(); let border = |x|Outer(false, Style::default().fg(Tui::g(0))).enclose(x); let d = 6 + self.midi_ins.len() + self.midi_outs.len(); - (move||Align::c(Map::new(tracks, { + let cells: ThunkBox<_> = (move||Align::c(Map::new(tracks, { let last_color = Arc::new(RwLock::new(ItemPalette::default())); - let area = self.size.w().saturating_sub(self.sidebar_w() as usize * 2); + let area = self.size.w().saturating_sub(self.w_sidebar() as usize * 2); move|(_, track, x1, x2), t| { let last_color = last_color.clone(); let same_track = selected_track == Some(t+1); @@ -247,7 +328,7 @@ impl Tek { let name = clip.map(|c|c.read().unwrap().name.clone()); Align::nw(Tui::fg(fg, Bsp::e(icon, Bsp::e(Tui::bold(true, name), " ")))) }; - let area = self.size.h().saturating_sub(d);//self.sidebar_w() as usize * 2); + let area = self.size.h().saturating_sub(d);//self.w_sidebar() as usize * 2); Either(y2 > area, (), map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active, Thunk::new(move||Bsp::a( Fill::xy(Align::nw(button(tab, label()))), @@ -266,96 +347,24 @@ impl Tek { })) }))).boxed() } - })).boxed()).into() - } - fn view_row <'a> ( - &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a - ) -> impl Content + 'a { - Fixed::y(h, Bsp::e( - Fixed::x(self.sidebar_w() as u16, a), - Fill::x(Align::c(Fixed::xy(w, h, b))) - )) - } + })).boxed()).into(); + Outer(false, Style::default().fg(Tui::g(0))).enclose_bg({ + let w = self.w(); + let d = 6 + self.midi_ins.len() + self.midi_outs.len(); + let h = self.size.h().saturating_sub(d) as u16; + self.view_row(w, h, header, cells)}) } fn w (&self) -> u16 { self.tracks_sizes(self.is_editing(), self.editor_w()) .last() .map(|x|x.3 as u16) .unwrap_or(0) } - fn sidebar_w (&self) -> u16 { + fn w_sidebar (&self) -> u16 { let w = self.size.w(); let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; let w = if self.is_editing() { 8 } else { w }; w } - fn input_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let fg = Tui::g(224); - let bg = Tui::g(64); - let input = move|input: &JackPort|Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(input.name.clone())))), - input.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, - Tui::fg_bg(fg, bg, connect.info())))))); - (move||{ - let label = format!(" midi ins ({})", self.midi_ins().len()); - let button = Fill::x(Align::w(self.button(" I ", label))); - Bsp::s(button, self.midi_ins().get(0).map(input)).boxed() - }).into() - } - fn input_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let rec = false; - let mon = false; - per_track!(|self, track, _t|Bsp::s(Tui::bold(true, row!( - Tui::fg_bg(if rec { White } else { track.color.light.rgb }, track.color.dark.rgb, "Rcrd"), - Tui::fg_bg(if rec { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), - Tui::fg_bg(if mon { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mntr"), - )), row!( - Tui::fg_bg(if rec { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), - Tui::fg_bg(if rec { White } else { track.color.dark.rgb }, track.color.darker.rgb, "▐"), - Tui::fg_bg(if mon { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), - ))) - } - fn output_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let fg = Tui::g(224); - let bg = Tui::g(64); - (move||Bsp::s(Fill::x(Align::w(self.button(" O ", format!(" midi outs ({}) ", self.midi_outs().len())))), self.midi_outs().get(0).map(|out|Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))), - out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, connect.info()))))), - ))).boxed()).into() - } - fn output_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let mute = false; - let solo = false; - per_track!(|self, track, _t|Bsp::s(Tui::bold(true, row!( - Tui::fg_bg(if mute { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mute"), - Tui::fg_bg(if mute { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), - Tui::fg_bg(if solo { White } else { track.color.light.rgb }, track.color.dark.rgb, "Solo"), - )), row!( - Tui::fg_bg(if mute { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), - Tui::fg_bg(if mute { White } else { track.color.darker.rgb }, track.color.darker.rgb, "▐"), - Tui::fg_bg(if solo { White } else { track.color.light.rgb }, track.color.darker.rgb, "CH**"), - ))) - } - fn track_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let add_track = ||self.button(" C-t ", format!(" add track ({}/{})", - self.selected.track().unwrap_or(0), - self.tracks().len())); - (move||Tui::bg(Tui::g(32), Fill::x(Align::w(add_track()))).boxed()).into() - } - fn track_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - per_track!(|self, track, t|{ - let active = self.selected().track() == Some(t+1); - let name = &track.name; - let fg = track.color.lightest.rgb; - let bg = if active { track.color.light.rgb } else { track.color.base.rgb }; - let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; - let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; - let bs = Style::default().fg(bfg).bg(bg); - let cell = Bsp::e( - Tui::fg_bg(bg, bg2, "▐"), - Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::nw(Bsp::e(" ", name)))))); - Outer(active, bs).enclose(cell) - }) - } fn button <'a> ( &'a self, key: impl Content + 'a, label: impl Content + 'a ) -> impl Content + 'a {