mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: fixing memoized arranger rendering
This commit is contained in:
parent
ec85224f3a
commit
5b797342b7
11 changed files with 129 additions and 120 deletions
|
|
@ -193,11 +193,10 @@ impl Tek {
|
||||||
scenes: vec![],
|
scenes: vec![],
|
||||||
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
|
..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.scenes_add(scenes);
|
||||||
tek.tracks_add(tracks, Some(track_width), &[], &[]);
|
tek.tracks_add(tracks, Some(track_width), &[], &[]);
|
||||||
tek.selected = Selection::Clip(1, 1);
|
|
||||||
tek.arranger = Default::default();
|
|
||||||
tek.redraw_arranger();
|
|
||||||
Ok(tek)
|
Ok(tek)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
tek/src/device.rs
Normal file
0
tek/src/device.rs
Normal 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_TRACK: &str = include_str!("keys_track.edn");
|
||||||
pub const KEYS_SCENE: &str = include_str!("keys_scene.edn");
|
pub const KEYS_SCENE: &str = include_str!("keys_scene.edn");
|
||||||
pub const KEYS_MIX: &str = include_str!("keys_mix.edn");
|
pub const KEYS_MIX: &str = include_str!("keys_mix.edn");
|
||||||
pub struct Keymaps {
|
|
||||||
}
|
|
||||||
handle!(TuiIn: |self: Tek, input|Ok({
|
handle!(TuiIn: |self: Tek, input|Ok({
|
||||||
// If editing, editor keys take priority
|
// If editing, editor keys take priority
|
||||||
if self.is_editing() {
|
if self.is_editing() {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ atom_command!(InputCommand: |app: Tek| {
|
||||||
command!(|self: InputCommand, app: Tek|match self {
|
command!(|self: InputCommand, app: Tek|match self {
|
||||||
Self::Add => {
|
Self::Add => {
|
||||||
app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?);
|
app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?);
|
||||||
|
app.redraw_arranger();
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ atom_command!(OutputCommand: |app: Tek| {
|
||||||
command!(|self: OutputCommand, app: Tek|match self {
|
command!(|self: OutputCommand, app: Tek|match self {
|
||||||
Self::Add => {
|
Self::Add => {
|
||||||
app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?);
|
app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?);
|
||||||
|
app.redraw_arranger();
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
mod cli; pub use self::cli::*;
|
mod cli; pub use self::cli::*;
|
||||||
mod audio; pub use self::audio::*;
|
mod audio; pub use self::audio::*;
|
||||||
|
mod device; pub use self::device::*;
|
||||||
mod keys; pub use self::keys::*;
|
mod keys; pub use self::keys::*;
|
||||||
mod keys_clip; pub use self::keys_clip::*;
|
mod keys_clip; pub use self::keys_clip::*;
|
||||||
mod keys_ins; pub use self::keys_ins::*;
|
mod keys_ins; pub use self::keys_ins::*;
|
||||||
|
|
|
||||||
|
|
@ -111,77 +111,6 @@ provide!(Selection: |self: Tek| {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
impl 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>>> {
|
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
||||||
}
|
}
|
||||||
|
|
@ -190,12 +119,6 @@ impl Tek {
|
||||||
clip.write().unwrap().toggle_loop()
|
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<()> {
|
fn activate (&mut self) -> Usually<()> {
|
||||||
let selected = self.selected().clone();
|
let selected = self.selected().clone();
|
||||||
match selected {
|
match selected {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,32 @@
|
||||||
use crate::*;
|
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 {
|
pub trait HasScenes: HasSelection + HasEditor + Send + Sync {
|
||||||
fn scenes (&self) -> &Vec<Scene>;
|
fn scenes (&self) -> &Vec<Scene>;
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,59 @@
|
||||||
use crate::*;
|
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 {
|
pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
|
||||||
fn midi_ins (&self) -> &Vec<JackMidiIn>;
|
fn midi_ins (&self) -> &Vec<JackMidiIn>;
|
||||||
fn midi_outs (&self) -> &Vec<JackMidiOut>;
|
fn midi_outs (&self) -> &Vec<JackMidiOut>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
(bsp/n (fixed/y 1 :transport)
|
(bsp/n (fixed/y 2 :transport) (bsp/s (fixed/y 2 :status) (fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))
|
||||||
(bsp/s (fixed/y 1 :status)
|
|
||||||
(fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Tek {
|
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.
|
/// Blit the currently visible section of the arranger view buffer to the output.
|
||||||
///
|
///
|
||||||
/// If the arranger is larger than the available display area,
|
/// If the arranger is larger than the available display area,
|
||||||
|
|
@ -30,12 +15,13 @@ impl Tek {
|
||||||
let source = self.arranger.read().unwrap();
|
let source = self.arranger.read().unwrap();
|
||||||
for (source_x, target_x) in (x0..x0+w).enumerate() {
|
for (source_x, target_x) in (x0..x0+w).enumerate() {
|
||||||
for (source_y, target_y) in (y0..y0+h).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));
|
let target_pos = Position::from((target_x, target_y));
|
||||||
if let Some(target) = to.buffer.cell_mut(target_pos) {
|
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) {
|
if let Some(source) = source.cell(source_pos) {
|
||||||
*target = source.clone();
|
*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.
|
/// Display the current scene scroll state.
|
||||||
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
|
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let offset = self.scene_scroll;
|
let offset = self.scene_scroll;
|
||||||
|
|
@ -98,26 +102,27 @@ impl Tek {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let editing = self.is_editing();
|
let editing = self.is_editing();
|
||||||
let s = self.w_sidebar() as u16;
|
let s = self.w_sidebar() as u16;
|
||||||
let w = self.w_tracks_area();
|
let w = self.w_tracks_area();
|
||||||
let w_full = self.w();
|
let w_full = self.w();
|
||||||
let h = self.h_scenes();
|
let h = self.h_scenes();
|
||||||
let h_area = self.h_tracks_area();
|
let h_area = self.h_tracks_area();
|
||||||
let selected_track = self.selected().track();
|
let selected_track = self.selected().track();
|
||||||
let selected_scene = self.selected().scene();
|
let selected_scene = self.selected().scene();
|
||||||
Tui::bg(Reset, Bsp::s(self.track_scrollbar(), Bsp::e(self.scene_scrollbar(),
|
let track_scroll = self.track_scrollbar();
|
||||||
Fixed::y(self.h_tracks_area(), row(self.w_tracks_area(), h, s,
|
let scene_scroll = self.scene_scrollbar();
|
||||||
Map::new(
|
Tui::bg(Reset, Bsp::s(track_scroll, Bsp::e(scene_scroll, Fixed::y(h_area, row(w, h, s,
|
||||||
move||self.scenes_with_colors(editing, h_area),
|
Map::new(
|
||||||
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_name(
|
move||self.scenes_with_colors(editing, h_area),
|
||||||
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
|
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_name(
|
||||||
self.per_track(move|t, track|Map::new(
|
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
|
||||||
move||self.scenes_with_track_colors(editing, h_area, t),
|
self.per_track(move|t, track|Map::new(
|
||||||
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_clip(
|
move||self.scenes_with_track_colors(editing, h_area, t),
|
||||||
w, (1 + y2 - y1) as u16, y1 as u16,
|
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_clip(
|
||||||
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
|
w, (1 + y2 - y1) as u16, y1 as u16,
|
||||||
() )))))
|
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
|
||||||
|
() )))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view_scene_name (
|
fn view_scene_name (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue