wip: fixing memoized arranger rendering

This commit is contained in:
🪞👃🪞 2025-02-15 16:01:36 +00:00
parent ec85224f3a
commit 5b797342b7
11 changed files with 129 additions and 120 deletions

View file

@ -193,11 +193,10 @@ impl Tek {
scenes: vec![],
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
tek.arranger = Default::default();
tek.selected = Selection::Clip(1, 1);
tek.scenes_add(scenes);
tek.tracks_add(tracks, Some(track_width), &[], &[]);
tek.selected = Selection::Clip(1, 1);
tek.arranger = Default::default();
tek.redraw_arranger();
Ok(tek)
}
}

0
tek/src/device.rs Normal file
View file

View file

@ -4,8 +4,6 @@ pub const KEYS_CLIP: &str = include_str!("keys_clip.edn");
pub const KEYS_TRACK: &str = include_str!("keys_track.edn");
pub const KEYS_SCENE: &str = include_str!("keys_scene.edn");
pub const KEYS_MIX: &str = include_str!("keys_mix.edn");
pub struct Keymaps {
}
handle!(TuiIn: |self: Tek, input|Ok({
// If editing, editor keys take priority
if self.is_editing() {

View file

@ -6,6 +6,7 @@ atom_command!(InputCommand: |app: Tek| {
command!(|self: InputCommand, app: Tek|match self {
Self::Add => {
app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?);
app.redraw_arranger();
None
},
});

View file

@ -6,6 +6,7 @@ atom_command!(OutputCommand: |app: Tek| {
command!(|self: OutputCommand, app: Tek|match self {
Self::Add => {
app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?);
app.redraw_arranger();
None
},
});

View file

@ -8,6 +8,7 @@
#![feature(trait_alias)]
mod cli; pub use self::cli::*;
mod audio; pub use self::audio::*;
mod device; pub use self::device::*;
mod keys; pub use self::keys::*;
mod keys_clip; pub use self::keys_clip::*;
mod keys_ins; pub use self::keys_ins::*;

View file

@ -111,77 +111,6 @@ provide!(Selection: |self: Tek| {
},
});
impl Tek {
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<(usize, &mut Scene)>
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: vec![None;self.tracks().len()],
color: color.unwrap_or_else(ItemPalette::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
pub fn scene_default_name (&self) -> Arc<str> {
format!("Sc{:3>}", self.scenes().len() + 1).into()
}
pub fn tracks_add (
&mut self, count: usize, width: Option<usize>,
midi_from: &[PortConnect], midi_to: &[PortConnect],
) -> Usually<()> {
let jack = self.jack().clone();
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let mut track = self.track_add(None, Some(color), midi_from, midi_to)?.1;
if let Some(width) = width {
track.width = width;
}
}
Ok(())
}
pub fn track_add (
&mut self, name: Option<&str>, color: Option<ItemPalette>,
midi_froms: &[PortConnect],
midi_tos: &[PortConnect],
) -> Usually<(usize, &mut Track)> {
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
let mut track = Track {
width: (name.len() + 2).max(12),
color: color.unwrap_or_else(ItemPalette::random),
player: MidiPlayer::new(
&format!("{name}"),
self.jack(),
Some(self.clock()),
None,
midi_froms,
midi_tos
)?,
name,
..Default::default()
};
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.push(None);
}
}
Ok((index, &mut self.tracks_mut()[index]))
}
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
self.scene()?.clips.get(self.selected().track()?)?.clone()
}
@ -190,12 +119,6 @@ impl Tek {
clip.write().unwrap().toggle_loop()
}
}
pub fn track_del (&mut self, index: usize) {
self.tracks_mut().remove(index);
for scene in self.scenes_mut().iter_mut() {
scene.clips.remove(index);
}
}
fn activate (&mut self) -> Usually<()> {
let selected = self.selected().clone();
match selected {

View file

@ -1,4 +1,32 @@
use crate::*;
impl Tek {
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
self.redraw_arranger();
Ok(())
}
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<(usize, &mut Scene)>
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: vec![None;self.tracks().len()],
color: color.unwrap_or_else(ItemPalette::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
pub fn scene_default_name (&self) -> Arc<str> {
format!("Sc{:3>}", self.scenes().len() + 1).into()
}
}
pub trait HasScenes: HasSelection + HasEditor + Send + Sync {
fn scenes (&self) -> &Vec<Scene>;
fn scenes_mut (&mut self) -> &mut Vec<Scene>;

View file

@ -1,4 +1,59 @@
use crate::*;
impl Tek {
pub fn tracks_add (
&mut self, count: usize, width: Option<usize>,
midi_from: &[PortConnect], midi_to: &[PortConnect],
) -> Usually<()> {
let jack = self.jack().clone();
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let mut track = self.track_add(None, Some(color), midi_from, midi_to)?.1;
if let Some(width) = width {
track.width = width;
}
}
self.redraw_arranger();
Ok(())
}
pub fn track_add (
&mut self, name: Option<&str>, color: Option<ItemPalette>,
midi_froms: &[PortConnect],
midi_tos: &[PortConnect],
) -> Usually<(usize, &mut Track)> {
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
let mut track = Track {
width: (name.len() + 2).max(12),
color: color.unwrap_or_else(ItemPalette::random),
player: MidiPlayer::new(
&format!("{name}"),
self.jack(),
Some(self.clock()),
None,
midi_froms,
midi_tos
)?,
name,
..Default::default()
};
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.push(None);
}
}
Ok((index, &mut self.tracks_mut()[index]))
}
pub fn track_del (&mut self, index: usize) {
self.tracks_mut().remove(index);
for scene in self.scenes_mut().iter_mut() {
scene.clips.remove(index);
}
}
}
pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
fn midi_ins (&self) -> &Vec<JackMidiIn>;
fn midi_outs (&self) -> &Vec<JackMidiOut>;

View file

@ -1,3 +1 @@
(bsp/n (fixed/y 1 :transport)
(bsp/s (fixed/y 1 :status)
(fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))
(bsp/n (fixed/y 2 :transport) (bsp/s (fixed/y 2 :status) (fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))

View file

@ -1,21 +1,6 @@
use crate::*;
impl Tek {
/// Draw the full arranger to the arranger view buffer.
///
/// This should happen on changes to the arrangement contents,
/// i.e. not on scroll. Scrolling just determines which
/// part of the arranger buffer to blit in [view_arranger]
pub fn redraw_arranger (&self) {
let width = self.w_tracks();
let height = self.h_scenes() + self.h_inputs() + self.h_outputs();
let buffer = Buffer::empty(ratatui::prelude::Rect { x: 0, y: 0, width, height });
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
Content::render(&Bsp::s(self.view_inputs(), Bsp::s(self.view_tracks(),
Bsp::n(self.view_outputs(), self.view_scenes()))), &mut output);
*self.arranger.write().unwrap() = output.buffer;
}
/// Blit the currently visible section of the arranger view buffer to the output.
///
/// If the arranger is larger than the available display area,
@ -30,12 +15,13 @@ impl Tek {
let source = self.arranger.read().unwrap();
for (source_x, target_x) in (x0..x0+w).enumerate() {
for (source_y, target_y) in (y0..y0+h).enumerate() {
let source_pos = Position::from((source_x as u16, source_y as u16));
let target_pos = Position::from((target_x, target_y));
if let Some(target) = to.buffer.cell_mut(target_pos) {
//target.set_bg(Color::Red);
target.set_bg(Color::Rgb(128,0,0));
let source_pos = Position::from((source_x as u16, source_y as u16));
if let Some(source) = source.cell(source_pos) {
*target = source.clone();
target.set_bg(Color::Rgb(0,128,0));
}
}
}
@ -43,6 +29,24 @@ impl Tek {
}))
}
/// Draw the full arranger to the arranger view buffer.
///
/// This should happen on changes to the arrangement contents,
/// i.e. not on scroll. Scrolling just determines which
/// part of the arranger buffer to blit in [view_arranger].
pub fn redraw_arranger (&self) {
let width = self.w_tracks();
let height = self.h_scenes() + self.h_inputs() + self.h_outputs();
let buffer = Buffer::empty(ratatui::prelude::Rect { x: 0, y: 0, width, height });
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
let layout = Bsp::s(self.view_inputs(),
Bsp::s(self.view_tracks(),
Bsp::n(self.view_outputs(),
self.view_scenes())));
Content::render(&layout, &mut output);
*self.arranger.write().unwrap() = output.buffer;
}
/// Display the current scene scroll state.
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
let offset = self.scene_scroll;
@ -98,26 +102,27 @@ impl Tek {
}
pub fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
let editing = self.is_editing();
let s = self.w_sidebar() as u16;
let w = self.w_tracks_area();
let w_full = self.w();
let h = self.h_scenes();
let h_area = self.h_tracks_area();
let editing = self.is_editing();
let s = self.w_sidebar() as u16;
let w = self.w_tracks_area();
let w_full = self.w();
let h = self.h_scenes();
let h_area = self.h_tracks_area();
let selected_track = self.selected().track();
let selected_scene = self.selected().scene();
Tui::bg(Reset, Bsp::s(self.track_scrollbar(), Bsp::e(self.scene_scrollbar(),
Fixed::y(self.h_tracks_area(), row(self.w_tracks_area(), h, s,
Map::new(
move||self.scenes_with_colors(editing, h_area),
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_name(
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
self.per_track(move|t, track|Map::new(
move||self.scenes_with_track_colors(editing, h_area, t),
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_clip(
w, (1 + y2 - y1) as u16, y1 as u16,
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
() )))))
let track_scroll = self.track_scrollbar();
let scene_scroll = self.scene_scrollbar();
Tui::bg(Reset, Bsp::s(track_scroll, Bsp::e(scene_scroll, Fixed::y(h_area, row(w, h, s,
Map::new(
move||self.scenes_with_colors(editing, h_area),
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_name(
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
self.per_track(move|t, track|Map::new(
move||self.scenes_with_track_colors(editing, h_area, t),
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_clip(
w, (1 + y2 - y1) as u16, y1 as u16,
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
() )))))
}
fn view_scene_name (