From 4af6e011b6e05a9c468c9b3b7175ac0a9583e2d8 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 13 Jan 2025 20:35:39 +0100 Subject: [PATCH] move track io to tracks trait --- tek/src/arranger-view.edn | 4 +- tek/src/lib.rs | 208 +++++++++++++++++--------------------- 2 files changed, 94 insertions(+), 118 deletions(-) diff --git a/tek/src/arranger-view.edn b/tek/src/arranger-view.edn index 1d1ac948..2e5088de 100644 --- a/tek/src/arranger-view.edn +++ b/tek/src/arranger-view.edn @@ -1,3 +1,3 @@ -(bsp/s :toolbar - (fill/x (align/c (bsp/w :pool +(bsp/s (fill/x (fixed/y (align/x :toolbar))) + (fill/x (align/c (bsp/w (align/e fixed/x :sidebar-w :pool)) (bsp/n :outputs (bsp/n :inputs (bsp/n :tracks :scenes))))))) diff --git a/tek/src/lib.rs b/tek/src/lib.rs index ce620d5e..7b9c710a 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -65,26 +65,19 @@ impl HasSampler for App { fn sample_index (&self) -> usize { self.editor.as_ref().map(|e|e.note_point()).unwrap_or(0) } } impl HasEditor for App { - fn editor (&self) -> &Option { - &self.editor - } - fn editor_mut (&mut self) -> &Option { - &mut self.editor - } - fn is_editing (&self) -> bool { - self.editing.load(Relaxed) - } + fn editor (&self) -> &Option { &self.editor } + fn editor_mut (&mut self) -> &Option { &mut self.editor } + fn is_editing (&self) -> bool { self.editing.load(Relaxed) } + fn editor_h (&self) -> usize { 15 } 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 editor_h (&self) -> usize { - 15 - } } edn_provide!(u16: |self: App|{ + ":sidebar-w" => self.sidebar_w(), ":sample-h" => if self.compact() { 0 } else { 5 }, ":samples-w" => if self.compact() { 4 } else { 11 }, ":samples-y" => if self.compact() { 1 } else { 0 }, @@ -102,13 +95,13 @@ edn_provide!(Arc>: |self: App| { _ => return None }); edn_provide!(Option>>: |self: App| { _ => return None }); edn_provide!('a: Box + 'a>: |self: App|{ ":editor" => (&self.editor).boxed(), - ":pool" => self.pool.as_ref().map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact(), pool)))).boxed(), + ":pool" => self.pool.as_ref().map(|pool|PoolView(self.compact(), pool)).boxed(), ":sample" => self.view_sample(self.is_editing()).boxed(), ":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(), ":status" => self.editor.as_ref().map(|e|Bsp::e(e.clip_status(), e.edit_status())).boxed(), - ":toolbar" => Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.clock)))).boxed(), - ":tracks" => self.row(self.w(), 3, self.track_header(), self.track_cells()).boxed(), - ":inputs" => self.row(self.w(), 3, self.input_header(), self.input_cells()).boxed(), + ":toolbar" => ClockView::new(true, &self.clock).boxed(), + ":tracks" => self.row(self.w(), 3, self.track_header(), self.track_cells()).boxed(), + ":inputs" => self.row(self.w(), 3, self.input_header(), self.input_cells()).boxed(), ":outputs" => self.row(self.w(), 3, self.output_header(), self.output_cells()).boxed(), ":scenes" => self.row(self.w(), self.size.h().saturating_sub(9) as u16, self.scene_header(), self.scene_cells(self.is_editing())).boxed(), @@ -137,7 +130,8 @@ render!(TuiOut: (self: Meter<'a>) => col!( #[derive(Debug, Default)] struct Meters<'a>(pub &'a[f32]); render!(TuiOut: (self: Meters<'a>) => col!( format!("L/{:>+9.3}", self.0[0]), - format!("R/{:>+9.3}", self.0[1]))); + format!("R/{:>+9.3}", self.0[1]) +)); /// Represents the current user selection in the arranger #[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection { /// The whole mix is selected @@ -163,11 +157,7 @@ impl Selection { use Selection::*; match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None } } - pub fn description ( - &self, - tracks: &[Track], - scenes: &[Scene], - ) -> Arc { + pub fn description (&self, tracks: &[Track], scenes: &[Scene]) -> Arc { format!("Selected: {}", match self { Self::Mix => "Everything".to_string(), Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name)) @@ -225,10 +215,14 @@ impl Track { } } impl HasTracks for App { + fn midi_ins (&self) -> &Vec> { &self.midi_ins } + fn midi_outs (&self) -> &Vec> { &self.midi_outs } fn tracks (&self) -> &Vec { &self.tracks } fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } } pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { + fn midi_ins (&self) -> &Vec>; + fn midi_outs (&self) -> &Vec>; fn tracks (&self) -> &Vec; fn tracks_mut (&mut self) -> &mut Vec; fn tracks_sizes <'a> (&'a self, editing: bool, bigger: usize) @@ -277,6 +271,63 @@ pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync )) })).boxed()).into() } + fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + let fg = TuiTheme::g(224); + let bg = TuiTheme::g(64); + (move||Bsp::s(help_tag("midi ", "I", "ns"), self.midi_ins().get(0).map(|inp|Bsp::s( + Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))), + inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, + Tui::fg_bg(fg, bg, connect.info()))))), + ))).boxed()).into() + } + fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { + let w = (x2 - x1) as u16; + let color: ItemPalette = track.color.dark.into(); + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( + Self::rec_mon(color.base.rgb, false, false), + phat_hi(color.base.rgb, color.dark.rgb) + )))) + })).boxed()).into() + } + fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content { + row!( + Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"), + Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"), + Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"), + Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"), + Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"), + ) + } + fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + let fg = TuiTheme::g(224); + let bg = TuiTheme::g(64); + (move||Bsp::s(help_tag("midi ", "O", "uts"), self.midi_outs().get(0).map(|out|Bsp::s( + Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))), + out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, + Tui::fg_bg(fg, bg, connect.info()))))), + ))).boxed()).into() + } + fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { + let w = (x2 - x1) as u16; + let color: ItemPalette = track.color.dark.into(); + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( + Self::mute_solo(color.base.rgb, false, false), + phat_hi(color.dark.rgb, color.darker.rgb) + )))) + })).boxed()).into() + } + fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content { + row!( + Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"), + Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"), + Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"), + ) + } + fn cell > (color: ItemPalette, field: T) -> impl Content { + Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) + } } trait Device: Send + Sync + std::fmt::Debug {} impl Device for Sampler {} @@ -331,7 +382,7 @@ impl Scene { Enqueue(usize), } edn_command!(SceneCommand: |state: App| { - ("add" [] Self::Add) + ("add" [] Self::Add) ("del" [a: usize] Self::Del(0)) ("zoom" [a: usize] Self::SetZoom(a.unwrap())) ("color" [a: usize] Self::SetColor(a.unwrap(), ItemPalette::random())) @@ -470,60 +521,6 @@ impl App { let w = if self.is_editing() { 8 } else { w }; w } - fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let fg = TuiTheme::g(224); - let bg = TuiTheme::g(64); - (move||Bsp::s(help_tag("midi ", "I", "ns"), self.midi_ins.get(0).map(|inp|Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))), - inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, - Tui::fg_bg(fg, bg, connect.info()))))), - ))).boxed()).into() - } - fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n( - Self::rec_mon(color.base.rgb, false, false), - phat_hi(color.base.rgb, color.dark.rgb) - )))) - })).boxed()).into() - } - fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content { - row!( - Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"), - Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"), - Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"), - Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"), - Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"), - ) - } - fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let fg = TuiTheme::g(224); - let bg = TuiTheme::g(64); - (move||Bsp::s(help_tag("midi ", "O", "uts"), self.midi_outs.get(0).map(|out|Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))), - out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, - Tui::fg_bg(fg, bg, connect.info()))))), - ))).boxed()).into() - } - fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n( - Self::mute_solo(color.base.rgb, false, false), - phat_hi(color.dark.rgb, color.darker.rgb) - )))) - })).boxed()).into() - } - fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content { - row!( - Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"), - Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"), - Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"), - ) - } } #[derive(Clone, Debug)] pub enum AppCommand { Clear, @@ -820,30 +817,27 @@ fn help_tag <'a> (before: &'a str, key: &'a str, after: &'a str) -> impl Content let hi = TuiTheme::orange(); Tui::bold(true, row!(Tui::fg(lo, before), Tui::fg(hi, key), Tui::fg(lo, after))) } -fn cell_clip <'a> ( - scene: &'a Scene, index: usize, track: &'a Track, w: u16, h: u16 -) -> impl Content + use<'a> { - scene.clips.get(index).map(|clip|clip.as_ref().map(|clip|{ - let clip = clip.read().unwrap(); - let mut bg = TuiTheme::border_bg(); - let name = clip.name.to_string(); - let max_w = name.len().min((w as usize).saturating_sub(2)); - let color = clip.color; - bg = color.dark.rgb; - if let Some((_, Some(ref playing))) = track.player.play_clip() { - if *playing.read().unwrap() == *clip { - bg = color.light.rgb - } - }; - Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w])))); - })) -} -fn cell > (color: ItemPalette, field: T) -> impl Content { - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) -} impl Arrangement for T where T: HasEditor + HasTracks + HasScenes + HasSelection + HasClock + HasJack {} pub trait Arrangement: HasEditor + HasTracks + HasScenes + HasSelection + HasClock + HasJack { + fn clip (&self) -> Option>> { + self.scene()?.clips.get(self.selected().track()?)?.clone() + } + fn toggle_loop (&mut self) { + if let Some(clip) = self.clip() { + clip.write().unwrap().toggle_loop() + } + } + //fn randomize_color (&mut self) { + //match self.selected { + //Selection::Mix => { self.color = ItemPalette::random() }, + //Selection::Track(t) => { self.tracks[t].color = ItemPalette::random() }, + //Selection::Scene(s) => { self.scenes[s].color = ItemPalette::random() }, + //Selection::Clip(t, s) => if let Some(clip) = &self.scenes[s].clips[t] { + //clip.write().unwrap().color = ItemPalette::random(); + //} + //} + //} fn track_add ( &mut self, name: Option<&str>, @@ -986,24 +980,6 @@ pub trait Arrangement: HasEditor + HasTracks + HasScenes + HasSelection + HasClo } Ok(()) } - fn clip (&self) -> Option>> { - self.scene()?.clips.get(self.selected().track()?)?.clone() - } - fn toggle_loop (&mut self) { - if let Some(clip) = self.clip() { - clip.write().unwrap().toggle_loop() - } - } - //fn randomize_color (&mut self) { - //match self.selected { - //Selection::Mix => { self.color = ItemPalette::random() }, - //Selection::Track(t) => { self.tracks[t].color = ItemPalette::random() }, - //Selection::Scene(s) => { self.scenes[s].color = ItemPalette::random() }, - //Selection::Clip(t, s) => if let Some(clip) = &self.scenes[s].clips[t] { - //clip.write().unwrap().color = ItemPalette::random(); - //} - //} - //} } #[cfg(test)] fn test_tek () { // TODO