From a67032053383b32e6d919e15835c2a834fb57202 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 16 Jan 2025 19:23:56 +0100 Subject: [PATCH] append tracks/scenes + move cursor --- midi/src/piano_h.rs | 23 ++++---- tek/src/lib.rs | 127 +++++++++++++++++++++++++------------------- 2 files changed, 86 insertions(+), 64 deletions(-) diff --git a/midi/src/piano_h.rs b/midi/src/piano_h.rs index 97adcc94..f132c4b8 100644 --- a/midi/src/piano_h.rs +++ b/midi/src/piano_h.rs @@ -60,20 +60,25 @@ impl PianoHorizontal { for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) { let cell = buf.get_mut(x, y).unwrap(); cell.set_bg(clip.color.darkest.rgb); - cell.set_fg(clip.color.darker.rgb); - cell.set_char(if time % 384 == 0 { - '│' + if time % 384 == 0 { + cell.set_fg(clip.color.darker.rgb); + cell.set_char('│'); } else if time % 96 == 0 { - '╎' + cell.set_fg(clip.color.dark.rgb); + cell.set_char('╎'); } else if time % note_len == 0 { - '┊' + cell.set_fg(clip.color.darker.rgb); + cell.set_char('┊'); } else if (127 - note) % 12 == 0 { - '=' + cell.set_fg(clip.color.darker.rgb); + cell.set_char('='); } else if (127 - note) % 6 == 0 { - '—' + cell.set_fg(clip.color.darker.rgb); + cell.set_char('—'); } else { - '·' - }); + cell.set_fg(clip.color.darker.rgb); + cell.set_char('·'); + } } } } diff --git a/tek/src/lib.rs b/tek/src/lib.rs index 5db4fd05..2f0afe61 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -225,9 +225,41 @@ impl Tek { )? }; 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) } + 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::::new(&jack, &format!("{}I", &track.name), midi_from)?; + track.player.midi_ins.push(port); + let port = JackPort::::new(&jack, &format!("{}O", &track.name), midi_to)?; + track.player.midi_outs.push(port); + } + Ok(()) + } fn new_groovebox ( jack: &Arc>, bpm: Option, @@ -300,7 +332,6 @@ impl Tek { // TODO: sync follow Ok(()) } - fn editor (&self) -> impl Content + '_ { &self.editor } fn view_clock (&self) -> impl Content + use<'_> { Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!( self.view_engine_stats(), " ", @@ -422,7 +453,7 @@ impl Tek { &mut self, name: Option<&str>, color: Option - ) -> Usually<&mut Track> { + ) -> Usually<(usize, &mut Track)> { let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into()); let track = Track { width: (name.len() + 2).max(9), @@ -439,28 +470,7 @@ impl Tek { scene.clips.push(None); } } - Ok(&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::::new(&jack, &format!("{}I", &track.name), midi_from)?; - track.player.midi_ins.push(port); - let port = JackPort::::new(&jack, &format!("{}O", &track.name), midi_to)?; - track.player.midi_outs.push(port); - } - Ok(()) + Ok((index, &mut self.tracks_mut()[index])) } fn track_del (&mut self, index: usize) { self.tracks_mut().remove(index); @@ -469,7 +479,7 @@ impl Tek { } } fn scene_add (&mut self, name: Option<&str>, color: Option) - -> Usually<&mut Scene> + -> Usually<(usize, &mut Scene)> { let scene = Scene { 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); let index = self.scenes().len() - 1; - Ok(&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(()) + Ok((index, &mut self.scenes_mut()[index])) } fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> { let editing = self.is_editing(); @@ -511,15 +511,14 @@ impl Tek { ("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32)) }; 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.to_string()))); - map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new( - active, + 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.to_string()))); + 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 ".into(), "".into()))), - self.editor())), + &self.editor)), Thunk::new(move||Bsp::a( When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))), phat_sel_3( @@ -704,13 +703,13 @@ command!(|self: TekCommand, app: Tek|match self { if let Some(ref pool) = app.pool { if app.is_editing() { if let Selection::Clip(t, s) = app.selected { - if let Some(scene) = app.scenes.get_mut(s) { - if let Some(slot) = scene.clips.get_mut(t) { + if let Some(scene) = app.scenes.get_mut(s.saturating_sub(1)) { + if let Some(slot) = scene.clips.get_mut(t.saturating_sub(1)) { if slot.is_none() { 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].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 { @@ -852,8 +851,17 @@ edn_command!(TrackCommand: |app: Tek| { ("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap())) }); command!(|self: TrackCommand, app: Tek|match self { - Self::Add => { app.track_add(None, None)?; None }, - Self::Del(index) => { app.track_del(index); None }, + Self::Add => { + 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::SetColor(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 active = match self.selected() { - Selection::Track(t) if editing => Some(t), - Selection::Clip(t, _) if editing => Some(t), + Selection::Track(t) if editing => Some(t.saturating_sub(1)), + Selection::Clip(t, _) if editing => Some(t.saturating_sub(1)), _ => None }; 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); x += width; data @@ -1014,7 +1022,16 @@ edn_command!(SceneCommand: |app: Tek| { ("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap())) }); 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::SetColor(index, color) => { let old = app.scenes[index].color; @@ -1044,11 +1061,11 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync { { let mut y = 0; 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) }; 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 data = (s, scene, y, y + height); y += height;