diff --git a/bin/cli_transport.rs b/bin/cli_transport.rs index abf9373c..2bee935c 100644 --- a/bin/cli_transport.rs +++ b/bin/cli_transport.rs @@ -2,9 +2,7 @@ include!("./lib.rs"); /// Application entrypoint. pub fn main () -> Usually<()> { - let name = "tek_transport"; - let engine = Tui::new()?; - let state = JackConnection::new(name)? - .activate_with(|jack|TransportTui::try_from(jack))?; - engine.run(&state) + let name = "tek_transport"; + Tui::new()?.run(&JackConnection::new(name)? + .activate_with(|jack|TransportTui::new(jack))?) } diff --git a/src/arranger.rs b/src/arranger.rs index 1200fe0c..418abbf9 100644 --- a/src/arranger.rs +++ b/src/arranger.rs @@ -7,6 +7,11 @@ mod arranger_track; pub(crate) use self::arranger_track::*; mod arranger_mode; pub(crate) use self::arranger_mode::*; mod arranger_v; #[allow(unused)] pub(crate) use self::arranger_v::*; mod arranger_h; +mod arranger_v_clips; pub(crate) use self::arranger_v_clips::*; +mod arranger_v_cursor; pub(crate) use self::arranger_v_cursor::*; +mod arranger_v_head; pub(crate) use self::arranger_v_head::*; +mod arranger_v_io; pub(crate) use self::arranger_v_io::*; +mod arranger_v_sep; pub(crate) use self::arranger_v_sep::*; /// Root view for standalone `tek_arranger` pub struct ArrangerTui { diff --git a/src/arranger/arranger_v.rs b/src/arranger/arranger_v.rs index 810b009d..70b92591 100644 --- a/src/arranger/arranger_v.rs +++ b/src/arranger/arranger_v.rs @@ -1,12 +1,7 @@ use crate::*; -mod v_clips; pub(crate) use self::v_clips::*; -mod v_cursor; pub(crate) use self::v_cursor::*; -mod v_head; pub(crate) use self::v_head::*; -mod v_io; pub(crate) use self::v_io::*; -mod v_sep; pub(crate) use self::v_sep::*; // egyptian snakes den -const HEADER_H: u16 = 5; -const SCENES_W_OFFSET: u16 = 3; +pub(crate) const HEADER_H: u16 = 5; +pub(crate) const SCENES_W_OFFSET: u16 = 3; impl ArrangerTui { pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Content + use<'_> { lay!( diff --git a/src/arranger/arranger_v/v_clips.rs b/src/arranger/arranger_v_clips.rs similarity index 100% rename from src/arranger/arranger_v/v_clips.rs rename to src/arranger/arranger_v_clips.rs diff --git a/src/arranger/arranger_v/v_cursor.rs b/src/arranger/arranger_v_cursor.rs similarity index 100% rename from src/arranger/arranger_v/v_cursor.rs rename to src/arranger/arranger_v_cursor.rs diff --git a/src/arranger/arranger_v/v_head.rs b/src/arranger/arranger_v_head.rs similarity index 100% rename from src/arranger/arranger_v/v_head.rs rename to src/arranger/arranger_v_head.rs diff --git a/src/arranger/arranger_v/v_io.rs b/src/arranger/arranger_v_io.rs similarity index 100% rename from src/arranger/arranger_v/v_io.rs rename to src/arranger/arranger_v_io.rs diff --git a/src/arranger/arranger_v/v_sep.rs b/src/arranger/arranger_v_sep.rs similarity index 100% rename from src/arranger/arranger_v/v_sep.rs rename to src/arranger/arranger_v_sep.rs diff --git a/src/clock/clock_tui.rs b/src/clock/clock_tui.rs index 6ad770ce..035e5eb7 100644 --- a/src/clock/clock_tui.rs +++ b/src/clock/clock_tui.rs @@ -5,30 +5,30 @@ use KeyCode::{Enter, Left, Right, Char}; /// Transport clock app. pub struct TransportTui { - pub jack: Arc>, - pub clock: Clock, - pub size: Measure, - pub cursor: (usize, usize), - pub color: ItemPalette, + pub jack: Arc>, + pub clock: Clock, } -from_jack!(|jack|TransportTui Self { - jack: jack.clone(), - clock: Clock::from(jack), - size: Measure::new(), - cursor: (0, 0), - color: ItemPalette::random(), -}); has_clock!(|self: TransportTui|&self.clock); audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope)); handle!(|self: TransportTui, from|TransportCommand::execute_with_state(self, from)); render!(Tui: (self: TransportTui) => TransportView(&self.clock)); +impl TransportTui { + pub fn new (jack: &Arc>) -> Usually { + Ok(Self { + jack: jack.clone(), + clock: Clock::from(jack), + }) + } +} pub struct TransportView<'a>(pub &'a Clock); -render!(Tui: (self: TransportView<'a>) => row!( +render!(Tui: (self: TransportView<'a>) => Outer( + Style::default().fg(TuiTheme::g(255)).bg(TuiTheme::g(0)) +).enclose(row!( BeatStats::new(self.0), " ", PlayPause(self.0.is_rolling()), " ", OutputStats::new(self.0), -)); +))); pub struct PlayPause(pub bool); render!(Tui: (self: PlayPause) => Tui::bg( @@ -36,15 +36,6 @@ render!(Tui: (self: PlayPause) => Tui::bg( Fixed::x(5, Tui::either(self.0, Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))); -impl std::fmt::Debug for TransportTui { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("TransportTui") - .field("jack", &self.jack) - .field("size", &self.size) - .field("cursor", &self.cursor) - .finish() - } -} pub struct BeatStats { bpm: String, beat: String, time: String, } impl BeatStats { @@ -60,7 +51,9 @@ impl BeatStats { } } render!(Tui: (self: BeatStats) => col!( - Bsp::e(&self.bpm, " BPM"), Bsp::e("Beat ", &self.beat), Bsp::e("Time ", &self.time), + Bsp::e(Tui::fg(TuiTheme::g(255), &self.bpm), " BPM"), + Bsp::e("Beat ", Tui::fg(TuiTheme::g(255), &self.beat)), + Bsp::e("Time ", Tui::fg(TuiTheme::g(255), &self.time)), )); pub struct OutputStats { sample_rate: String, buffer_size: String, latency: String, } @@ -76,14 +69,16 @@ impl OutputStats { } } render!(Tui: (self: OutputStats) => col!( - Bsp::e(format!("{}", self.sample_rate), " sample rate"), - Bsp::e(format!("{}", self.buffer_size), " sample buffer"), - Bsp::e(format!("{:.3}ms", self.latency), " latency"), + Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.sample_rate)), " sample rate"), + Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.buffer_size)), " sample buffer"), + Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"), )); + #[derive(Clone, Debug, PartialEq)] pub enum TransportCommand { Clock(ClockCommand), } + command!(|self:TransportCommand,state:TransportTui|match self { //Self::Focus(cmd) => cmd.execute(state)?.map(Self::Focus), Self::Clock(cmd) => cmd.execute(state)?.map(Self::Clock), diff --git a/src/groovebox.rs b/src/groovebox.rs index 6217a0d1..8de7ecf6 100644 --- a/src/groovebox.rs +++ b/src/groovebox.rs @@ -111,42 +111,42 @@ render!(Tui: (self: Groovebox) => { &self.size, Bsp::s( Fill::x(Fixed::y(3, Align::x(TransportView(&self.player.clock)))), - Bsp::s( - Fill::x(Fixed::y(1, Align::x(Bsp::e( - PhraseSelector::play_phrase(&self.player), - PhraseSelector::next_phrase(&self.player), - )))), - Bsp::n( - Fixed::y(9, col!( - Bsp::e( - self.sampler.mapped[note_pt].as_ref().map(|sample|format!( - "Sample {}-{}", - sample.read().unwrap().start, - sample.read().unwrap().end, - )), - MidiEditStatus(&self.editor), - ), - Bsp::a( - Outer(Style::default().fg(TuiTheme::g(128))), - Fill::x(Fixed::y(8, if let Some((_, sample)) = &self.sampler.recording { - SampleViewer(Some(sample.clone())) - } else if let Some(sample) = &self.sampler.mapped[note_pt] { - SampleViewer(Some(sample.clone())) - } else { - SampleViewer(None) - })), - ), + Bsp::n( + Fixed::y(9, Bsp::s( + self.sampler.mapped[note_pt].as_ref().map(|sample|format!( + "Sample {}-{}", + sample.read().unwrap().start, + sample.read().unwrap().end, )), - Bsp::w( - Align::e(Fill::y(Fixed::x(pool_w, PoolView(&self.pool)))), - Fill::xy(Bsp::e( - Align::w(Fixed::x(sampler_w, Tui::bg(TuiTheme::g(32), Fill::xy(col!( - Meters(self.sampler.input_meter.as_ref()), - GrooveboxSamples(self) - ))))), - Fill::xy(Align::c("kyp")) - )) - ) + Bsp::a( + Outer(Style::default().fg(TuiTheme::g(128))), + Fill::x(Fixed::y(8, if let Some((_, sample)) = &self.sampler.recording { + SampleViewer(Some(sample.clone())) + } else if let Some(sample) = &self.sampler.mapped[note_pt] { + SampleViewer(Some(sample.clone())) + } else { + SampleViewer(None) + })), + ), + )), + Bsp::w( + Align::e(Fill::y(Fixed::x(pool_w, PoolView(&self.pool)))), + Fill::xy(Bsp::e( + Align::w(Fixed::x(sampler_w, Tui::bg(TuiTheme::g(32), Fill::xy(col!( + Meters(self.sampler.input_meter.as_ref()), + GrooveboxSamples(self) + ))))), + Bsp::s( + Fill::x(Align::c(Bsp::e( + PhraseSelector::play_phrase(&self.player), + PhraseSelector::next_phrase(&self.player), + ))), + Bsp::n( + MidiEditStatus(&self.editor), + Fill::xy(Align::c("kyp")) + ), + ), + )) ) ) ) diff --git a/src/lib.rs b/src/lib.rs index 56441411..fd331761 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ pub mod jack; pub use self::jack::*; pub mod meter; pub use self::meter::*; pub mod midi; pub use self::midi::*; pub mod mixer; pub use self::mixer::*; -pub mod piano_h; pub use self::piano_h::*; +pub mod piano; pub use self::piano::*; pub mod plugin; pub use self::plugin::*; pub mod pool; pub use self::pool::*; pub mod sampler; pub use self::sampler::*; diff --git a/src/piano.rs b/src/piano.rs new file mode 100644 index 00000000..81c75119 --- /dev/null +++ b/src/piano.rs @@ -0,0 +1,46 @@ +use crate::*; +use super::*; + +mod piano_h; pub(crate) use self::piano_h::*; +mod piano_h_cursor; pub(crate) use self::piano_h_cursor::*; +mod piano_h_keys; pub(crate) use self::piano_h_keys::*; +mod piano_h_notes; pub(crate) use self::piano_h_notes::*; +mod piano_h_time; pub(crate) use self::piano_h_time::*; + +/// A phrase, rendered as a horizontal piano roll. +pub struct PianoHorizontal { + phrase: Option>>, + /// Buffer where the whole phrase is rerendered on change + buffer: BigBuffer, + /// Size of actual notes area + size: Measure, + /// The display window + range: MidiRangeModel, + /// The note cursor + point: MidiPointModel, + /// The highlight color palette + color: ItemPalette, + /// Width of the keyboard + keys_width: u16, +} + +impl PianoHorizontal { + pub fn new (phrase: Option<&Arc>>) -> Self { + let size = Measure::new(); + let mut range = MidiRangeModel::from((24, true)); + range.time_axis = size.x.clone(); + range.note_axis = size.y.clone(); + let color = phrase.as_ref() + .map(|p|p.read().unwrap().color) + .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); + Self { + buffer: Default::default(), + point: MidiPointModel::default(), + phrase: phrase.cloned(), + size, + range, + color, + keys_width: 5 + } + } +} diff --git a/src/piano_h.rs b/src/piano/piano_h.rs similarity index 84% rename from src/piano_h.rs rename to src/piano/piano_h.rs index 654ac785..f9711b06 100644 --- a/src/piano_h.rs +++ b/src/piano/piano_h.rs @@ -1,53 +1,10 @@ use crate::*; use super::*; -mod piano_h_cursor; use self::piano_h_cursor::*; -mod piano_h_keys; pub(crate) use self::piano_h_keys::*; -mod piano_h_notes; use self::piano_h_notes::*; -mod piano_h_time; use self::piano_h_time::*; - 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)) } -/// A phrase, rendered as a horizontal piano roll. -pub struct PianoHorizontal { - phrase: Option>>, - /// Buffer where the whole phrase is rerendered on change - buffer: BigBuffer, - /// Size of actual notes area - size: Measure, - /// The display window - range: MidiRangeModel, - /// The note cursor - point: MidiPointModel, - /// The highlight color palette - color: ItemPalette, - /// Width of the keyboard - keys_width: u16, -} - -impl PianoHorizontal { - pub fn new (phrase: Option<&Arc>>) -> Self { - let size = Measure::new(); - let mut range = MidiRangeModel::from((24, true)); - range.time_axis = size.x.clone(); - range.note_axis = size.y.clone(); - let color = phrase.as_ref() - .map(|p|p.read().unwrap().color) - .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); - Self { - buffer: Default::default(), - point: MidiPointModel::default(), - phrase: phrase.cloned(), - size, - range, - color, - keys_width: 5 - } - } -} - render!(Tui: (self: PianoHorizontal) => { 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) diff --git a/src/piano_h/piano_h_cursor.rs b/src/piano/piano_h_cursor.rs similarity index 98% rename from src/piano_h/piano_h_cursor.rs rename to src/piano/piano_h_cursor.rs index 7e7a4cbf..3a5d6f31 100644 --- a/src/piano_h/piano_h_cursor.rs +++ b/src/piano/piano_h_cursor.rs @@ -1,5 +1,5 @@ use crate::*; -use super::note_y_iter; +use super::*; pub struct PianoHorizontalCursor<'a>(pub(crate) &'a PianoHorizontal); render!(Tui: |self: PianoHorizontalCursor<'a>, render|{ diff --git a/src/piano_h/piano_h_keys.rs b/src/piano/piano_h_keys.rs similarity index 98% rename from src/piano_h/piano_h_keys.rs rename to src/piano/piano_h_keys.rs index 73c7d816..35efaa77 100644 --- a/src/piano_h/piano_h_keys.rs +++ b/src/piano/piano_h_keys.rs @@ -1,5 +1,5 @@ use crate::*; -use super::note_y_iter; +use super::*; pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal); diff --git a/src/piano_h/piano_h_notes.rs b/src/piano/piano_h_notes.rs similarity index 98% rename from src/piano_h/piano_h_notes.rs rename to src/piano/piano_h_notes.rs index 59041666..541b681c 100644 --- a/src/piano_h/piano_h_notes.rs +++ b/src/piano/piano_h_notes.rs @@ -1,5 +1,5 @@ use crate::*; -use super::note_y_iter; +use super::*; pub struct PianoHorizontalNotes<'a>(pub(crate) &'a PianoHorizontal); diff --git a/src/piano_h/piano_h_time.rs b/src/piano/piano_h_time.rs similarity index 76% rename from src/piano_h/piano_h_time.rs rename to src/piano/piano_h_time.rs index 7def5e37..8f3ccf74 100644 --- a/src/piano_h/piano_h_time.rs +++ b/src/piano/piano_h_time.rs @@ -1,4 +1,5 @@ use crate::*; +use super::*; pub struct PianoHorizontalTimeline<'a>(pub(crate) &'a PianoHorizontal); render!(Tui: |self: PianoHorizontalTimeline<'a>, render|{ @@ -12,9 +13,3 @@ render!(Tui: |self: PianoHorizontalTimeline<'a>, render|{ } } }); - - //Tui::fg_bg( - //self.0.color.lightest.rgb, - //self.0.color.darkest.rgb, - //format!("{}*{}", self.0.time_start(), self.0.time_zoom()).as_str() -//)); diff --git a/src/piano_v.rs b/src/piano_v.rs deleted file mode 100644 index 70b786d1..00000000 --- a/src/piano_v.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO diff --git a/src/pool.rs b/src/pool.rs index 07935891..c36f609b 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -211,7 +211,7 @@ render!(Tui: (self: PoolView<'a>) => { let color = self.0.phrase().read().unwrap().color; Outer( Style::default().fg(color.dark.rgb).bg(color.darkest.rgb) - ).enclose(Tui::bg(Color::Black, Tui::map(||self.0.phrases().iter(), |clip, i|{ + ).enclose(Tui::map(||self.0.phrases().iter(), |clip, i|{ let MidiClip { ref name, color, length, .. } = *clip.read().unwrap(); let item_height = 1; let item_offset = i as u16 * item_height; @@ -223,7 +223,7 @@ render!(Tui: (self: PoolView<'a>) => { Align::w(Tui::when(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "▶")))), Align::e(Tui::when(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "◀")))), ))) - }))) + })) /*//format!(" {i} {name} {length} ")[> //Push::y(i as u16 * 2, Fixed::y(2, Tui::bg(color.base.rgb, Fill::x( //format!(" {i} {name} {length} ")))))[>,