From 2401dc8fcdef28e1060997fa89b19d7546f1de77 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 10 Jan 2025 01:32:58 +0100 Subject: [PATCH] instant crash --- cli/tek.rs | 1 + midi/src/midi_editor.rs | 69 ++++---------- midi/src/piano_h.rs | 5 +- tek/src/arranger.rs | 1 + tek/src/arranger/arranger_tui.rs | 151 +++++++++++++++++-------------- 5 files changed, 105 insertions(+), 122 deletions(-) diff --git a/cli/tek.rs b/cli/tek.rs index dda48490..982da3b9 100644 --- a/cli/tek.rs +++ b/cli/tek.rs @@ -184,6 +184,7 @@ pub fn main () -> Usually<()> { midi_buf: vec![vec![];65536], note_buf: vec![], compact: false, + editing: true, color, perf, size, diff --git a/midi/src/midi_editor.rs b/midi/src/midi_editor.rs index c943f3aa..42a59caa 100644 --- a/midi/src/midi_editor.rs +++ b/midi/src/midi_editor.rs @@ -12,42 +12,35 @@ pub trait HasEditor { } } } - /// Contains state for viewing and editing a phrase pub struct MidiEditor { pub mode: PianoHorizontal, pub size: Measure } - from!(|phrase: &Arc>|MidiEditor = { - let mut model = Self::from(Some(phrase.clone())); + let model = Self::from(Some(phrase.clone())); model.redraw(); model }); - from!(|phrase: Option>>|MidiEditor = { let mut model = Self::default(); *model.phrase_mut() = phrase; model.redraw(); model }); - impl Default for MidiEditor { fn default () -> Self { - let mut mode = PianoHorizontal::new(None); + let mode = PianoHorizontal::new(None); mode.redraw(); Self { mode, size: Measure::new() } } } - has_size!(|self: MidiEditor|&self.size); - render!(TuiOut: (self: MidiEditor) => { self.autoscroll(); self.autozoom(); Fill::xy(Bsp::b(&self.size, &self.mode)) }); - impl TimeRange for MidiEditor { fn time_len (&self) -> &AtomicUsize { self.mode.time_len() } fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() } @@ -55,42 +48,27 @@ impl TimeRange for MidiEditor { fn time_start (&self) -> &AtomicUsize { self.mode.time_start() } fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() } } - impl NoteRange for MidiEditor { fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() } fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() } } - impl NotePoint for MidiEditor { - fn note_len (&self) -> usize { self.mode.note_len() } - fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) } - fn note_point (&self) -> usize { self.mode.note_point() } + fn note_len (&self) -> usize { self.mode.note_len() } + fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) } + fn note_point (&self) -> usize { self.mode.note_point() } fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) } } - impl TimePoint for MidiEditor { - fn time_point (&self) -> usize { self.mode.time_point() } + fn time_point (&self) -> usize { self.mode.time_point() } fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) } } - impl MidiViewer for MidiEditor { - fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) { - self.mode.buffer_size(phrase) - } - fn redraw (&self) { - self.mode.redraw() - } - fn phrase (&self) -> &Option>> { - self.mode.phrase() - } - fn phrase_mut (&mut self) -> &mut Option>> { - self.mode.phrase_mut() - } - fn set_phrase (&mut self, phrase: Option<&Arc>>) { - self.mode.set_phrase(phrase) - } + fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) { self.mode.buffer_size(phrase) } + fn redraw (&self) { self.mode.redraw() } + fn phrase (&self) -> &Option>> { self.mode.phrase() } + fn phrase_mut (&mut self) -> &mut Option>> { self.mode.phrase_mut() } + fn set_phrase (&mut self, p: Option<&Arc>>) { self.mode.set_phrase(p) } } - impl MidiEditor { /// Put note at current position pub fn put_note (&mut self, advance: bool) { @@ -136,27 +114,23 @@ impl MidiEditor { } pub fn edit_status (&self) -> impl Content + '_ { - let (color, name, length, looped) = if let Some(phrase) = self.phrase().as_ref().map(|p|p.read().unwrap()) { - (phrase.color, phrase.name.clone(), phrase.length, phrase.looped) + let (color, length) = if let Some(phrase) = self.phrase().as_ref().map(|p|p.read().unwrap()) { + (phrase.color, phrase.length) } else { - (ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false) + (ItemPalette::from(TuiTheme::g(64)), 0) }; let time_point = self.time_point(); - let time_start = self.time_start(); - let time_end = self.time_end(); - let time_axis = self.time_axis().get(); let time_zoom = self.time_zoom().get(); let time_lock = if self.time_lock().get() { "[lock]" } else { " " }; - let time_field = FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")); - let note_point = format!("{:>3}", self.note_point()); let note_name = format!("{:4}", Note::pitch_to_name(self.note_point())); - let note_len = format!("{:>4}", self.note_len());;;; - let note_field = FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")); - Bsp::e(time_field, note_field,) + let note_len = format!("{:>4}", self.note_len()); + Bsp::e( + FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")), + FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")), + ) } } - impl std::fmt::Debug for MidiEditor { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("MidiEditor") @@ -164,7 +138,6 @@ impl std::fmt::Debug for MidiEditor { .finish() } } - #[derive(Clone, Debug)] pub enum MidiEditCommand { // TODO: 1-9 seek markers that by default start every 8th of the phrase @@ -179,9 +152,7 @@ pub enum MidiEditCommand { SetTimeLock(bool), Show(Option>>), } - handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event())); - keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand { key(Up) => SetNoteCursor(s.note_point() + 1), key(Char('w')) => SetNoteCursor(s.note_point() + 1), @@ -217,13 +188,11 @@ keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand { //// TODO: kpat!(Char('/')) => // toggle 3plet //// TODO: kpat!(Char('?')) => // toggle dotted }); - impl MidiEditor { fn phrase_length (&self) -> usize { self.phrase().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1) } } - impl Command for MidiEditCommand { fn execute (self, state: &mut MidiEditor) -> Perhaps { use MidiEditCommand::*; diff --git a/midi/src/piano_h.rs b/midi/src/piano_h.rs index 20bbc00f..f3786b5b 100644 --- a/midi/src/piano_h.rs +++ b/midi/src/piano_h.rs @@ -1,11 +1,9 @@ use crate::*; use super::*; - pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iterator { (note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n)) } - -render!(TuiOut: (self: PianoHorizontal) => Bsp::s( +render!(TuiOut: (self: PianoHorizontal) => Bsp::s( // the freeze is in the piano Fixed::y(1, Bsp::e( Fixed::x(self.keys_width, ""), Fill::x(PianoHorizontalTimeline(self)), @@ -18,7 +16,6 @@ render!(TuiOut: (self: PianoHorizontal) => Bsp::s( ))), )), )); - impl PianoHorizontal { /// Draw the piano roll foreground using full blocks on note on and half blocks on legato: █▄ █▄ █▄ fn draw_bg (buf: &mut BigBuffer, phrase: &MidiClip, zoom: usize, note_len: usize) { diff --git a/tek/src/arranger.rs b/tek/src/arranger.rs index 1ecba120..b85158c6 100644 --- a/tek/src/arranger.rs +++ b/tek/src/arranger.rs @@ -21,6 +21,7 @@ pub struct Arranger { pub note_buf: Vec, pub midi_buf: Vec>>, pub editor: MidiEditor, + pub editing: bool, pub perf: PerfModel, pub compact: bool, } diff --git a/tek/src/arranger/arranger_tui.rs b/tek/src/arranger/arranger_tui.rs index 43b3c198..56118c5d 100644 --- a/tek/src/arranger/arranger_tui.rs +++ b/tek/src/arranger/arranger_tui.rs @@ -31,55 +31,6 @@ impl Arranger { pub const LEFT_SEP: char = '▎'; pub const TRACK_MIN_WIDTH: usize = 4; - /// A 1-row cell. - fn cell > (color: ItemPalette, field: T) -> impl Content { - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) - } - - /// A phat line - fn phat_lo (fg: Color, bg: Color) -> impl Content { - Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▄"))) - } - - fn phat_hi (fg: Color, bg: Color) -> impl Content { - Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▀"))) - } - - /// A cell that is 3-row on its own, but stacks, giving (N+1)*2 rows per N cells. - fn phat_cell > ( - color: ItemPalette, last: ItemPalette, field: T - ) -> impl Content { - Bsp::s(Self::phat_lo(color.base.rgb, last.base.rgb), - Bsp::n(Self::phat_hi(color.base.rgb, last.base.rgb), - Fixed::y(1, Fill::x(Tui::fg_bg(color.lightest.rgb, color.base.rgb, field))), - ) - ) - } - - fn phat_cell_3 > ( - field: T, top: Color, middle: Color, bottom: Color - ) -> impl Content { - Bsp::s(Self::phat_lo(middle, top), - Bsp::n(Self::phat_hi(middle, bottom), - Fixed::y(1, Fill::x(Tui::bg(middle, field))), - ) - ) - } - - fn phat_sel_3 > ( - selected: bool, field_1: T, field_2: T, top: Option, middle: Color, bottom: Color - ) -> impl Content { - let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle); - Either(selected, - Tui::bg(middle, Outer(border).enclose( Align::w(Bsp::s("", Bsp::s(field_1, ""))))), - Bsp::s( - Fixed::y(1, top.map(|top|Self::phat_lo(middle, top))), - Bsp::n(Self::phat_hi(middle, bottom), - Fixed::y(1, Fill::x(Tui::bg(middle, field_2))), - ) - )) - } - fn output_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { let fg = TuiTheme::g(192); let bg = TuiTheme::g(48); @@ -90,7 +41,7 @@ impl Arranger { (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { let w = (x2 - x1) as u16; let color: ItemPalette = track.color().dark.into(); - let cell = Bsp::s(format!(" M S "), Self::phat_hi(color.dark.rgb, color.darker.rgb)); + let cell = Bsp::s(format!(" M S "), phat_hi(color.dark.rgb, color.darker.rgb)); map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) })).boxed()).into() } @@ -111,7 +62,7 @@ impl Arranger { } else { String::new() }); - let cell = Bsp::s(value, Self::phat_hi(color.dark.rgb, color.darker.rgb)); + let cell = Bsp::s(value, phat_hi(color.dark.rgb, color.darker.rgb)); Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) })).boxed()).into() } @@ -125,7 +76,7 @@ impl Arranger { let color: ItemPalette = track.color().dark.into(); let until_next = Self::cell(color, Tui::bold(true, Self::cell_until_next(track, &self.clock().playhead))); let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, until_next); - let cell = Bsp::s(value, Self::phat_hi(color.dark.rgb, color.darker.rgb)); + let cell = Bsp::s(value, phat_hi(color.dark.rgb, color.darker.rgb)); Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) })).boxed()).into() } @@ -140,7 +91,7 @@ impl Arranger { let name = Push::x(1, &track.name); Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, Tui::fg_bg(color.lightest.rgb, color.base.rgb, - Self::phat_cell(color, color.darkest.rgb.into(), + phat_cell(color, color.darkest.rgb.into(), Tui::bold(true, name))))) })).boxed()).into() } @@ -170,7 +121,7 @@ impl Arranger { let h = (y2 - y1) as u16; let name = format!("🭬{}", &scene.name); let color = scene.color(); - let cell = Self::phat_sel_3( + let cell = phat_sel_3( selected_scene == Some(i), Push::x(1, Tui::bold(true, name.clone())), Push::x(1, Tui::bold(true, name)), @@ -193,7 +144,8 @@ impl Arranger { }).into() } fn scene_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + let editing = self.editing; + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), t| { let w = (x2 - x1) as u16; let cell = Bsp::s("[Rec]", "[Mon]"); let color: ItemPalette = track.color().dark.into(); @@ -204,21 +156,38 @@ impl Arranger { }; map_east(x1 as u16, w, Fixed::x(w, Tui::bg(Color::Rgb(0,0,0), Fill::y(Map::new( ||self.scenes_with_heights(2), - move|(_, scene, y1, y2), j| { + move|(_, scene, y1, y2), s| { let h = (y2 - y1) as u16; let color = scene.color(); let name = "⏹ "; - let cell = Self::phat_sel_3( - selected_track == Some(i) && selected_scene == Some(j), - Tui::fg(TuiTheme::g(64), Push::x(1, name)), - Tui::fg(TuiTheme::g(64), Push::x(1, name)), - if selected_track == Some(i) && selected_scene.map(|s|s+1) == Some(j) { - None - } else { - Some(TuiTheme::g(32).into()) - }, - TuiTheme::g(32).into(), - TuiTheme::g(32).into(), + let last = last_color.read().unwrap().clone(); + //let cell = phat_sel_3( + //selected_track == Some(i) && selected_scene == Some(j), + //Tui::fg(TuiTheme::g(64), Push::x(1, name)), + //Tui::fg(TuiTheme::g(64), Push::x(1, name)), + //if selected_track == Some(i) && selected_scene.map(|s|s+1) == Some(j) { + //None + //} else { + //Some(TuiTheme::g(32).into()) + //}, + //TuiTheme::g(32).into(), + //TuiTheme::g(32).into(), + //); + let active = editing && selected_track == Some(t) && selected_scene == Some(s); + let cell = Either(active, + Thunk::new(||&self.editor), + Thunk::new(move||phat_sel_3( + selected_track == Some(t) && selected_scene == Some(s), + Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), + Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), + if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) { + None + } else { + Some(TuiTheme::g(32).into()) + }, + TuiTheme::g(32).into(), + TuiTheme::g(32).into(), + )) ); map_south(y1 as u16, 3, Fill::x(cell)) } @@ -283,7 +252,8 @@ impl Arranger { Map::new(||self.scenes_with_heights(1), move|(_, scene, y1, y2), i| { let h = (y2 - y1) as u16; let color = scene.color(); - let cell = Fixed::y(h, Fixed::x(scenes_w, Self::cell(color, scene.name.clone()))); + let cell = Self::cell(color, scene.name.clone()); + let cell = Fixed::y(h, Fixed::x(scenes_w, cell)); map_south(y1 as u16, 1, cell) }) } @@ -439,6 +409,51 @@ impl Arranger { }; }) } + + /// A 1-row cell. + fn cell > (color: ItemPalette, field: T) -> impl Content { + Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) + } +} +/// A phat line +fn phat_lo (fg: Color, bg: Color) -> impl Content { + Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▄"))) +} +/// A phat line +fn phat_hi (fg: Color, bg: Color) -> impl Content { + Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▀"))) +} +/// A cell that is 3-row on its own, but stacks, giving (N+1)*2 rows per N cells. +fn phat_cell > ( + color: ItemPalette, last: ItemPalette, field: T +) -> impl Content { + Bsp::s(phat_lo(color.base.rgb, last.base.rgb), + Bsp::n(phat_hi(color.base.rgb, last.base.rgb), + Fixed::y(1, Fill::x(Tui::fg_bg(color.lightest.rgb, color.base.rgb, field))), + ) + ) +} +fn phat_cell_3 > ( + field: T, top: Color, middle: Color, bottom: Color +) -> impl Content { + Bsp::s(phat_lo(middle, top), + Bsp::n(phat_hi(middle, bottom), + Fixed::y(1, Fill::x(Tui::bg(middle, field))), + ) + ) +} +fn phat_sel_3 > ( + selected: bool, field_1: T, field_2: T, top: Option, middle: Color, bottom: Color +) -> impl Content { + let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle); + Either(selected, + Tui::bg(middle, Outer(border).enclose( Align::w(Bsp::s("", Bsp::s(field_1, ""))))), + Bsp::s( + Fixed::y(1, top.map(|top|phat_lo(middle, top))), + Bsp::n(phat_hi(middle, bottom), + Fixed::y(1, Fill::x(Tui::bg(middle, field_2))), + ) + )) } //pub struct ArrangerVCursor {