From 8bdd088f7022d0951955523e254b22d678d0a688 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 10 Dec 2024 00:01:34 +0100 Subject: [PATCH] fix 'beheaded' notes in phrase editor --- crates/tek/src/api/phrase.rs | 4 ++ crates/tek/src/tui.rs | 44 +++++------- crates/tek/src/tui/phrase_editor.rs | 101 +++++++++++----------------- 3 files changed, 59 insertions(+), 90 deletions(-) diff --git a/crates/tek/src/api/phrase.rs b/crates/tek/src/api/phrase.rs index 7aacf37b..7125856a 100644 --- a/crates/tek/src/api/phrase.rs +++ b/crates/tek/src/api/phrase.rs @@ -131,6 +131,10 @@ impl Phrase { color: color.unwrap_or_else(ItemColorTriplet::random) } } + pub fn set_length (&mut self, length: usize) { + self.length = length; + self.notes = vec![Vec::with_capacity(16);length]; + } pub fn duplicate (&self) -> Self { let mut clone = self.clone(); clone.uuid = uuid::Uuid::new_v4(); diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 8b80407d..e17be900 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -5,40 +5,24 @@ mod engine_input; pub(crate) use engine_input::*; mod engine_style; pub(crate) use engine_style::*; mod engine_output; pub(crate) use engine_output::*; +//////////////////////////////////////////////////////// + mod app_transport; pub(crate) use app_transport::*; - -mod app_sequencer; pub(crate) use app_sequencer::*; -mod ctrl_sequencer; pub(crate) use ctrl_sequencer::*; - -mod app_arranger; pub(crate) use app_arranger::*; -mod ctrl_arranger; pub(crate) use ctrl_arranger::*; -mod model_arranger; pub(crate) use model_arranger::*; +mod app_sequencer; pub(crate) use app_sequencer::*; +mod app_arranger; pub(crate) use app_arranger::*; //////////////////////////////////////////////////////// -mod view_status_bar; pub(crate) use view_status_bar::*; +mod status_bar; pub(crate) use status_bar::*; +mod file_browser; pub(crate) use file_browser::*; +mod phrase_editor; pub(crate) use phrase_editor::*; +mod phrase_length; pub(crate) use phrase_length::*; +mod phrase_rename; pub(crate) use phrase_rename::*; +mod phrase_list; pub(crate) use phrase_list::*; +mod phrase_player; pub(crate) use phrase_player::*; +mod phrase_select; pub(crate) use phrase_select::*; -mod model_file_browser; pub(crate) use model_file_browser::*; -mod view_file_browser; pub(crate) use view_file_browser::*; -mod ctrl_file_browser; pub(crate) use ctrl_file_browser::*; - -mod model_phrase_editor; pub(crate) use model_phrase_editor::*; -mod view_phrase_editor; pub(crate) use view_phrase_editor::*; -mod ctrl_phrase_editor; pub(crate) use ctrl_phrase_editor::*; - -mod model_phrase_length; pub(crate) use model_phrase_length::*; -mod view_phrase_length; pub(crate) use view_phrase_length::*; -mod ctrl_phrase_length; pub(crate) use ctrl_phrase_length::*; - -mod model_phrase_list; pub(crate) use model_phrase_list::*; -mod view_phrase_list; pub(crate) use view_phrase_list::*; -mod ctrl_phrase_list; pub(crate) use ctrl_phrase_list::*; - -mod model_phrase_player; pub(crate) use model_phrase_player::*; - -mod view_phrase_selector; pub(crate) use view_phrase_selector::*; - -mod ctrl_phrase_rename; pub(crate) use ctrl_phrase_rename::*; +//////////////////////////////////////////////////////// #[macro_export] macro_rules! render { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { @@ -53,6 +37,8 @@ mod ctrl_phrase_rename; pub(crate) use ctrl_phrase_rename::*; } } +//////////////////////////////////////////////////////// + pub struct Tui { pub exited: Arc, pub buffer: Buffer, diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index a3b10c34..2334e78c 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -196,7 +196,6 @@ render!(|self: PhraseView<'a>|{ //now: _, .. } = self; - // is it r6d that `to` is the receiver? let keys = move|to: &mut TuiOutput|{ Ok(if to.area().h() >= 2 { view_mode.render_keys(to, *note_hi, *note_lo) }) }; @@ -214,58 +213,38 @@ render!(|self: PhraseView<'a>|{ ) }) }; - //let playhead = move|_: &mut TuiOutput|{ // will this live or die? - ////let playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(40,50,30)); - ////let playhead_active = playhead_inactive.clone().yellow().bold().not_dim(); - ////if let Some(_) = phrase { - ////let now = now.get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length; - ////let time_clamp = time_clamp; - ////for x in 0..(time_clamp/time_zoom).saturating_sub(*time_start) { - ////let this_step = time_start + (x + 0) * time_zoom; - ////let next_step = time_start + (x + 1) * time_zoom; - ////let x = to.area().x() + x as u16; - ////let active = this_step <= now && now < next_step; - ////let character = if active { "|" } else { "·" }; - ////let style = if active { playhead_active } else { playhead_inactive }; - ////to.blit(&character, x, to.area.y(), Some(style)); - ////} - ////} - //Ok(()) - //}; - let indicators = lay!(move|add|{ - let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; - let upper_left = format!("╭{note_hi} {note_hi_name} {}", - phrase.as_ref().map(|p|p.read().unwrap().name.clone()).unwrap_or(String::new()) - ); - let lower_left = format!("╰{note_lo} {note_lo_name}"); - let mut lower_right = format!(" {} ", size.format()); - if *focused && *entered { - lower_right = format!("Note: {} ({}) {} {lower_right}", - note_point, to_note_name(*note_point), pulses_to_name(*note_len) - ); - } - let mut upper_right = format!("[{}]", if *entered {"■"} else {" "}); - if let Some(phrase) = phrase { - upper_right = format!("Time: {}/{} {} {upper_right}", - time_point, phrase.read().unwrap().length, pulses_to_name(view_mode.time_zoom()), - ) - }; - add(&Tui::at_nw(Tui::fg(title_color, upper_left)))?; - add(&Tui::at_sw(Tui::fg(title_color, lower_left)))?; - add(&Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right)))))?; - add(&Tui::fill_xy(Tui::at_se(Tui::pull_x(1, Tui::fg(title_color, lower_right)))))?; - Ok(()) - }); - let content = Tui::bg(Color::Rgb(40, 50, 30), Tui::fill_x(row!([ - Tui::push_y(1, Tui::fill_y(Widget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys))), - Tui::fill_x(lay!([ - Tui::push_y(1, Tui::fill_x(Widget::new(|to|Ok(Some(to)), notes))), - Tui::push_y(1, Widget::new(|to|Ok(Some(to)), cursor)) - ])), - ]))); lay!([ - indicators, - content + lay!(move|add|{ + let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; + let upper_left = format!("╭{note_hi} {note_hi_name} {}", + phrase.as_ref().map(|p|p.read().unwrap().name.clone()).unwrap_or(String::new()) + ); + let lower_left = format!("╰{note_lo} {note_lo_name}"); + let mut lower_right = format!(" {} ", size.format()); + if *focused && *entered { + lower_right = format!("Note: {} ({}) {} {lower_right}", + note_point, to_note_name(*note_point), pulses_to_name(*note_len) + ); + } + let mut upper_right = format!("[{}]", if *entered {"■"} else {" "}); + if let Some(phrase) = phrase { + upper_right = format!("Time: {}/{} {} {upper_right}", + time_point, phrase.read().unwrap().length, pulses_to_name(view_mode.time_zoom()), + ) + }; + add(&Tui::at_nw(Tui::fg(title_color, upper_left)))?; + add(&Tui::at_sw(Tui::fg(title_color, lower_left)))?; + add(&Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right)))))?; + add(&Tui::fill_xy(Tui::at_se(Tui::pull_x(1, Tui::fg(title_color, lower_right)))))?; + Ok(()) + }), + Tui::bg(Color::Rgb(40, 50, 30), Tui::fill_x(row!([ + Tui::push_y(1, Tui::fill_y(Widget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys))), + Tui::fill_x(lay!([ + Tui::push_y(1, Tui::fill_x(Widget::new(|to|Ok(Some(to)), notes))), + Tui::push_y(1, Widget::new(|to|Ok(Some(to)), cursor)) + ])), + ]))) ]) }); @@ -464,16 +443,16 @@ impl PhraseViewMode { let mut notes_on = [false;128]; for (x, time_start) in (0..phrase.length).step_by(time_zoom).enumerate() { let time_end = time_start + time_zoom; - for time in time_start..time_end { - for (y, note) in (0..127).rev().enumerate() { - let cell = target.get_mut(x, y).unwrap(); - if notes_on[note] { - cell.set_fg(Color::Rgb(255, 255, 255)); - cell.set_bg(Color::Rgb(0, 0, 0)); - cell.set_char('▄'); - cell.set_style(style); - } + for (y, note) in (0..127).rev().enumerate() { + let cell = target.get_mut(x, 127 - note).unwrap(); + if notes_on[note] { + cell.set_fg(Color::Rgb(255, 255, 255)); + cell.set_bg(Color::Rgb(0, 0, 0)); + cell.set_char('▄'); + cell.set_style(style); } + } + for time in time_start..time_end { for event in phrase.notes[time].iter() { match event { MidiMessage::NoteOn { key, .. } => {