mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
TransportView it is
This commit is contained in:
parent
cda0708642
commit
4ce4742959
5 changed files with 131 additions and 147 deletions
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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|{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
]))
|
||||
])
|
||||
])
|
||||
//])
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<RwLock<JackClient>>,
|
||||
pub clock: ClockModel,
|
||||
pub size: Measure<Tui>,
|
||||
pub cursor: (usize, usize),
|
||||
pub focus: FocusState<TransportFocus>,
|
||||
}
|
||||
|
||||
/// Create app state from JACK handle.
|
||||
impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
|
@ -17,111 +26,6 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
|||
}
|
||||
}
|
||||
|
||||
/// Stores and displays time-related info.
|
||||
pub struct TransportTui {
|
||||
pub jack: Arc<RwLock<JackClient>>,
|
||||
pub clock: ClockModel,
|
||||
pub size: Measure<Tui>,
|
||||
pub cursor: (usize, usize),
|
||||
pub focus: FocusState<TransportFocus>,
|
||||
}
|
||||
|
||||
impl JackApi for TransportTui {
|
||||
fn jack (&self) -> &Arc<RwLock<JackClient>> {
|
||||
&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<T: HasClock> 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<T: HasClock> 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<RwLock<JackClient>> {
|
||||
&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<T: HasClock> 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 {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,13 @@ 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|{
|
||||
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 title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
|
||||
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);
|
||||
|
|
@ -48,14 +52,7 @@ render!(|self:PhraseSelector<'a>|{
|
|||
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::fill_xy(Tui::at_nw(Tui::push_x(1, Tui::fg(title_color, *title)))),
|
||||
)
|
||||
]))
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue