From 9cd9131d5dc81e87fc1dbd37a66b8b4a1aec0915 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 22 Oct 2024 01:10:35 +0300 Subject: [PATCH] add align_s/n; position playhead --- crates/tek_core/src/space.rs | 2 + crates/tek_sequencer/src/arranger_tui.rs | 86 +++++++++++------------ crates/tek_sequencer/src/sequencer.rs | 23 +++--- crates/tek_sequencer/src/sequencer_tui.rs | 70 +++++++----------- crates/tek_sequencer/src/transport_snd.rs | 2 +- 5 files changed, 79 insertions(+), 104 deletions(-) diff --git a/crates/tek_core/src/space.rs b/crates/tek_core/src/space.rs index 3dffb68c..b36beefa 100644 --- a/crates/tek_core/src/space.rs +++ b/crates/tek_core/src/space.rs @@ -141,6 +141,8 @@ pub trait Layout: Widget + Sized { fn align_ne (self) -> Align { Align::NE(self) } fn align_e (self) -> Align { Align::E(self) } fn align_se (self) -> Align { Align::SE(self) } + fn align_n (self) -> Align { Align::N(self) } + fn align_s (self) -> Align { Align::S(self) } fn align_x (self) -> Align { Align::X(self) } fn align_y (self) -> Align { Align::Y(self) } fn fixed_x (self, x: E::Unit) -> Fixed { Fixed::X(x, self) } diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index 7eb01741..d910c978 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -43,42 +43,45 @@ impl Content for ArrangerStatusBar { ["", "<>", "resize view"], ]), Self::ArrangementClip => command(&[ - ["", "g", "et"], - ["", "s", "et"], - ["", "a", "dd"], - ["", "i", "ns"], - ["", "d", "up"], - ["", "e", "dit"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["", ",.", "select"], + ["", "g", "et"], + ["", "s", "et"], + ["", "a", "dd"], + ["", "i", "ns"], + ["", "d", "up"], + ["", "e", "dit"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["", ",.", "select"], ]), Self::ArrangementTrack => command(&[ ["", ",.", "resize"], ["", "<>", "move"], - ["", "i", "nput"], - ["", "o", "utput"], - ["", "m", "ute"], - ["", "s", "olo"], + ["", "i", "nput"], + ["", "o", "utput"], + ["", "m", "ute"], + ["", "s", "olo"], ]), Self::PhrasePool => command(&[ - ["", "a", "ppend"], - ["", "i", "nsert"], - ["", "d", "uplicate"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["leng", "t", "h"], - ["", ",.", "move"], - ["", "<>", "resize view"], + ["", "a", "ppend"], + ["", "i", "nsert"], + ["", "d", "uplicate"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["leng", "t", "h"], + ["", ",.", "move"], + ["", "<>", "resize view"], ]), Self::PhraseView => command(&[ - ["", "enter", " edit"], + ["", "enter", " edit"], ["", "arrows/pgup/pgdn", " scroll"], + ["", ",.", "zoom"], ]), Self::PhraseEdit => command(&[ ["", "esc", " exit"], - ["", "a", "ppend"], - ["", "s", "et"], + ["", "a", "ppend"], + ["", "s", "et"], + ["", ",.", "length"], + ["", "<>", "zoom"], ]), _ => command(&[]) }; @@ -101,20 +104,11 @@ impl Content for Arrangement { fn content (&self) -> impl Widget { Layers::new(move |add|{ match self.mode { - ArrangementViewMode::Horizontal => add(&HorizontalArranger(&self)), - ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)) + ArrangementViewMode::Horizontal => add(&HorizontalArranger(&self)), + ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)), }?; let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; - //if self.focused { - //let commands = "[G]et [S]et [A]dd [I]nsert [D]uplicate [E]dit [C]olor"; - //let lower_left = Align::SW(TuiStyle::fg(commands, color).push_x(1)); - //add(&lower_left)?; - //} - //let description = self.selected.description(&self.tracks, &self.scenes); - //let lower_right = Align::SE(TuiStyle::fg(description.as_str(), color).pull_x(1)); - //add(&lower_right)?; - let upper_left = TuiStyle::fg("Session", color).push_x(1); - add(&upper_left) + add(&TuiStyle::fg("Session", color).push_x(1)) }) } } @@ -122,22 +116,22 @@ impl<'a> Content for VerticalArranger<'a, Tui> { type Engine = Tui; fn content (&self) -> impl Widget { let Self(state, factor) = self; - let (cols, rows) = if *factor == 0 {( - state.track_widths(), Scene::ppqs(state.scenes.as_slice()), - )} else {( - state.track_widths(), - (0..=state.scenes.len()).map(|i|(factor*PPQ, factor*PPQ*i)).collect::>(), - )}; + let cols = state.track_widths(); + let rows = if *factor == 0 { + Scene::ppqs(state.scenes.as_slice()) + } else { + (0..=state.scenes.len()).map(|i|(factor*PPQ, factor*PPQ*i)).collect::>() + }; let tracks: &[ArrangementTrack] = state.tracks.as_ref(); let scenes: &[Scene] = state.scenes.as_ref(); - let offset = 3 + Scene::longest_name(scenes) as u16; // x of 1st track - let bg = state.color; - let clip_bg = Color::Rgb(40, 50, 30); + let offset = 3 + Scene::longest_name(scenes) as u16; // x of 1st track + let bg = state.color; + let clip_bg = Color::Rgb(40, 50, 30); let border_bg = Color::Rgb(40, 50, 30); let border_hi = Color::Rgb(100, 110, 40); let border_lo = Color::Rgb(70, 80, 50); let border_fg = if self.0.focused { border_hi } else { border_lo }; - let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); + let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); Layers::new(move |add|{ let rows: &[(usize, usize)] = rows.as_ref(); let cols: &[(usize, usize)] = cols.as_ref(); diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index f722e372..dca6e3f4 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -170,27 +170,22 @@ impl PhrasePool { } return None } + fn new_phrase (name: Option<&str>, color: Option) -> Arc> { + Arc::new(RwLock::new(Phrase::new( + String::from(name.unwrap_or("(new)")), true, 4 * PPQ, None, color + ))) + } pub fn append_new (&mut self, name: Option<&str>, color: Option) { - let mut phrase = Phrase::default(); - phrase.name = String::from(name.unwrap_or("(new)")); - phrase.color = color.unwrap_or_else(random_color); - phrase.length = 4 * PPQ; - phrase.notes = vec![Vec::with_capacity(16);phrase.length]; - self.phrases.push(Arc::new(RwLock::new(phrase))); + self.phrases.push(Self::new_phrase(name, color)); self.phrase = self.phrases.len() - 1; } pub fn insert_new (&mut self, name: Option<&str>, color: Option) { - let mut phrase = Phrase::default(); - phrase.name = String::from(name.unwrap_or("(new)")); - phrase.color = color.unwrap_or_else(random_color); - phrase.length = 4 * PPQ; - phrase.notes = vec![Vec::with_capacity(16);phrase.length]; - self.phrases.insert(self.phrase + 1, Arc::new(RwLock::new(phrase))); + self.phrases.insert(self.phrase + 1, Self::new_phrase(name, color)); self.phrase += 1; } pub fn insert_dup (&mut self) { let mut phrase = self.phrases[self.phrase].read().unwrap().duplicate(); - phrase.color = random_color_near(phrase.color, 0.2); + phrase.color = random_color_near(phrase.color, 0.25); self.phrases.insert(self.phrase + 1, Arc::new(RwLock::new(phrase))); self.phrase += 1; } @@ -234,7 +229,7 @@ impl PhraseEditor { notes_out: Arc::new(RwLock::new([false;128])), keys: keys_vert(), buffer: Default::default(), - note_axis: FixedAxis { start: 12, point: Some(36) }, + note_axis: FixedAxis { start: 12, point: Some(36) }, time_axis: ScaledAxis { start: 0, scale: 24, point: Some(0) }, focused: false, entered: false, diff --git a/crates/tek_sequencer/src/sequencer_tui.rs b/crates/tek_sequencer/src/sequencer_tui.rs index 07ba9cb7..6bd548ee 100644 --- a/crates/tek_sequencer/src/sequencer_tui.rs +++ b/crates/tek_sequencer/src/sequencer_tui.rs @@ -43,16 +43,7 @@ impl Content for PhrasePool { let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)}; let title = format!("Phrases ({})", phrases.len()); let title = TuiStyle::fg(title, title_color).push_x(1); - Layers::new(move|add|{ - add(&content)?; - add(&title)?; - //if self.focused { - //let commands = "[A]ppend [I]nsert [D]uplicate [C]olor re[N]ame leng[T]h [,.] Move"; - //let lower_left = Align::SW(TuiStyle::fg(commands, title_color).push_x(1)); - //add(&lower_left)?; - //} - Ok(()) - }) + Layers::new(move|add|{ add(&content)?; add(&title)?; Ok(()) }) } } impl Content for PhraseEditor { @@ -75,26 +66,6 @@ impl Content for PhraseEditor { } Ok(()) }).fill_y(); - let playhead = CustomWidget::new(|_|Ok(Some([32u16,2u16])), move|to: &mut TuiOutput|{ - if let Some(_) = phrase { - let time_0 = time_axis.start; - let time_z = time_axis.scale; - let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; - let [x, y, width, _] = to.area(); - let x2 = x as usize + Self::H_KEYS_OFFSET; - let x3 = x as usize + width as usize; - for x in x2..x3 { - let step = (time_0 + x2) * time_z; - let next_step = (time_0 + x2 + 1) * time_z; - let mut style = Style::default(); - if step <= now && now < next_step { - style = style.yellow().bold().not_dim() - } - to.blit(&"-", x as u16, y, Some(style)); - } - } - Ok(()) - }).fill_x(); let notes = CustomWidget::new(|_|Ok(Some([32u16,4u16])), move|to: &mut TuiOutput|{ if to.area().h() >= 2 && to.area().w() >= offset { let area = to.area().push_x(offset).shrink_x(offset); @@ -124,32 +95,45 @@ impl Content for PhraseEditor { } Ok(()) }); + let playhead = CustomWidget::new(|_|Ok(Some([32u16,2u16])), move|to: &mut TuiOutput|{ + if let Some(_) = phrase { + let time_0 = time_axis.start; + let time_z = time_axis.scale; + let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; + let [x, y, width, _] = to.area(); + let x2 = x as usize + Self::H_KEYS_OFFSET; + let x3 = x as usize + width as usize; + for x in x2..x3 { + let step = (time_0 + x2) * time_z; + let next_step = (time_0 + x2 + 1) * time_z; + let mut style = Style::default().fg(Color::Rgb(255,255,255)); + if step <= now && now < next_step { + style = style.yellow().bold().not_dim() + } + to.blit(&"-", x as u16, y, Some(style)); + } + } + Ok(()) + }).align_sw().fill_xy().push_xy(1, 1); let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); - let piano_roll = lay!(keys, playhead, notes, cursor).fill_x(); + let piano_roll = lay!(keys, notes, cursor).fill_x(); let content_bg = Color::Rgb(40, 50, 30); let content = piano_roll.bg(content_bg).border(border); let mut upper_left = String::from("Sequencer"); - let mut lower_left = String::new(); - let mut lower_right = format!("Zoom: {}", ppq_to_name(time_axis.scale)); if let Some(phrase) = phrase { upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name); } - if *focused { - if *entered { - //lower_left = "[Esc] Exit edit mode [A]ppend [S]et".to_string(); - lower_right = format!("[,.] Note: {} [<>] {lower_right}", ppq_to_name(*note_len)); - } else { - //lower_left = "[Enter] Edit notes".to_string(); - lower_right = format!("[,.] {lower_right}"); - } + let mut upper_right = format!("Zoom: {}", ppq_to_name(time_axis.scale)); + if *focused && *entered { + upper_right = format!("Note: {} {upper_right}", ppq_to_name(*note_len)); } lay!( content, + playhead, TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(), - TuiStyle::fg(lower_left.to_string(), title_color).push_x(1).align_sw().fill_xy(), - TuiStyle::fg(lower_right.to_string(), title_color).pull_x(1).align_se().fill_xy(), + TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(), ) } } diff --git a/crates/tek_sequencer/src/transport_snd.rs b/crates/tek_sequencer/src/transport_snd.rs index cf84a422..12b1ed30 100644 --- a/crates/tek_sequencer/src/transport_snd.rs +++ b/crates/tek_sequencer/src/transport_snd.rs @@ -1,7 +1,7 @@ use crate::*; impl Audio for TransportToolbar { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - self.update(&scope); + let _ = self.update(&scope); Control::Continue } }