From ed462cd0f6e0567b85f2a9f7ee79f94399732b62 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 11 Jan 2025 20:48:29 +0100 Subject: [PATCH] provide components to App --- cli/tek.rs | 13 ++-- tek/src/app.rs | 176 +++++++++++++++++++++++++++---------------- tek/src/arranger.rs | 33 +++++--- tek/src/groovebox.rs | 14 ++++ tek/src/sequencer.rs | 18 +++++ 5 files changed, 172 insertions(+), 82 deletions(-) diff --git a/cli/tek.rs b/cli/tek.rs index 7c08540a..750a5a30 100644 --- a/cli/tek.rs +++ b/cli/tek.rs @@ -76,8 +76,8 @@ pub fn main () -> Usually<()> { let left_tos = PortConnection::collect(&cli.left_to, empty, empty); let right_froms = PortConnection::collect(&cli.right_from, empty, empty); let right_tos = PortConnection::collect(&cli.right_to, empty, empty); - let audio_froms = &[&left_froms, &right_froms]; - let audio_tos = &[&left_tos, &right_tos ]; + let audio_froms = &[left_froms.as_slice(), right_froms.as_slice()]; + let audio_tos = &[left_tos.as_slice(), right_tos.as_slice() ]; let perf = PerfModel::default(); let size = Measure::new(); let default_clip = ||Arc::new(RwLock::new(MidiClip::new( @@ -93,10 +93,7 @@ pub fn main () -> Usually<()> { } clock }; - let default_clock = |jack: &Arc>|{ - let clock = Clock::from(jack); - default_bpm(clock) - }; + let default_clock = |jack: &Arc>|default_bpm(Clock::from(jack)); // TODO: enable sync master/follow //let sync_clock = |jack: &Arc>, app|{ //if cli.sync_lead { @@ -148,7 +145,7 @@ pub fn main () -> Usually<()> { App::groovebox( jack, (&clip).into(), (&clip).into(), Some(player), &midi_froms, &midi_tos, - sampler, &audio_froms, &audio_tos, + sampler, audio_froms, audio_tos, ) }))?)?, TekMode::Arranger { scenes, tracks, track_width, .. } => engine.run(&jack.activate_with(|jack|Ok({ @@ -156,7 +153,7 @@ pub fn main () -> Usually<()> { jack, PoolModel::default(), MidiEditor::default(), &midi_froms, &midi_tos, - default_sampler(jack)?, &audio_froms, &audio_tos, + default_sampler(jack)?, audio_froms, audio_tos, scenes, tracks, track_width ) }))?)?, diff --git a/tek/src/app.rs b/tek/src/app.rs index 1f0b5bfb..9d19b5cc 100644 --- a/tek/src/app.rs +++ b/tek/src/app.rs @@ -1,17 +1,6 @@ use crate::*; #[derive(Default)] pub struct App { - //jack: Arc>, - //view: EdnView, - //pool: Option, - //editor: Option, - //player: Option, - //compact: AtomicBool, - //size: Measure, - //perf: PerfModel, - //note_buf: Vec, - //midi_buf: Vec>> - pub jack: Arc>, pub edn: String, pub clock: Clock, @@ -34,7 +23,6 @@ pub struct App { pub size: Measure, pub perf: PerfModel, } -impl EdnViewData for &App {} impl App { pub fn sequencer ( jack: &Arc>, @@ -100,58 +88,120 @@ impl App { } } render!(TuiOut: (self: App) => self.size.of(EdnView::from_source(self, self.edn.as_ref()))); - -/// Root view for standalone `tek_sequencer`. -pub struct Sequencer { - pub _jack: Arc>, - - pub pool: PoolModel, - pub editor: MidiEditor, - pub player: MidiPlayer, - - pub transport: bool, - pub selectors: bool, - pub compact: bool, - - pub size: Measure, - pub status: bool, - pub note_buf: Vec, - pub midi_buf: Vec>>, - pub perf: PerfModel, +audio!(|self: App, _client, _scope|Control::Continue); +handle!(TuiIn: |self: App, input| Ok(None)); +impl EdnViewData for &App { + fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { + use EdnItem::*; + let w = self.tracks_with_sizes().last().map(|x|x.3 as u16).unwrap_or(0); + match item { + Nil => Box::new(()), + Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), + Sym(":editor") => (&self.editor).boxed(), + Sym(":inputs") => self.input_row(w).boxed(), + Sym(":outputs") => self.output_row(w).boxed(), + Sym(":pool") => self.pool().boxed(), + Sym(":sample") => self.sample().boxed(), + Sym(":sampler") => self.sampler().boxed(), + Sym(":scenes") => self.scene_row(w).boxed(), + Sym(":status") => self.status(0).boxed(), + Sym(":toolbar") => self.toolbar().boxed(), + Sym(":tracks") => self.track_row(w).boxed(), + _ => panic!("no content for {item:?}") + } + } + fn get_unit (&self, item: EdnItem<&str>) -> u16 { + use EdnItem::*; + match item.to_str() { + ":sample-h" => if self.compact() { 0 } else { 5 }, + ":samples-w" => if self.compact() { 4 } else { 11 }, + ":samples-y" => if self.compact() { 1 } else { 0 }, + ":pool-w" => if self.compact() { 5 } else { + let w = self.size.w(); + if w > 60 { 20 } else if w > 40 { 15 } else { 10 } + }, + _ => 0 + } + } } +impl App { + fn compact (&self) -> bool { false } + fn toolbar (&self) -> impl Content + use<'_> { + Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock)))) + } + fn status (&self, note_pt: usize) -> impl Content + use<'_> { + self.editor.as_ref() + .map(|e|Bsp::e(e.clip_status(), e.edit_status())) + } + fn pool (&self) -> impl Content + use<'_> { + self.pool.as_ref() + .map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact(), pool)))) + } + fn editor (&self) -> impl Content + '_ { + &self.editor + } + fn sample <'a> (&'a self) -> impl Content + 'a { + let compact = self.is_editing(); + if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) { + let note_pt = editor.note_point(); + let sample_h = if compact { 0 } else { 5 }; + return Some(Max::y(sample_h, Fill::xy(Bsp::a( + Fill::x(Align::w(Fixed::y(1, self.status(note_pt)))), + sampler.viewer(note_pt) + )))) + } + None + } + fn sampler (&self) -> impl Content + use<'_> { + let compact = self.is_editing(); + if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) { + let note_pt = editor.note_point(); + let w = if compact { 4 } else { 40 }; + let y = if compact { 1 } else { 0 }; + return Some(Fixed::x(w, Push::y(y, Fill::y(sampler.list(compact, editor))))) + } + None + } + fn track_row (&self, w: u16) -> impl Content + '_ { "" } + fn input_row (&self, w: u16) -> impl Content + '_ { "" } + fn scene_row (&self, w: u16) -> impl Content + '_ { "" } + fn output_row (&self, w: u16) -> impl Content + '_ { "" } -pub struct Groovebox { - pub _jack: Arc>, - pub player: MidiPlayer, - pub pool: PoolModel, - pub editor: MidiEditor, - pub sampler: Sampler, - - pub compact: bool, - pub size: Measure, - pub status: bool, - pub note_buf: Vec, - pub midi_buf: Vec>>, - pub perf: PerfModel, + pub fn tracks_with_sizes (&self) + -> impl Iterator + { + tracks_with_sizes(self.tracks.iter(), match self.selected { + ArrangerSelection::Track(t) if self.is_editing() => Some(t), + ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t), + _ => None + }, self.editor_w()) + } + fn is_editing (&self) -> bool { + self.editing.load(Relaxed) + } + fn editor_w (&self) -> usize { + let editor = self.editor.as_ref().expect("missing editor"); + (5 + (editor.time_len().get() / editor.time_zoom().get())) + .min(self.size.w().saturating_sub(20)) + .max(16) + } + fn sidebar_w (&self) -> u16 { + let w = self.size.w(); + let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + let w = if self.is_editing() { 8 } else { w }; + w + } } - -/// Root view for standalone `tek_arranger` -pub struct Arranger { - pub jack: Arc>, - pub midi_ins: Vec>, - pub midi_outs: Vec>, - pub clock: Clock, - pub pool: PoolModel, - pub tracks: Vec, - pub scenes: Vec, - pub splits: [u16;2], - pub selected: ArrangerSelection, - pub color: ItemPalette, - pub size: Measure, - pub note_buf: Vec, - pub midi_buf: Vec>>, - pub editor: MidiEditor, - pub editing: AtomicBool, - pub perf: PerfModel, - pub compact: bool, +pub fn tracks_with_sizes <'a> ( + tracks: impl Iterator, + active: Option, + bigger: usize +) -> impl Iterator { + let mut x = 0; + tracks.enumerate().map(move |(index, track)|{ + let width = if Some(index) == active { bigger } else { track.width.max(8) }; + let data = (index, track, x, x + width); + x += width; + data + }) } diff --git a/tek/src/arranger.rs b/tek/src/arranger.rs index 597ca84c..cf680f4a 100644 --- a/tek/src/arranger.rs +++ b/tek/src/arranger.rs @@ -5,11 +5,30 @@ mod arranger_track; pub use self::arranger_track::*; mod arranger_h; use ClockCommand::{Play, Pause}; use self::ArrangerCommand as Cmd; +/// Root view for standalone `tek_arranger` +pub struct Arranger { + pub jack: Arc>, + pub midi_ins: Vec>, + pub midi_outs: Vec>, + pub clock: Clock, + pub pool: PoolModel, + pub tracks: Vec, + pub scenes: Vec, + pub splits: [u16;2], + pub selected: ArrangerSelection, + pub color: ItemPalette, + pub size: Measure, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub editor: MidiEditor, + pub editing: AtomicBool, + pub perf: PerfModel, + pub compact: bool, +} render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN))); impl EdnViewData for &Arranger { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { use EdnItem::*; - let scenes_w = self.sidebar_w(); let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16; match item { Nil => Box::new(()), @@ -77,19 +96,11 @@ impl Arranger { pub fn tracks_with_sizes (&self) -> impl Iterator { - let active = match self.selected { + tracks_with_sizes(self.tracks.iter(), match self.selected { ArrangerSelection::Track(t) if self.is_editing() => Some(t), ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t), _ => None - }; - let big = self.editor_w(); - let mut x = 0; - self.tracks.iter().enumerate().map(move |(index, track)|{ - let width = if Some(index) == active { big } else { track.width.max(8) }; - let data = (index, track, x, x + width); - x += width; - data - }) + }, self.editor_w()) } fn play_row (&self, tracks_w: u16) -> impl Content + '_ { diff --git a/tek/src/groovebox.rs b/tek/src/groovebox.rs index 2dbcaff9..e7120193 100644 --- a/tek/src/groovebox.rs +++ b/tek/src/groovebox.rs @@ -7,6 +7,20 @@ use MidiEditCommand::*; use MidiPoolCommand::*; use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right}; use std::marker::ConstParamTy; +pub struct Groovebox { + pub _jack: Arc>, + pub player: MidiPlayer, + pub pool: PoolModel, + pub editor: MidiEditor, + pub sampler: Sampler, + + pub compact: bool, + pub size: Measure, + pub status: bool, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub perf: PerfModel, +} render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN))); impl EdnViewData for &Groovebox { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { diff --git a/tek/src/sequencer.rs b/tek/src/sequencer.rs index c056dddb..62037f6b 100644 --- a/tek/src/sequencer.rs +++ b/tek/src/sequencer.rs @@ -5,6 +5,24 @@ use SequencerCommand as Cmd; use MidiEditCommand::*; use MidiPoolCommand::*; render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN))); +/// Root view for standalone `tek_sequencer`. +pub struct Sequencer { + pub _jack: Arc>, + + pub pool: PoolModel, + pub editor: MidiEditor, + pub player: MidiPlayer, + + pub transport: bool, + pub selectors: bool, + pub compact: bool, + + pub size: Measure, + pub status: bool, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub perf: PerfModel, +} impl EdnViewData for &Sequencer { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { use EdnItem::*;