remove 1 more per-cell allocation

This commit is contained in:
🪞👃🪞 2025-01-20 23:12:57 +01:00
parent f7dcc28e1f
commit 93462e7501
2 changed files with 128 additions and 92 deletions

View file

@ -17,7 +17,7 @@ pub use ::tek_sampler::{self, *};
pub use ::tek_plugin::{self, *};
pub use ::tek_tui::{
*, tek_edn::*, tek_input::*, tek_output::*,
ratatui, ratatui::{prelude::{Color, Style, Stylize, Buffer, Modifier}, buffer::Cell},
ratatui, ratatui::{prelude::{Color::{self, *}, Style, Stylize, Buffer, Modifier}, buffer::Cell},
crossterm, crossterm::event::{
Event, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, KeyCode::{self, *},
},
@ -157,6 +157,7 @@ impl TekCli {
pub fmtd_sr: Arc<RwLock<String>>,
pub fmtd_buf: Arc<RwLock<String>>,
pub fmtd_lat: Arc<RwLock<String>>,
pub fmtd_stop: Arc<str>,
}
has_size!(<TuiOut>|self: Tek|&self.size);
has_clock!(|self: Tek|self.clock);
@ -238,6 +239,7 @@ impl Tek {
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);
@ -452,9 +454,9 @@ impl Tek {
else if value >= -25.0 { 3 }
else if value >= -30.0 { 2 }
else if value >= -40.0 { 1 }
else { 0 }, 1, Tui::bg(if value >= 0.0 { Color::Red }
else if value >= -3.0 { Color::Yellow }
else { Color::Green }, ())))
else { 0 }, 1, Tui::bg(if value >= 0.0 { Red }
else if value >= -3.0 { Yellow }
else { Green }, ())))
}
fn view_meters (&self, values: &[f32;2]) -> impl Content<TuiOut> + use<'_> {
col!(
@ -466,14 +468,14 @@ impl Tek {
let playing = self.clock.is_rolling();
let compact = self.is_editing();
Tui::bg(
if playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
if playing{Rgb(0,128,0)}else{Rgb(128,64,0)},
Either::new(compact,
Thunk::new(move||Fixed::x(9, Either::new(playing,
Tui::fg(Color::Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Color::Rgb(255, 128, 0), " STOPPED ")))),
Tui::fg(Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Rgb(255, 128, 0), " STOPPED ")))),
Thunk::new(move||Fixed::x(5, Either::new(playing,
Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
}
fn view_editor (&self) -> impl Content<TuiOut> + use<'_> {
self.editor.as_ref().map(|e|Bsp::e(e.clip_status(), e.edit_status()))
@ -482,8 +484,8 @@ impl Tek {
self.pool.as_ref().map(|pool|PoolView(self.is_editing(), pool))
}
fn pool (&self) -> impl Content<TuiOut> + use<'_> {
self.pool.as_ref()
.map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.is_editing(), pool))))
let by_pool = |pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.is_editing(), pool)));
self.pool.as_ref().map(by_pool)
}
fn w (&self) -> u16 {
self.tracks_sizes(self.is_editing(), self.editor_w())
@ -530,40 +532,65 @@ impl Tek {
move|(_, track, x1, x2), t| {
let last_color = Arc::new(RwLock::new(ItemPalette::default()));
let w = (x2 - x1) as u16;
let color: ItemPalette = track.color;
map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s| {
let last_color = last_color.clone();
let h = (1 + y2 - y1) as u16;
let color = scene.color;
let mut name = String::from("");
let mut fg = Tui::g(64);
let mut bg = ItemPalette::G[32];
if let Some(clip) = &scene.clips[t] {
let clip = clip.read().unwrap();
name.clear();
write!(&mut name, "{}", clip.name);
fg = clip.color.lightest.rgb;
bg = clip.color
};
let same_track = selected_track == Some(t+1);
let selected = same_track && Some(s+1) == selected_scene;
let neighbor = same_track && Some(s) == selected_scene;
let active = editing && selected;
let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.clone())));
let mid = if active { bg.light } else { bg.base };
let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) };
let mid = mid.rgb;
let low = Color::Rgb(0, 0, 0);
let last_color = last_color.clone();
let mut fg = Tui::g(64);
let mut bg = ItemPalette::G[32];
if let Some(clip) = &scene.clips[t] {
let clip = clip.read().unwrap();
fg = clip.color.lightest.rgb;
bg = clip.color
};
//let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) };
let top = if s == 0 {
Some(Reset)
} else if neighbor {
None
} else {
Some(last_color.read().unwrap().base.rgb)
};
let mid = if selected { bg.light } else { bg.base }.rgb;
let low = Some(Reset);
let h = (1 + y2 - y1) as u16;
*last_color.write().unwrap() = bg;
let tab = " Tab ";
let name = if active {
self.editor.as_ref()
.map(|e|e.clip().as_ref().map(|c|c.clone()))
.flatten()
.map(|c|c.read().unwrap().name.clone())
.unwrap_or_else(||"".into())
} else {
"edit".into()
};
let label = move||{
let stop = self.fmtd_stop.clone();
let clip = scene.clips[t].clone();
Tui::fg(fg, Push::x(1, Tui::bold(true, clip
.map(|c|c.read().unwrap().name.clone())
.unwrap_or(stop))))
};
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active,
Thunk::new(||Bsp::a(
Fill::xy(Align::nw(button(" Tab ", ""))),
Thunk::new(move||Bsp::a(
Fill::xy(Align::nw(button(tab, Tui::fg_bg(fg, bg.base.rgb, name.clone())))),
&self.editor)),
Thunk::new(move||Bsp::a(
When::new(selected, Fill::y(Align::n(button(" Tab ", "edit")))),
phat_sel_3(selected, label(), label(), top, mid, low)
When::new(selected, Fill::y(Align::n(button(tab, "edit")))),
Fill::xy(phat_sel_3(
selected,
label(),
label(),
top, mid, low
))
)),
))))
}))).boxed()
}
})).boxed()).into()
@ -679,34 +706,34 @@ handle!(TuiIn: |self: Tek, input|Ok({
Zoom(Option<usize>),
}
atom_command!(TekCommand: |app: Tek| {
("stop-all" [] Self::StopAll)
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
("redo" [d: usize] Self::History(d.unwrap_or(0) as isize))
("zoom" [z: usize] Self::Zoom(z))
("edit" [] Self::Edit(None))
("edit" [c: bool] Self::Edit(c))
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
("stop" [] Self::StopAll)
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
("redo" [d: usize] Self::History(d.unwrap_or(0) as isize))
("zoom" [z: usize] Self::Zoom(z))
("edit" [] Self::Edit(None))
("edit" [c: bool] Self::Edit(c))
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
(0, 0) => Self::Select(Selection::Mix),
(t, 0) => Self::Select(Selection::Track(t)),
(0, s) => Self::Select(Selection::Scene(s)),
(t, s) => Self::Select(Selection::Clip(t, s)),
})
("clip" [,..a] Self::Clip(ClipCommand::try_from_expr(app, a)
.expect("invalid command")))
("clock" [,..a] Self::Clock(ClockCommand::try_from_expr(app.clock(), a)
.expect("invalid command")))
("editor" [,..a] Self::Editor(MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a)
.expect("invalid command")))
("pool" [,..a] Self::Pool(PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a)
.expect("invalid command")))
("sampler" [,..a] Self::Sampler(SamplerCommand::try_from_expr(app.sampler.as_ref().expect("no sampler"), a)
.expect("invalid command")))
("scene" [,..a] Self::Scene(SceneCommand::try_from_expr(app, a)
.expect("invalid command")))
("track" [,..a] Self::Track(TrackCommand::try_from_expr(app, a)
.expect("invalid command")))
("clip" [,..a] Self::Clip(
ClipCommand::try_from_expr(app, a).expect("invalid command")))
("clock" [,..a] Self::Clock(
ClockCommand::try_from_expr(app.clock(), a).expect("invalid command")))
("editor" [,..a] Self::Editor(
MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).expect("invalid command")))
("pool" [,..a] Self::Pool(
PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).expect("invalid command")))
("sampler" [,..a] Self::Sampler(
SamplerCommand::try_from_expr(app.sampler.as_ref().expect("no sampler"), a).expect("invalid command")))
("scene" [,..a] Self::Scene(
SceneCommand::try_from_expr(app, a).expect("invalid command")))
("track" [,..a] Self::Track(
TrackCommand::try_from_expr(app, a).expect("invalid command")))
});
command!(|self: TekCommand, app: Tek|match self {
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
@ -741,7 +768,10 @@ command!(|self: TekCommand, app: Tek|match self {
let (index, mut clip) = pool.add_new_clip();
// autocolor: new clip colors from scene and track color
clip.write().unwrap().color = ItemColor::random_near(
app.tracks[t.saturating_sub(1)].color.base.mix(scene.color.base, 0.5),
app.tracks[t.saturating_sub(1)].color.base.mix(
scene.color.base,
0.5
),
0.2
).into();
if let Some(ref mut editor) = app.editor {
@ -759,7 +789,7 @@ command!(|self: TekCommand, app: Tek|match self {
Self::Clock(cmd) => cmd.delegate(app, Self::Clock)?,
Self::Scene(cmd) => cmd.delegate(app, Self::Scene)?,
Self::Track(cmd) => cmd.delegate(app, Self::Track)?,
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Editor(cmd) => app.editor.as_mut()
.map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(),
Self::Sampler(cmd) => app.sampler.as_mut()
@ -978,7 +1008,7 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
let fg = color.lightest.rgb;
let bg = color.base.rgb;
let active = self.selected().track() == Some(i + 1);
let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) };
let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) };
let border = Style::default().fg(bfg).bg(bg);
Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16,
Outer(false, border)
@ -998,11 +1028,11 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
}
fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content<TuiOut> {
row!(
Tui::fg_bg(if rec { Color::Red } else { bg }, bg, ""),
Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"),
Tui::fg_bg(if rec { Color::White } else { bg }, bg, ""),
Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"),
Tui::fg_bg(if mon { Color::White } else { bg }, bg, ""),
Tui::fg_bg(if rec { Red } else { bg }, bg, ""),
Tui::fg_bg(if rec { White } else { Rgb(0,0,0) }, bg, "REC"),
Tui::fg_bg(if rec { White } else { bg }, bg, ""),
Tui::fg_bg(if mon { White } else { Rgb(0,0,0) }, bg, "MON"),
Tui::fg_bg(if mon { White } else { bg }, bg, ""),
)
}
fn output_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> {
@ -1017,16 +1047,16 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
}
fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content<TuiOut> {
row!(
Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"),
Tui::fg_bg(if mute { Color::White } else { bg }, bg, ""),
Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"),
Tui::fg_bg(if mute { White } else { Rgb(0,0,0) }, bg, "MUT"),
Tui::fg_bg(if mute { White } else { bg }, bg, ""),
Tui::fg_bg(if solo { White } else { Rgb(0,0,0) }, bg, "SOL"),
)
}
fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
}
}
trait Device: Send + Sync + std::fmt::Debug {}
pub trait Device: Send + Sync + std::fmt::Debug {}
impl Device for Sampler {}
impl Device for Plugin {}
#[derive(Debug, Default)] pub struct Scene {
@ -1149,21 +1179,19 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync {
let selected = self.selected().scene();
let iter = ||self.scenes_sizes(self.is_editing(), 2, 15);
Map::new(iter, move|(_, scene, y1, y2), i| {
let h = (1 + y2 - y1) as u16;
let name = format!("🭬{}", &scene.name);
let color = scene.color;
let active = selected == Some(i + 1);
let neighbor = selected == Some(i);
let mid = if active { color.light } else { color.base };
let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) };
let mid = mid.rgb;
let low = Color::Rgb(0, 0, 0);
let color = scene.color;
let top = if i == 0 { Some(Reset) } else if selected == Some(i+0) { None } else { Some(last_color.read().unwrap().base.rgb) };
let mid = if selected == Some(i+1) { color.light } else { color.base }.rgb;
let low = Some(Reset);
let cell = phat_sel_3(
active,
Tui::bold(true, name.clone()),
Tui::bold(true, name),
top, mid, low
selected == Some(i),
Tui::bold(true, Bsp::e("🭬", &scene.name)),
Tui::bold(true, Bsp::e("🭬", &scene.name)),
top,
mid,
low
);
let h = (1 + y2 - y1) as u16;
*last_color.write().unwrap() = color;
map_south(y1 as u16, h, Push::y(1, Fixed::y(h,
Outer(false, Style::default().fg(Tui::g(0))).enclose(cell))))
@ -1275,7 +1303,10 @@ audio!(|self: Tek, client, scope|{
self.perf.update(t0, scope);
Control::Continue
});
fn button (key: &'static str, label: &'static str) -> impl Content<TuiOut> + 'static {
fn button <'a> (
key: impl Content<TuiOut> + 'a,
label: impl Content<TuiOut> + 'a
) -> impl Content<TuiOut> + 'a {
Tui::bold(true, Bsp::e(
Margin::x(1, Tui::fg_bg(Tui::g(0), Tui::orange(), key)),
Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label)),

