From 4ce47429599e39d545c6e5bc5ab9f593beb41720 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 9 Dec 2024 23:17:46 +0100 Subject: [PATCH] TransportView it is --- crates/tek/src/api/clock.rs | 5 +- crates/tek/src/tui/app_arranger.rs | 9 +- crates/tek/src/tui/app_sequencer.rs | 23 +-- crates/tek/src/tui/app_transport.rs | 208 ++++++++++----------- crates/tek/src/tui/view_phrase_selector.rs | 33 ++-- 5 files changed, 131 insertions(+), 147 deletions(-) diff --git a/crates/tek/src/api/clock.rs b/crates/tek/src/api/clock.rs index 5d1e03b2..8e6e8574 100644 --- a/crates/tek/src/api/clock.rs +++ b/crates/tek/src/api/clock.rs @@ -166,7 +166,10 @@ impl ClockModel { match self.transport.query_state()? { TransportState::Rolling => { if started.is_none() { - *started = Some(Moment::from_sample(&self.timebase, current_frames as f64)); + let moment = Moment::zero(&self.timebase); + moment.sample.set(current_frames as f64); + moment.usec.set(current_usecs as f64); + *started = Some(moment); } }, TransportState::Stopped => { diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 57490c2f..9a0640f2 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -111,14 +111,7 @@ render!(|self: ArrangerTui|{ let arranger_focused = self.arranger_focused(); let border = Lozenge(Style::default().bg(TuiTheme::border_bg()).fg(TuiTheme::border_fg(arranger_focused))); col!([ - lay!([ - Tui::fill_x(Lozenge(Style::default())), - Tui::outset_xy(1, 1, row!([ - WorldClock::from(self), - " ", - PlayClock::from(self), - ])) - ]), + TransportView::from(self), col!([ Tui::fixed_y(self.splits[0], lay!([ border.wrap(Tui::grow_y(1, Layers::new(move |add|{ diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index dccd4ede..eea3070e 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -90,30 +90,23 @@ render!(|self: SequencerTui|{ //col_up!([ //Tui::max_y(2, SequencerStatusBar::from(self)), col!([ - lay!([ - Tui::fill_x(Lozenge(Style::default())), - Tui::outset_xy(1, 1, row!([ - WorldClock::from(self), - " ", - PlayClock::from(self), - ])) - ]), - Tui::min_y(20, row!([ - Tui::min_x(20, col!([ - Tui::fixed_y(4, PhraseSelector::play_phrase( + TransportView::from(self), + row!([ + Tui::fixed_x(20, col!([ + PhraseSelector::play_phrase( &self.player, self.focused() == SequencerFocus::PhrasePlay, self.entered() - )), - Tui::fixed_y(4, PhraseSelector::next_phrase( + ), + PhraseSelector::next_phrase( &self.player, self.focused() == SequencerFocus::PhraseNext, self.entered() - )), + ), PhraseListView::from(self) ])), PhraseView::from(self) - ])) + ]) ]) //]) }); diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index e68083e9..a399a989 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -3,6 +3,15 @@ use crate::api::ClockCommand::{SetBpm, SetQuant, SetSync}; use TransportCommand::{Focus, Clock}; use KeyCode::{Enter, Left, Right, Char}; +/// Transport clock app. +pub struct TransportTui { + pub jack: Arc>, + pub clock: ClockModel, + pub size: Measure, + pub cursor: (usize, usize), + pub focus: FocusState, +} + /// Create app state from JACK handle. impl TryFrom<&Arc>> for TransportTui { type Error = Box; @@ -17,111 +26,6 @@ impl TryFrom<&Arc>> for TransportTui { } } -/// Stores and displays time-related info. -pub struct TransportTui { - pub jack: Arc>, - pub clock: ClockModel, - pub size: Measure, - pub cursor: (usize, usize), - pub focus: FocusState, -} - -impl JackApi for TransportTui { - fn jack (&self) -> &Arc> { - &self.jack - } -} - -impl Audio for TransportTui { - fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { - ClockAudio(self).process(client, scope) - } -} - -pub struct WorldClock { - rate: String, - sample: String, - second: String, -} - -impl From<&T> for WorldClock { - fn from (state: &T) -> Self { - let clock = state.clock(); - let rate = format!("{}", clock.timebase.sr.get()); - if let Some(started) = clock.started.read().unwrap().as_ref() { - Self { - rate, - sample: format!("{:.0}", started.sample.get()), - second: format!("{:.0}", started.usec.get()), - } - } else { - Self { - rate, - sample: format!("{:.0}", clock.global.sample.get()), - second: format!("{:.0}", clock.global.usec.get()), - } - } - } -} - -render!(|self: WorldClock|row!([ - col!(["Rate", self.rate ]), " ", - col!(["Sample", self.sample]), " ", - col!(["Second", self.second]), " ", -])); - -pub struct PlayClock { - started: bool, - sample: String, - second: String, -} -impl From<&T> for PlayClock { - fn from (state: &T) -> Self { - let clock = state.clock(); - if let Some(started) = clock.started.read().unwrap().as_ref() { - Self { - started: true, - sample: format!("{:.0}", clock.global.sample.get() - started.sample.get()), - second: format!("{:.0}", clock.global.usec.get() - started.usec.get()), - } - } else { - Self { - started: false, - sample: "".to_string(), - second: "".to_string(), - } - } - } -} -render!(|self: PlayClock|lay!(|add|{ - if self.started { - add(&row!([ - col!(["", Tui::fg(Color::Rgb(0, 255, 0), "▶ PLAYING ")]), - " ", - col!(["Sample", self.sample]), - " ", - col!(["Second", self.second]), - " ", - col!(["Beat", "00B 0b 00/00"]), - ])) - } else { - add(&col!([Tui::fg(Color::Rgb(255, 128, 0), "⏹ STOPPED "), ""])) - } -})); - -render!(|self: TransportTui|{ - let bg = TuiTheme::border_bg(); - let border_style = Style::default().bg(bg).fg(TuiTheme::border_fg(false)); - lay!([ - Tui::fill_x(Lozenge(border_style)), - Tui::bg(bg, Tui::outset_xy(1, 1, row!([ - WorldClock::from(self), - " ", - PlayClock::from(self), - ]))) - ])//Tui::to_south(world, timer))) -}); - impl std::fmt::Debug for TransportTui { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("TransportTui") @@ -138,6 +42,100 @@ impl HasClock for TransportTui { } } +impl JackApi for TransportTui { + fn jack (&self) -> &Arc> { + &self.jack + } +} + +impl Audio for TransportTui { + fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { + ClockAudio(self).process(client, scope) + } +} + +render!(|self: TransportTui|TransportView::from(self)); + +pub struct TransportView { + sr: String, + bpm: String, + ppq: String, + + global_sample: String, + global_second: String, + + started: bool, + + current_sample: String, + current_second: String, +} + +impl From<&T> for TransportView { + fn from (state: &T) -> Self { + let clock = state.clock(); + let sr = format!("{:.1}k", clock.timebase.sr.get() / 1000.0); + let bpm = format!("{:.3}", clock.timebase.bpm.get()); + let ppq = format!("{:.0}", clock.timebase.ppq.get()); + if let Some(started) = clock.started.read().unwrap().as_ref() { + Self { + sr, + bpm, + ppq, + started: true, + global_sample: format!("{:.0}", started.sample.get()), + global_second: format!("{:.0}", started.usec.get()), + current_sample: format!("{:.0}", clock.global.sample.get() - started.sample.get()), + current_second: format!("{:.0}", clock.global.usec.get() - started.usec.get()), + } + } else { + Self { + sr, + bpm, + ppq, + started: false, + global_sample: format!("{:.0}", clock.global.sample.get()), + global_second: format!("{:.0}", clock.global.usec.get()), + current_sample: "".to_string(), + current_second: "".to_string(), + } + } + } +} + +render!(|self: TransportView|{ + let bg = TuiTheme::border_bg(); + let border_style = Style::default().bg(bg).fg(TuiTheme::border_fg(false)); + lay!([ + Tui::fill_x(Lozenge(border_style)), + Tui::bg(bg, Tui::outset_xy(1, 1, row!([ + row!([ + col!(["SR", self.sr ]), " ", + col!(["BPM", self.bpm]), " ", + col!(["PPQ", self.ppq]), " ", + ]), + row!([ + col!(["Sample", self.global_sample]), " ", + col!(["Second", self.global_second]), " ", + ]), + lay!(|add|{ + if self.started { + add(&row!([ + col!(["", Tui::fg(Color::Rgb(0, 255, 0), "▶ PLAYING ")]), + " ", + col!(["Sample", self.current_sample]), + " ", + col!(["Second", self.current_second]), + " ", + col!(["Beat", "00B 0b 00/00"]), + ])) + } else { + add(&col!([Tui::fg(Color::Rgb(255, 128, 0), "⏹ STOPPED "), ""])) + } + }), + ]))) + ]) +}); + /// Which item of the transport toolbar is focused #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum TransportFocus { diff --git a/crates/tek/src/tui/view_phrase_selector.rs b/crates/tek/src/tui/view_phrase_selector.rs index 248c3979..d2ec9a41 100644 --- a/crates/tek/src/tui/view_phrase_selector.rs +++ b/crates/tek/src/tui/view_phrase_selector.rs @@ -35,27 +35,24 @@ impl<'a> PhraseSelector<'a> { } // TODO: Display phrases always in order of appearance -render!(|self:PhraseSelector<'a>|{ +render!(|self: PhraseSelector<'a>|{ let Self { title, phrase, focused, entered } = self; - let content = Layers::new(move|add|{ - if let Some((instant, Some(phrase))) = phrase { - let Phrase { ref name, color, length, .. } = *phrase.read().unwrap(); - let length = PhraseLength::new(length, None); - let length = Tui::fill_x(Tui::at_e(length)); - let row1 = Tui::fill_x(lay!([Tui::fill_x(Tui::at_w(format!(" "))), length])); - let row2 = format!(" {name}"); - let row2 = Tui::bold(true, row2); - add(&Tui::bg(color.base.rgb, Tui::fill_x(Tui::to_south(row1, row2))))?; - } - Ok(()) - }); let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)}; let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); - //let content = Tui::bg(Color::Rgb(28, 35, 25), Tui::fill_xy(content)).border(border); - let content = Tui::bg(Color::Rgb(28, 35, 25), Tui::fill_xy(content)); let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)}; - Tui::over( - border.wrap(content), + Tui::fixed_y(4, lay!([ + border.wrap(Tui::bg(Color::Rgb(28, 35, 25), Tui::fill_xy(Layers::new(move|add|{ + if let Some((instant, Some(phrase))) = phrase { + let Phrase { ref name, color, length, .. } = *phrase.read().unwrap(); + let length = PhraseLength::new(length, None); + let length = Tui::fill_x(Tui::at_e(length)); + let row1 = Tui::fill_x(lay!([Tui::fill_x(Tui::at_w(format!(" "))), length])); + let row2 = format!(" {name}"); + let row2 = Tui::bold(true, row2); + add(&Tui::bg(color.base.rgb, Tui::fill_x(Tui::to_south(row1, row2))))?; + } + Ok(()) + })))), Tui::fill_xy(Tui::at_nw(Tui::push_x(1, Tui::fg(title_color, *title)))), - ) + ])) });