append tracks/scenes + move cursor

This commit is contained in:
🪞👃🪞 2025-01-16 19:23:56 +01:00
parent 5bf1bad7be
commit a670320533
2 changed files with 86 additions and 64 deletions

View file

@ -60,20 +60,25 @@ impl PianoHorizontal {
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) { for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
let cell = buf.get_mut(x, y).unwrap(); let cell = buf.get_mut(x, y).unwrap();
cell.set_bg(clip.color.darkest.rgb); cell.set_bg(clip.color.darkest.rgb);
cell.set_fg(clip.color.darker.rgb); if time % 384 == 0 {
cell.set_char(if time % 384 == 0 { cell.set_fg(clip.color.darker.rgb);
'│' cell.set_char('│');
} else if time % 96 == 0 { } else if time % 96 == 0 {
'╎' cell.set_fg(clip.color.dark.rgb);
cell.set_char('╎');
} else if time % note_len == 0 { } else if time % note_len == 0 {
'┊' cell.set_fg(clip.color.darker.rgb);
cell.set_char('┊');
} else if (127 - note) % 12 == 0 { } else if (127 - note) % 12 == 0 {
'=' cell.set_fg(clip.color.darker.rgb);
cell.set_char('=');
} else if (127 - note) % 6 == 0 { } else if (127 - note) % 6 == 0 {
'—' cell.set_fg(clip.color.darker.rgb);
cell.set_char('—');
} else { } else {
'·' cell.set_fg(clip.color.darker.rgb);
}); cell.set_char('·');
}
} }
} }
} }

View file

