diff --git a/crates/tek/src/api/note.rs b/crates/tek/src/api/note.rs index e39d0024..3429cb77 100644 --- a/crates/tek/src/api/note.rs +++ b/crates/tek/src/api/note.rs @@ -56,7 +56,9 @@ pub trait MidiRange { fn note_lo (&self) -> usize; fn set_note_lo (&self, x: usize); fn note_axis (&self) -> usize; + fn time_axis (&self) -> usize; fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() } + fn time_end (&self) -> usize { self.time_start() + self.time_axis() * self.time_zoom() } } impl MidiRange for MidiRangeModel { fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) } @@ -68,6 +70,7 @@ impl MidiRange for MidiRangeModel { fn note_lo (&self) -> usize { self.note_lo.load(Relaxed) } fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); } fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) } + fn time_axis (&self) -> usize { self.time_axis.load(Relaxed) } } #[derive(Debug, Clone)] diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 2be7c65a..16b75cd8 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -173,7 +173,7 @@ audio!(|self:SequencerTui,client,scope|{ Control::Continue }); -render!(|self: SequencerTui|lay!([self.size, Tui::split_n(false, 4, +render!(|self: SequencerTui|lay!([self.size, Tui::split_n(false, 5, Tui::fill_xy(col!([ PhraseEditStatus(&self.editor), SequencerStatusBar::from(self), diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index bb238e35..7c4b70a5 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -117,8 +117,8 @@ render!(|self: TransportView|{ Tui::bg(self.bg, Tui::fill_x(row!([ //PlayPause(self.started), " ", col!([ - Field("Beat", self.beat.as_str()), - Field("BPM ", self.bpm.as_str()), + Field(" Beat", self.beat.as_str()), + Field(" BPM ", self.bpm.as_str()), ]), " ", col!([ diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index 40bd74ab..2263c001 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -141,7 +141,8 @@ impl MidiRange for PhraseEditorModel { fn set_time_start (&self, x: usize) { self.mode.set_time_start(x); } fn set_note_lo (&self, x: usize) { self.mode.set_note_lo(x); } fn note_lo (&self) -> usize { self.mode.note_lo() } - fn note_axis (&self) -> usize { self.mode.note_lo() } + fn note_axis (&self) -> usize { self.mode.note_axis() } + fn time_axis (&self) -> usize { self.mode.time_axis() } fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() } } impl MidiPoint for PhraseEditorModel { @@ -232,24 +233,29 @@ render!(|self:PhraseEditStatus<'a>|row!(|add|{ } else { (ItemPalette::from(TuiTheme::g(64)), String::new(), 0) }; - let bg = color.base.rgb; + let bg = color.darker.rgb; let fg = color.lightest.rgb; - let mut field = |name:&str, value:String|add(&Tui::fixed_xy(8, 2, - Tui::bg(bg, Tui::fg(fg, col!([ - name, Tui::bold(true, &value) - ]))))); - - field("Edit", format!("{name}"))?; - field("Length", format!("{length}"))?; - field("TimePt", format!("{}", self.0.time_point()))?; - field("TimeZoom", format!("{}", self.0.time_zoom()))?; - field("TimeLock", format!("{}", self.0.time_lock()))?; - field("TimeStrt", format!("{}", self.0.time_start()))?; - field("NoteLen", format!("{}", self.0.note_len()))?; - field("NoteLo", format!("{}", self.0.note_lo()))?; - field("NoteAxis", format!("{}", self.0.note_axis()))?; - field("NoteHi", format!("{}", self.0.note_hi()))?; - field("NotePt", format!("{}", self.0.note_point()))?; - - Ok(()) + add(&Tui::fill_x(Tui::bg(bg, row!(|add|{ + add(&Tui::fixed_xy(16, 3, col!(![ + row!(![" Edit ", Tui::bold(true, format!("{name}"))]), + row!(![" Length ", Tui::bold(true, format!("{length}"))]), + row!(![" Loop ", Tui::bold(true, format!("on"))]), + ])))?; + add(&Tui::fixed_xy(12, 3, col!(![ + row!(!["Time ", Tui::bold(true, format!("{}", self.0.time_point()))]), + row!(!["Note ", Tui::bold(true, format!("{}", self.0.note_point()))]), + row!(!["Len ", Tui::bold(true, format!("{}", self.0.note_len()))]), + ])))?; + add(&Tui::fixed_xy(20, 3, col!(![ + row!(!["TimeRange ", Tui::bold(true, format!("{}-{}", self.0.time_start(), self.0.time_end()))]), + row!(!["TimeAxis ", Tui::bold(true, format!("{}", self.0.time_axis()))]), + row!(!["TimeZoom ", Tui::bold(true, format!("{} {}", self.0.time_zoom(), self.0.time_lock()))]), + ])))?; + add(&Tui::fixed_xy(20, 3, col!(![ + row!(!["NoteRange", Tui::bold(true, format!("{}-{}", self.0.note_lo(), self.0.note_hi()))]), + row!(!["NoteAxis ", Tui::bold(true, format!("{}", self.0.note_axis()))]), + "" + ])))?; + Ok(()) + })))) })); diff --git a/crates/tek/src/tui/piano_horizontal.rs b/crates/tek/src/tui/piano_horizontal.rs index 891d5c7e..ff048a0b 100644 --- a/crates/tek/src/tui/piano_horizontal.rs +++ b/crates/tek/src/tui/piano_horizontal.rs @@ -48,36 +48,38 @@ render!(|self: PianoHorizontal|{ 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_s(false, 1, - 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, + lay!([ + &self.size, + Tui::fill_xy(Tui::bg(bg, Tui::split_s(false, 1, + Tui::fill_x(Tui::push_x(5, Tui::bg(fg.darkest.rgb, Tui::fg(fg.lightest.rgb, + PianoHorizontalTimeline { + time_start, + time_zoom, + } + )))), + Tui::split_e(true, 5, Tui::debug(lay!([ + PianoHorizontalNotes { + source: &self.buffer, + time_start, + note_hi, + }, + PianoHorizontalCursor { + 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, - }, - PianoHorizontalCursor { - 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), - })) - )) + note_point: Some(note_point), + }), + ))) + ]) }); pub struct PianoHorizontalTimeline { @@ -252,7 +254,8 @@ impl MidiRange for PianoHorizontal { fn set_time_start (&self, x: usize) { self.range.set_time_start(x); } fn set_note_lo (&self, x: usize) { self.range.set_note_lo(x); } fn note_lo (&self) -> usize { self.range.note_lo() } - fn note_axis (&self) -> usize { self.range.note_lo() } + fn note_axis (&self) -> usize { self.range.note_axis() } + fn time_axis (&self) -> usize { self.range.time_axis() } fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() } } impl MidiPoint for PianoHorizontal { diff --git a/crates/tek/src/tui/status_bar.rs b/crates/tek/src/tui/status_bar.rs index cc8846ad..b3af3c5b 100644 --- a/crates/tek/src/tui/status_bar.rs +++ b/crates/tek/src/tui/status_bar.rs @@ -26,121 +26,3 @@ pub trait StatusBar: Render { Tui::to_north(state.into(), content) } } - -/// Status bar for sequencer app -#[derive(Clone)] -pub struct SequencerStatusBar { - pub(crate) width: usize, - pub(crate) cpu: Option, - pub(crate) size: String, - pub(crate) res: String, - pub(crate) mode: &'static str, - pub(crate) help: &'static [(&'static str, &'static str, &'static str)] -} - -impl StatusBar for SequencerStatusBar { - type State = SequencerTui; - fn hotkey_fg () -> Color { - TuiTheme::HOTKEY_FG - } - fn update (&mut self, _: &SequencerTui) { - todo!() - } -} - -impl From<&SequencerTui> for SequencerStatusBar { - fn from (state: &SequencerTui) -> Self { - let samples = state.clock.chunk.load(Ordering::Relaxed); - let rate = state.clock.timebase.sr.get() as f64; - let buffer = samples as f64 / rate; - let width = state.size.w(); - Self { - width, - cpu: state.perf.percentage().map(|cpu|format!("│{cpu:.01}%")), - size: format!("{}x{}│", width, state.size.h()), - res: format!("│{}s│{:.1}kHz│{:.1}ms│", samples, rate / 1000., buffer * 1000.), - mode: " SEQUENCER ", - help: &[ - ("", "SPACE", " play/pause"), - ("", "✣", " cursor"), - ("", "Ctrl-✣", " scroll"), - ("", ".,", " length"), - ("", "><", " triplet"), - ("", "[]", " phrase"), - ("", "{}", " order"), - ("en", "q", "ueue"), - ("", "e", "dit"), - ("", "a", "dd note"), - ("", "A", "dd phrase"), - ("", "D", "uplicate phrase"), - ] - } - } -} - -render!(|self: SequencerStatusBar|{ - lay!(|add|if self.width > 40 { - add(&Tui::fill_x(Tui::fixed_y(1, lay!([ - Tui::fill_x(Tui::at_w(SequencerMode::from(self))), - Tui::fill_x(Tui::at_e(SequencerStats::from(self))), - ])))) - } else { - add(&Tui::fill_x(col!(![ - Tui::fill_x(Tui::center_x(SequencerMode::from(self))), - Tui::fill_x(Tui::center_x(SequencerStats::from(self))), - ]))) - }) -}); - -struct SequencerMode { - mode: &'static str, - help: &'static [(&'static str, &'static str, &'static str)] -} -impl From<&SequencerStatusBar> for SequencerMode { - fn from (state: &SequencerStatusBar) -> Self { - Self { - mode: state.mode, - help: state.help, - } - } -} -render!(|self: SequencerMode|{ - let black = TuiTheme::g(0); - let light = TuiTheme::g(50); - let white = TuiTheme::g(255); - let orange = TuiTheme::orange(); - let yellow = TuiTheme::yellow(); - row!([ - Tui::bg(orange, Tui::fg(black, Tui::bold(true, self.mode))), - Tui::bg(light, Tui::fg(white, row!((prefix, hotkey, suffix) in self.help.iter() => { - row!([" ", prefix, Tui::fg(yellow, *hotkey), suffix]) - }))) - ]) -}); - -struct SequencerStats<'a> { - cpu: &'a Option, - size: &'a String, - res: &'a String, -} -impl<'a> From<&'a SequencerStatusBar> for SequencerStats<'a> { - fn from (state: &'a SequencerStatusBar) -> Self { - Self { - cpu: &state.cpu, - size: &state.size, - res: &state.res, - } - } -} -render!(|self:SequencerStats<'a>|{ - let orange = TuiTheme::orange(); - let dark = TuiTheme::g(25); - let cpu = &self.cpu; - let res = &self.res; - let size = &self.size; - Tui::bg(dark, row!([ - Tui::fg(orange, cpu), - Tui::fg(orange, res), - Tui::fg(orange, size), - ])) -});