diff --git a/Justfile b/Justfile index 07b49870..19f8e663 100644 --- a/Justfile +++ b/Justfile @@ -17,18 +17,26 @@ ftpush: git push --tags -fu codeberg git push --tags -fu origin transport: + reset cargo run --bin tek_transport arranger: + reset cargo run --bin tek_arranger sequencer: + reset cargo run --bin tek_sequencer sequencer-release: + reset cargo run --release --bin tek_sequencer mixer: + reset cargo run --bin tek_mixer track: + reset cargo run --bin tek_track sampler: + reset cargo run --bin tek_sampler plugin: + reset cargo run --bin tek_plugin diff --git a/crates/tek/src/layout.rs b/crates/tek/src/layout.rs index 6b4f538f..c3007b8e 100644 --- a/crates/tek/src/layout.rs +++ b/crates/tek/src/layout.rs @@ -1,7 +1,6 @@ mod align; pub(crate) use align::*; mod bsp; pub(crate) use bsp::*; mod cond; pub(crate) use cond::*; -mod debug; pub(crate) use debug::*; mod fill; pub(crate) use fill::*; mod fixed; pub(crate) use fixed::*; mod inset_outset; pub(crate) use inset_outset::*; diff --git a/crates/tek/src/layout/debug.rs b/crates/tek/src/layout/debug.rs deleted file mode 100644 index 6de7cbce..00000000 --- a/crates/tek/src/layout/debug.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::*; - -impl> LayoutDebug for W {} - -pub trait LayoutDebug: Render + Sized { - fn debug (self) -> DebugOverlay { - DebugOverlay(Default::default(), self) - } -} - -pub struct DebugOverlay>(PhantomData, pub W); diff --git a/crates/tek/src/layout/measure.rs b/crates/tek/src/layout/measure.rs index 798366f8..12cd39a3 100644 --- a/crates/tek/src/layout/measure.rs +++ b/crates/tek/src/layout/measure.rs @@ -1,5 +1,15 @@ use crate::*; +impl LayoutDebug for E {} + +pub trait LayoutDebug { + fn debug > (other: W) -> DebugOverlay { + DebugOverlay(Default::default(), other) + } +} + +pub struct DebugOverlay>(PhantomData, pub W); + /// A widget that tracks its render width and height #[derive(Default)] pub struct Measure { @@ -57,13 +67,12 @@ impl Render for Measure { impl Measure { pub fn debug (&self) -> ShowMeasure { - let measure: Measure = (*self).clone(); - ShowMeasure(measure) + ShowMeasure(&self) } } -pub struct ShowMeasure(Measure); -render!(|self: ShowMeasure|render(|to|Ok({ +pub struct ShowMeasure<'a>(&'a Measure); +render!(|self: ShowMeasure<'a>|render(|to|Ok({ let w = self.0.w(); let h = self.0.h(); to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some( diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 778bd549..1860b848 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -1228,6 +1228,7 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option Cmd::Editor(PhraseCommand::Show(Some( state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone() ))), + // WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor _ => match state.focused() { ArrangerFocus::Transport(_) => { match to_transport_command(state, input)? { diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 224c22cf..7508c363 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -196,14 +196,14 @@ render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1, col!([ Tui::fixed_y(2, TransportView::from(( self, - self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten(), + self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone(), if let SequencerFocus::Transport(_) = self.focus { true } else { false } ))), - //self.editor + Tui::fill_xy(&self.editor) ]), ) )])); diff --git a/crates/tek/src/tui/engine_input.rs b/crates/tek/src/tui/engine_input.rs index 3ac34da0..2d6f5686 100644 --- a/crates/tek/src/tui/engine_input.rs +++ b/crates/tek/src/tui/engine_input.rs @@ -28,20 +28,39 @@ impl Input for TuiInput { } } +//#[macro_export] macro_rules! key_pat { +//} +//#[macro_export] macro_rules! key_expr { +//} + /// Define key pattern in key match statement #[macro_export] macro_rules! key { + (Ctrl-Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, + modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT, + kind: KeyEventKind::Press, + state: KeyEventState::NONE + })) }; + (Ctrl-Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, + modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT, + kind: KeyEventKind::Press, + state: KeyEventState::NONE + })) }; + (Ctrl-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE })) }; + (Ctrl-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE })) }; + (Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE })) }; (Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE })) }; + (Shift-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::SHIFT, kind: KeyEventKind::Press, state: KeyEventState::NONE })) }; diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index 9314d37b..0af662bd 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -54,21 +54,23 @@ impl InputToCommand for PhraseCommand { key!(Char('>')) => SetNoteLength(next_note_length(note_len)), // TODO: '/' set triplet, '?' set dotted _ => match from.event() { - key!(Up) => SetNoteCursor(note_point + 1), - key!(Down) => SetNoteCursor(note_point.saturating_sub(1)), - key!(PageUp) => SetNoteCursor(note_point + 3), - key!(PageDown) => SetNoteCursor(note_point.saturating_sub(3)), - key!(Left) => SetTimeCursor(time_point.saturating_sub(note_len)), - key!(Right) => SetTimeCursor((time_point + note_len) % length), - key!(Shift-Left) => SetTimeCursor(time_point.saturating_sub(time_zoom)), - key!(Shift-Right) => SetTimeCursor((time_point + time_zoom) % length), + key!(Up) => SetNoteCursor(note_point + 1), + key!(Down) => SetNoteCursor(note_point.saturating_sub(1)), + key!(Left) => SetTimeCursor(time_point.saturating_sub(note_len)), + key!(Right) => SetTimeCursor((time_point + note_len) % length), + key!(Alt-Up) => SetNoteCursor(note_point + 3), + key!(Alt-Down) => SetNoteCursor(note_point.saturating_sub(3)), + key!(Alt-Left) => SetTimeCursor(time_point.saturating_sub(time_zoom)), + key!(Alt-Right) => SetTimeCursor((time_point + time_zoom) % length), - key!(Ctrl-Up) => SetNoteScroll(note_lo + 1), - key!(Ctrl-Down) => SetNoteScroll(note_lo.saturating_sub(1)), - key!(Ctrl-PageUp) => SetNoteScroll(note_point + 3), - key!(Ctrl-PageDown) => SetNoteScroll(note_point.saturating_sub(3)), - key!(Ctrl-Left) => SetTimeScroll(time_start.saturating_sub(note_len)), - key!(Ctrl-Right) => SetTimeScroll(time_start + note_len), + key!(Ctrl-Up) => SetNoteScroll(note_lo + 1), + key!(Ctrl-Down) => SetNoteScroll(note_lo.saturating_sub(1)), + key!(Ctrl-Left) => SetTimeScroll(time_start.saturating_sub(note_len)), + key!(Ctrl-Right) => SetTimeScroll(time_start + note_len), + key!(Ctrl-Alt-Up) => SetNoteScroll(note_point + 3), + key!(Ctrl-Alt-Down) => SetNoteScroll(note_point.saturating_sub(3)), + key!(Ctrl-Alt-Left) => SetTimeScroll(time_point.saturating_sub(time_zoom)), + key!(Ctrl-Alt-Right) => SetTimeScroll((time_point + time_zoom) % length), _ => return None }, }) @@ -151,6 +153,9 @@ impl PhraseViewMode for PhraseEditorModel { fn phrase (&self) -> &Arc>>>> { self.mode.phrase() } + fn set_phrase (&mut self, phrase: Option>>) { + self.mode.set_phrase(phrase) + } } #[derive(Debug, Clone)] @@ -194,12 +199,24 @@ impl PhraseEditorRange { pub fn set_time_lock (&self, x: bool) { self.time_lock.store(x, Relaxed); } + pub fn time_start (&self) -> usize { + self.time_start.load(Relaxed) + } pub fn set_time_start (&self, x: usize) { self.time_start.store(x, Relaxed); } pub fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); } + pub fn note_lo (&self) -> usize { + self.note_lo.load(Relaxed) + } + pub fn note_axis (&self) -> usize { + self.note_lo.load(Relaxed) + } + pub fn note_hi (&self) -> usize { + self.note_lo() + self.note_axis() + } } #[derive(Debug, Clone)] @@ -228,6 +245,9 @@ impl PhraseEditorPoint { pub fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) } + pub fn note_point (&self) -> usize { + self.note_point.load(Relaxed) + } pub fn time_point (&self) -> usize { self.time_point.load(Relaxed) } diff --git a/crates/tek/src/tui/piano_horizontal.rs b/crates/tek/src/tui/piano_horizontal.rs index 09ba70b2..6b7a019a 100644 --- a/crates/tek/src/tui/piano_horizontal.rs +++ b/crates/tek/src/tui/piano_horizontal.rs @@ -4,6 +4,7 @@ use super::*; /// A phrase, rendered as a horizontal piano roll. pub struct PianoHorizontal { phrase: Arc>>>>, + /// Buffer where the whole phrase is rerendered on change buffer: BigBuffer, /// Width and height of notes area at last render size: Measure, @@ -11,6 +12,8 @@ pub struct PianoHorizontal { range: PhraseEditorRange, /// The note cursor point: PhraseEditorPoint, + /// The highlight color palette + color: ItemPalette, } impl PianoHorizontal { @@ -19,56 +22,69 @@ impl PianoHorizontal { let mut range = PhraseEditorRange::default(); range.time_axis = size.x.clone(); range.note_axis = size.y.clone(); + let phrase = phrase.clone(); + let color = phrase.read().unwrap().as_ref() + .map(|p|p.read().unwrap().color) + .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); Self { buffer: Default::default(), - phrase: phrase.clone(), point: PhraseEditorPoint::default(), - range, size, + range, + phrase, + color } } } render!(|self: PianoHorizontal|{ - let bg = TuiTheme::g(32); - let fg = self.phrase().read().unwrap() - .as_ref().map(|p|p.read().unwrap().color) - .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); + let bg = TuiTheme::g(32); + let fg = self.color; + let note_lo = self.range.note_lo(); + let note_hi = self.range.note_hi(); + let time_lock = self.range.time_lock(); + let time_start = self.range.time_start(); + let time_zoom = self.range.time_zoom(); + let time_point = self.point.time_point(); + let note_point = self.point.note_point(); + let note_len = self.point.note_len(); Tui::bg(bg, Tui::split_down(false, 1, - Tui::bg(fg.dark.rgb, PianoHorizontalTimeline { - start: "|0".into() - }), - Split::right(false, 5, PianoHorizontalKeys { - color: ItemPalette::random(), - note_lo: 0, - note_hi: 0, - note_point: None - }, lay!([ - //self.size, + Tui::debug(Tui::fill_x(Tui::push_x(5, Tui::bg(fg.darkest.rgb, Tui::fg(fg.lightest.rgb, + PianoHorizontalTimeline { + time_start, + time_zoom, + } + ))))), + Tui::fill_xy(Split::right(true, 5, Tui::debug(lay!([ + self.size, PianoHorizontalNotes { - source: &self.buffer, - time_start: 0, - note_hi: 0, + source: &self.buffer, + time_start, + note_hi, }, PianoHorizontalCursor { - time_zoom: 0, - time_point: 0, - time_start: 0, - note_point: 0, - note_len: 0, - note_hi: 0, - note_lo: 0, + time_zoom, + time_point, + time_start, + note_point: note_point, + note_len: note_len, + note_hi: note_hi, + note_lo: note_lo, }, - ])), + ])), PianoHorizontalKeys { + color: self.color, + note_lo, + note_hi, + note_point: Some(note_point), + })) )) }); pub struct PianoHorizontalTimeline { - start: String + time_start: usize, + time_zoom: usize, } -render!(|self: PianoHorizontalTimeline|{ - Tui::fg(TuiTheme::g(224), Tui::push_x(5, self.start.as_str())) -}); +render!(|self: PianoHorizontalTimeline|format!("{}*{}", self.time_start, self.time_zoom).as_str()); pub struct PianoHorizontalKeys { color: ItemPalette, @@ -253,6 +269,13 @@ impl PhraseViewMode for PianoHorizontal { }; self.buffer = buffer } + fn set_phrase (&mut self, phrase: Option>>) { + *self.phrase().write().unwrap() = phrase; + self.color = self.phrase.read().unwrap().as_ref() + .map(|p|p.read().unwrap().color) + .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); + self.redraw(); + } } impl std::fmt::Debug for PianoHorizontal { @@ -263,38 +286,6 @@ impl std::fmt::Debug for PianoHorizontal { .finish() } } - - //fn render_cursor ( - //&self, - //to: &mut TuiOutput, - //time_point: usize, - //time_start: usize, - //note_point: usize, - //note_len: usize, - //note_hi: usize, - //note_lo: usize, - //) { - //let time_zoom = self.time_zoom; - //let [x0, y0, w, h] = to.area().xywh(); - //let style = Some(Style::default().fg(Color::Rgb(0,255,0))); - //for (y, note) in (note_lo..=note_hi).rev().enumerate() { - //if note == note_point { - //for x in 0..w { - //let time_1 = time_start + x as usize * time_zoom; - //let time_2 = time_1 + time_zoom; - //if time_1 <= time_point && time_point < time_2 { - //to.blit(&"█", x0 + x as u16, y0 + y as u16, style); - //let tail = note_len as u16 / time_zoom as u16; - //for x_tail in (x0 + x + 1)..(x0 + x + tail) { - //to.blit(&"▂", x_tail, y0 + y as u16, style); - //} - //break - //} - //} - //break - //} - //} - //} // Update sequencer playhead indicator //self.now().set(0.); //if let Some((ref started_at, Some(ref playing))) = self.player.play_phrase {