@ -225,9 +225,41 @@ impl Tek {
)? )?
}; };
arranger.scenes_add(scenes); arranger.scenes_add(scenes);
arranger.tracks_add(tracks, track_width, &[], &[]); arranger.tracks_add(tracks, track_width, midi_froms, midi_tos);
arranger.selected = Selection::Clip(1, 1);
Ok(arranger) Ok(arranger)
} }
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(())
}
fn tracks_add (
&mut self,
count: usize,
width: usize,
midi_from: &[PortConnection],
midi_to: &[PortConnection],
) -> 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))?.1;
track.width = width;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), midi_from)?;
track.player.midi_ins.push(port);
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), midi_to)?;
track.player.midi_outs.push(port);
}
Ok(())
}
fn new_groovebox ( fn new_groovebox (
jack: &Arc<RwLock<JackConnection>>, jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>, bpm: Option<f64>,
@ -300,7 +332,6 @@ impl Tek {
// TODO: sync follow // TODO: sync follow
Ok(()) Ok(())
} }
fn editor (&self) -> impl Content<TuiOut> + '_ { &self.editor }
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> { fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!( Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!(
self.view_engine_stats(), " ", self.view_engine_stats(), " ",
@ -422,7 +453,7 @@ impl Tek {
&mut self, &mut self,
name: Option<&str>, name: Option<&str>,
color: Option<ItemPalette> color: Option<ItemPalette>
) -> Usually<&mut Track> { ) -> Usually<(usize, &mut Track)> {
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into()); let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
let track = Track { let track = Track {
width: (name.len() + 2).max(9), width: (name.len() + 2).max(9),
@ -439,28 +470,7 @@ impl Tek {
scene.clips.push(None); scene.clips.push(None);
} }
} }
Ok(&mut self.tracks_mut()[index]) Ok((index, &mut self.tracks_mut()[index]))
}
fn tracks_add (
&mut self,
count: usize,
width: usize,
midi_from: &[PortConnection],
midi_to: &[PortConnection],
) -> 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))?;
track.width = width;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), midi_from)?;
track.player.midi_ins.push(port);
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), midi_to)?;
track.player.midi_outs.push(port);
}
Ok(())
} }
fn track_del (&mut self, index: usize) { fn track_del (&mut self, index: usize) {
self.tracks_mut().remove(index); self.tracks_mut().remove(index);
@ -469,7 +479,7 @@ impl Tek {
} }
} }
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>) fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut Scene> -> Usually<(usize, &mut Scene)>
{ {
let scene = Scene { let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()), name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
@ -478,17 +488,7 @@ impl Tek {
}; };
self.scenes_mut().push(scene); self.scenes_mut().push(scene);
let index = self.scenes().len() - 1; let index = self.scenes().len() - 1;
Ok(&mut self.scenes_mut()[index]) Ok((index, &mut self.scenes_mut()[index]))
}
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 _scene = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
} }
fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> { fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let editing = self.is_editing(); let editing = self.is_editing();
@ -511,15 +511,14 @@ impl Tek {
("".to_string(), TuiTheme::g(64), TuiTheme::g(32)) ("".to_string(), TuiTheme::g(64), TuiTheme::g(32))
}; };
let same_track = selected_track == Some(t+1); let same_track = selected_track == Some(t+1);
let selected = same_track && Some(s+1) == selected_scene; let selected = same_track && Some(s+1) == selected_scene;
let neighbor = same_track && Some(s) == selected_scene; let neighbor = same_track && Some(s) == selected_scene;
let active = editing && selected; let active = editing && selected;
let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))); let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string())));
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new( map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active,
active,
Thunk::new(||Bsp::a( Thunk::new(||Bsp::a(
Fill::xy(Align::nw(button(" Tab ".into(), "".into()))), Fill::xy(Align::nw(button(" Tab ".into(), "".into()))),
self.editor())), &self.editor)),
Thunk::new(move||Bsp::a( Thunk::new(move||Bsp::a(
When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))), When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))),
phat_sel_3( phat_sel_3(
@ -704,13 +703,13 @@ command!(|self: TekCommand, app: Tek|match self {
if let Some(ref pool) = app.pool { if let Some(ref pool) = app.pool {
if app.is_editing() { if app.is_editing() {
if let Selection::Clip(t, s) = app.selected { if let Selection::Clip(t, s) = app.selected {
if let Some(scene) = app.scenes.get_mut(s) { if let Some(scene) = app.scenes.get_mut(s.saturating_sub(1)) {
if let Some(slot) = scene.clips.get_mut(t) { if let Some(slot) = scene.clips.get_mut(t.saturating_sub(1)) {
if slot.is_none() { if slot.is_none() {
let (index, mut clip) = pool.add_new_clip(); let (index, mut clip) = pool.add_new_clip();
// autocolor: new clip colors from scene and track color // autocolor: new clip colors from scene and track color
clip.write().unwrap().color = ItemColor::random_near( clip.write().unwrap().color = ItemColor::random_near(
app.tracks[t].color.base.mix(scene.color.base, 0.5), app.tracks[t.saturating_sub(1)].color.base.mix(scene.color.base, 0.5),
0.2 0.2
).into(); ).into();
if let Some(ref mut editor) = app.editor { if let Some(ref mut editor) = app.editor {
@ -852,8 +851,17 @@ edn_command!(TrackCommand: |app: Tek| {
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap())) ("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
}); });
command!(|self: TrackCommand, app: Tek|match self { command!(|self: TrackCommand, app: Tek|match self {
Self::Add => { app.track_add(None, None)?; None }, Self::Add => {
Self::Del(index) => { app.track_del(index); None }, use Selection::*;
let index = app.track_add(None, None)?.0 + 1;
app.selected = match app.selected {
Track(t) => Track(index),
Clip(t, s) => Clip(index, s),
_ => app.selected
};
Some(Self::Del(index))
},
Self::Del(index) => { app.track_del(index); None },
Self::Stop(track) => { app.tracks[track].player.enqueue_next(None); None }, Self::Stop(track) => { app.tracks[track].player.enqueue_next(None); None },
Self::SetColor(index, color) => { Self::SetColor(index, color) => {
let old = app.tracks[index].color; let old = app.tracks[index].color;
@ -881,12 +889,12 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
{ {
let mut x = 0; let mut x = 0;
let active = match self.selected() { let active = match self.selected() {
Selection::Track(t) if editing => Some(t), Selection::Track(t) if editing => Some(t.saturating_sub(1)),
Selection::Clip(t, _) if editing => Some(t), Selection::Clip(t, _) if editing => Some(t.saturating_sub(1)),
_ => None _ => None
}; };
self.tracks().iter().enumerate().map(move |(index, track)|{ self.tracks().iter().enumerate().map(move |(index, track)|{
let width = if Some(&index) == active { bigger } else { track.width.max(8) }; let width = if Some(index) == active { bigger } else { track.width.max(8) };
let data = (index, track, x, x + width); let data = (index, track, x, x + width);
x += width; x += width;
data data
@ -1014,7 +1022,16 @@ edn_command!(SceneCommand: |app: Tek| {
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap())) ("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
}); });
command!(|self: SceneCommand, app: Tek|match self { command!(|self: SceneCommand, app: Tek|match self {
Self::Add => { app.scene_add(None, None)?; None } Self::Add => {
use Selection::*;
let index = app.scene_add(None, None)?.0 + 1;
app.selected = match app.selected {
Scene(s) => Scene(index),
Clip(t, s) => Clip(t, index),
_ => app.selected
};
Some(Self::Del(index))
},
Self::Del(index) => { app.scene_del(index); None }, Self::Del(index) => { app.scene_del(index); None },
Self::SetColor(index, color) => { Self::SetColor(index, color) => {
let old = app.scenes[index].color; let old = app.scenes[index].color;
@ -1044,11 +1061,11 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync {
{ {
let mut y = 0; let mut y = 0;
let (selected_track, selected_scene) = match self.selected() { let (selected_track, selected_scene) = match self.selected() {
Selection::Clip(t, s) => (Some(t), Some(s)), Selection::Clip(t, s) => (Some(t.saturating_sub(1)), Some(s.saturating_sub(1))),
_ => (None, None) _ => (None, None)
}; };
self.scenes().iter().enumerate().map(move|(s, scene)|{ self.scenes().iter().enumerate().map(move|(s, scene)|{
let active = editing && selected_track.is_some() && selected_scene == Some(&s); let active = editing && selected_track.is_some() && selected_scene == Some(s);
let height = if active { larger } else { height }; let height = if active { larger } else { height };
let data = (s, scene, y, y + height); let data = (s, scene, y, y + height);
y += height; y += height;