View file

@ -158,17 +158,22 @@ pub fn phat_cell_3 <T: Content<TuiOut>> (
)
}
pub fn phat_sel_3 <T: Content<TuiOut>> (
selected: bool, field_1: T, field_2: T, top: Option<Color>, middle: Color, bottom: Color
selected: bool, field_1: T, field_2: T,
top: Option<Color>,
mid: Color,
low: Option<Color>,
) -> impl Content<TuiOut> {
let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle);
Either::new(selected,
Tui::bg(middle, Outer(true, border)
.enclose(Align::w(Bsp::s("", Bsp::n("", Fill::y(field_1)))))),
Bsp::s(Fixed::y(1, top.map(|top|phat_lo(middle, top))),
Bsp::n(Fixed::y(1, phat_hi(middle, bottom)),
Fill::xy(Tui::bg(middle, field_2)),
)
)
let border = Style::default().fg(Color::Rgb(255,255,255)).bg(mid);
let top = top.map(|top|phat_lo(mid, top));
let low = low.map(|low|phat_hi(mid, low));
Either::new(
selected,
Tui::bg(mid, Outer(true, border).enclose(
Align::w(Bsp::s("", Bsp::n("", Fill::y(field_1))))
)),
Bsp::s(Fixed::y(1, top), Bsp::n(Fixed::y(1, low),
Fill::xy(Tui::bg(mid, field_2))
)),
)
}