diff --git a/jack/src/jack_client.rs b/jack/src/jack_client.rs index 97c3955f..b45ae8db 100644 --- a/jack/src/jack_client.rs +++ b/jack/src/jack_client.rs @@ -18,6 +18,9 @@ pub trait HasJack { fn port_by_name (&self, name: &str) -> Option> { self.with_client(|client|client.port_by_name(name)) } + fn port_by_id (&self, id: u32) -> Option> { + self.with_client(|c|c.port_by_id(id)) + } fn register_port (&self, name: impl AsRef) -> Usually> { self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?)) } diff --git a/jack/src/jack_port.rs b/jack/src/jack_port.rs index 5b10f92c..d699a7d6 100644 --- a/jack/src/jack_port.rs +++ b/jack/src/jack_port.rs @@ -90,6 +90,9 @@ pub trait JackPortAutoconnect: JackPort + for<'a>JackPortConnect<&'a Port, re_type: Option<&str>, flags: PortFlags) -> Vec { self.with_client(|c|c.ports(re_name, re_type, flags)) } + fn port_by_id (&self, id: u32) -> Option> { + self.with_client(|c|c.port_by_id(id)) + } fn port_by_name (&self, name: impl AsRef) -> Option> { self.with_client(|c|c.port_by_name(name.as_ref())) } diff --git a/tek/src/audio.rs b/tek/src/audio.rs index 0629d601..6bde3964 100644 --- a/tek/src/audio.rs +++ b/tek/src/audio.rs @@ -73,26 +73,22 @@ audio!( |self, event|{ use JackEvent::*; match event { - SampleRate(sr) => - { self.clock.timebase.sr.set(sr as f64); }, - PortRegistration(id, true) => - { /*println!("\rport add: {id}")*/ }, - PortRegistration(id, false) => - { /*println!("\rport del: {id}")*/ }, - PortsConnected(a, b, true) => - { /*println!("\rport conn: {a} {b}")*/ }, - PortsConnected(a, b, false) => - { /*println!("\rport disc: {a} {b}")*/ }, - ClientRegistration(id, true) => - {}, - ClientRegistration(id, false) => - {}, - ThreadInit => - {}, - XRun => - {}, - GraphReorder => - {}, + SampleRate(sr) => { self.clock.timebase.sr.set(sr as f64); }, + PortRegistration(id, true) => { + //let port = self.jack().port_by_id(id); + //println!("\rport add: {id} {port:?}"); + println!("\rport add: {id}"); + }, + PortRegistration(id, false) => { + /*println!("\rport del: {id}")*/ + }, + PortsConnected(a, b, true) => { /*println!("\rport conn: {a} {b}")*/ }, + PortsConnected(a, b, false) => { /*println!("\rport disc: {a} {b}")*/ }, + ClientRegistration(id, true) => {}, + ClientRegistration(id, false) => {}, + ThreadInit => {}, + XRun => {}, + GraphReorder => {}, _ => { panic!("{event:?}"); } } } diff --git a/tek/src/view.rs b/tek/src/view.rs index 51bad67c..615baba5 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -5,23 +5,19 @@ mod input; pub use self::input::*; mod output; pub use self::output::*; macro_rules! def_sizes_iter { ($Type:ident => $($Item:ty),+) => { - trait $Type<'a> = Iterator + Send + Sync + 'a; - } -} + pub(crate) trait $Type<'a> = + Iterator + Send + Sync + 'a;}} def_sizes_iter!(ScenesSizes => Scene); def_sizes_iter!(TracksSizes => Track); def_sizes_iter!(InputsSizes => JackMidiIn); def_sizes_iter!(OutputsSizes => JackMidiOut); def_sizes_iter!(PortsSizes => Arc, [PortConnect]); +pub(crate) trait ScenesColors<'a> = Iterator>; +pub(crate) type SceneColor<'a> = (usize, &'a Scene, usize, usize, Option); #[derive(Debug, Default)] struct ViewMemo { value: T, view: Arc> } impl ViewMemo { - fn new (value: T, view: U) -> Self { - Self { - value, - view: Arc::new(view.into()) - } - } + fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } } fn update (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option { if newval != self.value { let result = render(&mut*self.view.write().unwrap(), &newval, &self.value); @@ -254,17 +250,37 @@ impl Tek { } /// COMPONENTS //////////////////////////////////////////////////////////////////////////////// fn row <'a> ( - &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a + &'a self, + w: u16, + h: u16, + a: impl Content + 'a, + b: impl Content + 'a, + c: impl Content + 'a, ) -> impl Content + 'a { Fixed::y(h, Bsp::a( - Fill::xy(Align::w(Fixed::x(self.w_sidebar() as u16, a))), - Fill::xy(Align::c(Fixed::x(w, Align::x(b))))))} + Fill::xy(Align::c(Fixed::x(w, Align::x(b)))), + Bsp::a( + Fill::xy(Align::w(Fixed::x(self.w_sidebar() as u16, a))), + Fill::xy(Align::e(Fixed::x(self.w_sidebar() as u16, c))), + ), + )) + } fn row_top <'a> ( - &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a + &'a self, + w: u16, + h: u16, + a: impl Content + 'a, + b: impl Content + 'a, + c: impl Content + 'a, ) -> impl Content + 'a { Fixed::y(h, Bsp::a( - Fill::xy(Align::nw(Fixed::xy(self.w_sidebar() as u16, h, a))), - Fill::xy(Align::n(Fixed::xy(w, h, Align::x(b))))))} + Fill::x(Align::n(Fixed::x(w, Align::x(Tui::bg(Green, b))))), + Bsp::a( + Fill::x(Align::nw(Fixed::x(self.w_sidebar() as u16, Tui::bg(Red, a)))), + Fill::x(Align::ne(Fixed::x(self.w_sidebar() as u16, Tui::bg(Yellow, c)))), + ), + )) + } fn per_track <'a, T: Content + 'a> ( &'a self, f: impl Fn(usize, &'a Track)->T + Send + Sync + 'a ) -> impl Content + 'a { diff --git a/tek/src/view/clip.rs b/tek/src/view/clip.rs index df2b89df..27210909 100644 --- a/tek/src/view/clip.rs +++ b/tek/src/view/clip.rs @@ -4,77 +4,90 @@ impl Tek { let w = (self.size.w() as u16).saturating_sub(2 * self.w_sidebar()); let data = (self.selected.track().unwrap_or(0), self.tracks().len()); self.fmtd.write().unwrap().trks.update(Some(data), rewrite!(buf, "{}/{}", data.0, data.1)); - self.row(w, 1, Align::w(Bsp::e( - self.button3("t", "track", self.fmtd.read().unwrap().trks.view.clone()), - self.button2("T", "add track"), - )), self.per_track(|t, track|{ - let active = self.selected().track() == Some(t+1); - let name = &track.name; - let fg = track.color.lightest.rgb; - let bg = if active { track.color.light.rgb } else { track.color.base.rgb }; - let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; - let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; - Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name)))) - })) - } + self.row(w, 1, self.button3("t", "track", self.fmtd.read().unwrap().trks.view.clone()), + self.per_track(|t, track|self.view_track_header(t, track)), + self.button2("T", "add track")) } + fn view_track_header <'a> ( + &self, t: usize, track: &'a Track + ) -> impl Content + use<'a> { + let active = self.selected().track() == Some(t+1); + let name = &track.name; + let fg = track.color.lightest.rgb; + let bg = if active { track.color.light.rgb } else { track.color.base.rgb }; + let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; + let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; + Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name)))) } pub fn view_scenes (&self) -> impl Content + use<'_> { - let w = self.w_tracks_area(); + let w_full = self.w(); let h = self.h_tracks_area(); let editing = self.is_editing(); let selected_track = self.selected().track(); let selected_scene = self.selected().scene(); - self.row(w, h, - Align::y(Map::new( - move||self.scenes_sizes(editing, 2, 15).map_while( - move|(s, scene, y1, y2)|if y2 as u16 > h { - None - } else { Some((s, scene, y1, y2, if s == 0 { - None - } else { - Some(self.scenes[s-1].color) - })) }), - move|(_, scene, y1, y2, prev), s| { - let height = (1 + y2 - y1) as u16; - let bg = scene.color; - let fg = scene.color.lightest.rgb; - let name = Some(scene.name.clone()); - let cell = self.clip_cell(true, s, &bg, prev, name, " ⯈ ", fg); - map_south(y1 as u16, height, Fixed::y(height, cell)) })).boxed(), - self.per_track(move|t, track|{ - let same_track = selected_track == Some(t+1); - Map::new( - move||self.scenes_sizes(editing, 2, 15).map_while( - move|(s, scene, y1, y2)|if y2 as u16 > h { - None - } else { Some((s, scene, y1, y2, if s == 0 { - None - } else { - Some(self.scenes[s-1].clips[t].as_ref() - .map(|c|c.read().unwrap().color) - .unwrap_or(ItemPalette::G[32])) - })) }), - move|(_, scene, y1, y2, prev), s| { - let height = (1 + y2 - y1) as u16; - let (name, fg, bg) = if let Some(clip) = &scene.clips[t] { - let clip = clip.read().unwrap(); - (Some(clip.name.clone()), clip.color.lightest.rgb, clip.color) - } else { - (None, Tui::g(96), ItemPalette::G[32]) - }; - let active = editing && same_track && selected_scene == Some(s+1); - let edit = |x|Bsp::b(x, When(active, &self.editor)); - let cell = self.clip_cell(same_track, s, &bg, prev, name, " ⏹ ", fg); - map_south(y1 as u16, height, edit(Fixed::y(height, cell)))}) })) } - const TAB: &str = " Tab"; + Tui::bg(Black, self.row_top( + self.w_tracks_area(), + h, + Map::new( + move||self.scenes_with_colors(editing, h), + move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_name( + w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)), + self.per_track(move|t, track|Map::new( + move||self.scenes_with_track_colors(editing, h, t), + move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_track( + (1 + y2 - y1) as u16, y1 as u16, + scene, prev, s, t, editing, selected_track == Some(t+1), selected_scene))), ())) } + fn scenes_with_colors (&self, editing: bool, h: u16) -> impl ScenesColors<'_> { + self.scenes_sizes(editing, 2, 15).map_while( + move|(s, scene, y1, y2)|if y2 as u16 > h { + None + } else { Some((s, scene, y1, y2, if s == 0 { + None + } else { + Some(self.scenes[s-1].color) + })) }) } + fn view_scene_name ( + &self, width: u16, height: u16, offset: u16, + s: usize, scene: &Scene, prev: Option, + ) -> impl Content + use<'_> { + let bg = scene.color; + let fg = scene.color.lightest.rgb; + let name = Some(scene.name.clone()); + let cell = self.clip_cell(true, s, &bg, prev, name, " ⯈ ", fg); + Fixed::x(width, map_south(offset, height, Fixed::y(height, cell))) } + fn scenes_with_track_colors (&self, editing: bool, h: u16, t: usize) -> impl ScenesColors<'_> { + self.scenes_sizes(editing, 2, 15).map_while( + move|(s, scene, y1, y2)|if y2 as u16 > h { + None + } else { Some((s, scene, y1, y2, if s == 0 { + None + } else { + Some(self.scenes[s-1].clips[t].as_ref() + .map(|c|c.read().unwrap().color) + .unwrap_or(ItemPalette::G[32])) + })) }) } + fn view_scene_track ( + &self, height: u16, offset: u16, + scene: &Scene, prev: Option, s: usize, t: usize, + editing: bool, same_track: bool, selected_scene: Option + ) -> impl Content + use<'_> { + let (name, fg, bg) = if let Some(clip) = &scene.clips[t] { + let clip = clip.read().unwrap(); + (Some(clip.name.clone()), clip.color.lightest.rgb, clip.color) + } else { + (None, Tui::g(96), ItemPalette::G[32]) + }; + let active = editing && same_track && selected_scene == Some(s+1); + let edit = |x|Bsp::b(x, When(active, &self.editor)); + let cell = self.clip_cell(same_track, s, &bg, prev, name, " ⏹ ", fg); + map_south(offset, height, edit(Fixed::y(height, cell))) } fn clip_cell <'a> ( &self, same_track: bool, - scene: usize, - color: &ItemPalette, - prev: Option, - name: Option>, - icon: &'a str, - fg: Color, + scene: usize, + color: &ItemPalette, + prev: Option, + name: Option>, + icon: &'a str, + fg: Color, ) -> impl Content + use<'a> { let selected_scene = self.selected().scene(); let selected = same_track && selected_scene == Some(scene+1); @@ -82,34 +95,23 @@ impl Tek { let is_last = scene == self.scenes.len().saturating_sub(1); let colors = Self::colors(color, prev, selected, neighbor, is_last); let content = Fill::x(Align::w(Tui::fg(fg, Tui::bold(true, Bsp::e(icon, name))))); - Phat { width: 0, height: 0, selected, content, colors, } - } + Phat { width: 0, height: 0, selected, content, colors, } } + fn colors ( + theme: &ItemPalette, prev: Option, + selected: bool, neighbor: bool, is_last: bool, + ) -> [Color;4] { + let fg = theme.lightest.rgb; + let bg = if selected { theme.light } else { theme.base }.rgb; + let hi = Self::color_hi(prev, neighbor); + let lo = Self::color_lo(theme, is_last, selected); + [fg, bg, hi, lo] } + fn color_hi (prev: Option, neighbor: bool) -> Color { + prev.map(|prev|if neighbor { prev.light.rgb } else { prev.base.rgb }).unwrap_or(Reset) } + fn color_lo (theme: &ItemPalette, is_last: bool, selected: bool) -> Color { + if is_last { Reset } else if selected { theme.light.rgb } else { theme.base.rgb } } + const TAB: &str = " Tab"; pub fn view_scene_add (&self) -> impl Content + use<'_> { let data = (self.selected().scene().unwrap_or(0), self.scenes().len()); self.fmtd.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1)); - self.button3("S", "add scene", self.fmtd.read().unwrap().scns.view.clone()) - } - fn colors ( - this: &ItemPalette, - prev: Option, - selected: bool, - neighbor: bool, - is_last: bool, - ) -> [Color;4] { - let fg = this.lightest.rgb; - let bg = if selected { this.light } else { this.base }.rgb; - let hi = if neighbor { - prev.map(|prev|prev.light.rgb) - } else { - prev.map(|prev|prev.base.rgb) - }.unwrap_or(Reset); - let lo = if is_last { - Reset - } else if selected { - this.light.rgb - } else { - this.base.rgb - }; - [fg, bg, hi, lo] - } + self.button3("S", "add scene", self.fmtd.read().unwrap().scns.view.clone()) } } diff --git a/tek/src/view/input.rs b/tek/src/view/input.rs index 217e4313..8e2f8fda 100644 --- a/tek/src/view/input.rs +++ b/tek/src/view/input.rs @@ -9,12 +9,9 @@ impl Tek { track.color.dark.rgb, track.color.darker.rgb, ||self.inputs_sizes() - ))); + )), ()); let ports = self.row_top(w, 1, - Fill::x(Align::w(Bsp::e( - self.button3("i", "midi ins", format!("{}", self.midi_ins.len())), - self.button2("I", "add midi in"), - ))), + self.button3("i", "midi ins", format!("{}", self.midi_ins.len())), self.per_track_top(move|t, track|{ let rec = track.player.recording; let mon = track.player.monitoring; @@ -28,14 +25,16 @@ impl Tek { let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e( Tui::fg_bg(rec, bg, "Rec "), - Tui::fg_bg(mon, bg, "Mon ")))))})); + Tui::fg_bg(mon, bg, "Mon "))))) + }), + self.button2("I", "add midi in")); Bsp::s( Bsp::s(routes, ports), self.row_top(w, 2, Bsp::s(Align::e("Input:"), Align::e("Into:")), self.per_track_top(|_, _|Tui::bg(Reset, Align::c(Bsp::s( OctaveVertical::default(), - " ------ "))))) + " ------ ")))), ()) ) } } diff --git a/tek/src/view/output.rs b/tek/src/view/output.rs index bc3157eb..a5489b7d 100644 --- a/tek/src/view/output.rs +++ b/tek/src/view/output.rs @@ -3,16 +3,12 @@ impl Tek { pub fn view_outputs (&self) -> impl Content + use<'_> { let w = self.w_tracks_area(); let fg = Tui::g(224); - let nexts = self.row_top(w, 1, Align::e("Next:"), - self.per_track_top(|_, _|Tui::bg(Reset, " ------ "))); - let froms = self.row_top(w, 2, Align::e("From:"), - self.per_track_top(|_, _|Tui::bg(Reset, - Align::c(Bsp::s(" ------ ", OctaveVertical::default(),))))); + let nexts = self.per_track_top(|_, _|Tui::bg(Reset, " ------ ")); + let nexts = self.row_top(w, 1, Align::e("Next:"), nexts, ()); + let froms = self.per_track_top(|_, _|Tui::bg(Reset, Align::c(Bsp::s(" ------ ", OctaveVertical::default(),)))); + let froms = self.row_top(w, 2, Align::e("From:"), froms, ()); let ports = self.row_top(w, 1, - Fill::x(Align::w(Bsp::e( - self.button3("o", "midi outs", format!("{}", self.midi_outs.len())), - self.button2("O", "add midi out"), - ))), + self.button3("o", "midi outs", format!("{}", self.midi_outs.len())), self.per_track_top(move|t, track|{ let mute = false; let solo = false; @@ -22,11 +18,12 @@ impl Tek { let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e( Tui::fg_bg(mute, bg, "Play "), - Tui::fg_bg(solo, bg, "Solo ")))))})); + Tui::fg_bg(solo, bg, "Solo ")))))}), + self.button2("O", "add midi out")); let routes = self.row_top(w, self.h_outputs() - 1, self.io_ports(fg, Tui::g(32), ||self.outputs_sizes()), self.per_track_top(move|t, track|self.io_connections( - track.color.dark.rgb, track.color.darker.rgb, ||self.outputs_sizes()))); + track.color.dark.rgb, track.color.darker.rgb, ||self.outputs_sizes())), ()); Align::n(Bsp::s(Bsp::s(nexts, froms), Bsp::s(ports, routes))) }