diff --git a/output/src/op_transform.rs b/output/src/op_transform.rs index 17a1831d..5900d8cd 100644 --- a/output/src/op_transform.rs +++ b/output/src/op_transform.rs @@ -86,7 +86,7 @@ macro_rules! transform_xy_unit { let u = iter.next().expect("no unit specified"); let c = iter.next().expect("no content specified"); let u = state.get(&u.value).expect("no unit provided"); - let c = state.get_content(&c.value).expect("not content provided"); + let c = state.get_content(&c.value).expect("no content provided"); return Some(match k { $x => Self::x(u, c), $y => Self::y(u, c), @@ -99,7 +99,7 @@ macro_rules! transform_xy_unit { let c = iter.next().expect("no content specified"); let u = state.get(&u.value).expect("no unit provided"); let v = state.get(&v.value).expect("no unit provided"); - let c = state.get_content(&c.value).expect("not content provided"); + let c = state.get_content(&c.value).expect("no content provided"); return Some(Self::xy(u, v, c)) } } diff --git a/tek/src/cli.rs b/tek/src/cli.rs index f3321109..9212cb01 100644 --- a/tek/src/cli.rs +++ b/tek/src/cli.rs @@ -44,9 +44,9 @@ pub struct TekCli { /// Multi-track MIDI sequencer. Arranger { /// Number of scenes - #[arg(short = 'y', long, default_value_t = 1)] scenes: usize, + #[arg(short = 'y', long, default_value_t = 12)] scenes: usize, /// Number of tracks - #[arg(short = 'x', long, default_value_t = 1)] tracks: usize, + #[arg(short = 'x', long, default_value_t = 16)] tracks: usize, /// Width of tracks #[arg(short = 'w', long, default_value_t = 12)] track_width: usize, }, diff --git a/tek/src/keys.rs b/tek/src/keys.rs index bee0697d..57fc1ef8 100644 --- a/tek/src/keys.rs +++ b/tek/src/keys.rs @@ -55,7 +55,8 @@ atom_command!(TekCommand: |app: Tek| { ("zoom" [z: usize] Some(Self::Zoom(z))) ("edit" [] Some(Self::Edit(None))) ("edit" [c: bool] Some(Self::Edit(c))) - ("color" [c: Color] Some(Self::Color(c.map(ItemPalette::from).unwrap_or_default()))) + ("color" [c: Color] Some(Self::Color(ItemPalette::random()))) + ("color" [c: Color] Some(Self::Color(c.map(ItemPalette::from).expect("no color")))) ("enqueue" [c: Arc>] Some(Self::Enqueue(c))) ("clip" [,..a] ClipCommand::try_from_expr(app, a).map(Self::Clip)) ("clock" [,..a] ClockCommand::try_from_expr(app.clock(), a).map(Self::Clock)) @@ -107,7 +108,7 @@ command!(|self: TekCommand, app: Tek|match self { let (index, mut clip) = pool.add_new_clip(); // autocolor: new clip colors from scene and track color clip.write().unwrap().color = ItemColor::random_near( - app.tracks[t.saturating_sub(1)].color.base.mix( + app.tracks[t].color.base.mix( scene.color.base, 0.5 ), @@ -146,20 +147,16 @@ command!(|self: TekCommand, app: Tek|match self { old }, Track(t) => { - let t = t.saturating_sub(1); let old = app.tracks[t].color; app.tracks[t].color = palette; old } Scene(s) => { - let s = s.saturating_sub(1); let old = app.scenes[s].color; app.scenes[s].color = palette; old } Clip(t, s) => { - let t = t.saturating_sub(1); - let s = s.saturating_sub(1); if let Some(ref clip) = app.scenes[s].clips[t] { let mut clip = clip.write().unwrap(); let old = clip.color; @@ -229,9 +226,9 @@ atom_command!(TrackCommand: |app: Tek| { ("add" [] Some(Self::Add)) ("size" [a: usize] Some(Self::SetSize(a.unwrap()))) ("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) - ("color" [a: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random()))) - ("del" [a: usize] Some(Self::Del(a.unwrap().saturating_sub(1)))) - ("stop" [a: usize] Some(Self::Stop(a.unwrap().saturating_sub(1)))) + ("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemPalette::random()))) + ("del" [a: usize] Some(Self::Del(a.unwrap()))) + ("stop" [a: usize] Some(Self::Stop(a.unwrap()))) ("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap()))) ("play" [] Some(Self::TogglePlay)) ("solo" [] Some(Self::ToggleSolo)) @@ -289,8 +286,8 @@ atom_command!(SceneCommand: |app: Tek| { ("add" [] Some(Self::Add)) ("del" [a: usize] Some(Self::Del(0))) ("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) - ("color" [a: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random()))) - ("enqueue" [a: usize] Some(Self::Enqueue(a.unwrap().saturating_sub(1)))) + ("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemPalette::G[128]))) + ("enqueue" [a: usize] Some(Self::Enqueue(a.unwrap()))) ("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap()))) }); command!(|self: SceneCommand, app: Tek|match self { @@ -327,12 +324,12 @@ command!(|self: SceneCommand, app: Tek|match self { SetColor(usize, usize, ItemPalette), } atom_command!(ClipCommand: |app: Tek| { - ("get" [a: usize ,b: usize] Some(Self::Get(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1)))) - ("put" [a: usize, b: usize, c: Option>>] Some(Self::Put(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap()))) - ("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1)))) + ("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap()))) + ("put" [a: usize, b: usize, c: Option>>] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap()))) + ("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) ("edit" [a: Option>>] Some(Self::Edit(a.unwrap()))) - ("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap()))) - ("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), ItemPalette::random()))) + ("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap()))) + ("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemPalette::random()))) }); command!(|self: ClipCommand, app: Tek|match self { Self::Get(track, scene) => { todo!() }, @@ -345,5 +342,14 @@ command!(|self: ClipCommand, app: Tek|match self { app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); None }, + Self::SetColor(track, scene, color) => { + app.scenes[scene].clips[track].as_ref().map(|clip|{ + let mut clip = clip.write().unwrap(); + let old = clip.color.clone(); + clip.color = color.clone(); + panic!("{color:?} {old:?}"); + Self::SetColor(track, scene, old) + }) + }, _ => None }); diff --git a/tek/src/keys_clip.edn b/tek/src/keys_clip.edn index 9bc887df..f1123cae 100644 --- a/tek/src/keys_clip.edn +++ b/tek/src/keys_clip.edn @@ -1,4 +1,4 @@ -(@q clip launch :track :scene) +(@q clip enqueue :track :scene) (@c clip color :track :scene) (@g clip get) (@p clip put) diff --git a/tek/src/view.rs b/tek/src/view.rs index 399d5dea..33dd0e71 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -20,8 +20,9 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(), ":scene-add" => self.view_scene_add().boxed(), ":scenes" => self.view_scenes().boxed(), - ":status" => self.view_editor().boxed(), ":toolbar" => self.view_clock().boxed(), + ":transport" => self.view_transport().boxed(), + ":status" => self.view_status().boxed(), ":tracks" => self.view_tracks().boxed(), }); provide_num!(u16: |self: Tek| { @@ -50,8 +51,6 @@ impl Tek { pub(crate) fn w (&self) -> u16 { self.size.w() as u16 } pub(crate) fn h (&self) -> u16 { self.size.h() as u16 } pub(crate) fn w_sidebar (&self) -> u16 { self.w() / if self.is_editing() { 16 } else { 8 } as u16 } - pub(crate) fn w_tracks_area (&self) -> u16 { self.w().saturating_sub(2 * self.w_sidebar()) } - pub(crate) fn h_tracks_area (&self) -> u16 { self.h().saturating_sub(self.h_inputs() + self.h_outputs() + 10) } pub(crate) fn row <'a> ( &'a self, w: u16, @@ -77,10 +76,10 @@ impl Tek { c: impl Content + 'a, ) -> impl Content + 'a { Fixed::y(h, Bsp::a( - Fill::x(Align::n(Fixed::x(w, Align::x(Tui::bg(Green, b))))), + Fill::x(Align::n(Fixed::x(w, Align::x(Tui::bg(Reset, 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)))), + Fill::x(Align::nw(Fixed::x(self.w_sidebar() as u16, Tui::bg(Reset, a)))), + Fill::x(Align::ne(Fixed::x(self.w_sidebar() as u16, Tui::bg(Reset, c)))), ), )) } diff --git a/tek/src/view_arranger.edn b/tek/src/view_arranger.edn index 72d06f45..f8431aa4 100644 --- a/tek/src/view_arranger.edn +++ b/tek/src/view_arranger.edn @@ -1,5 +1,4 @@ -(bsp/n - (fixed/y 2 :toolbar) - (fill/xy (bsp/a - (fill/xy (align/e :pool)) - (bsp/s :inputs (bsp/s :tracks (bsp/n :outputs :scenes)))))) +(bsp/n (fixed/y 1 :transport) + (bsp/s (fixed/y 1 :status) + (fill/xy (bsp/a (fill/xy (align/e :pool)) + (bsp/s :inputs (bsp/s :tracks (bsp/n :outputs :scenes))))))) diff --git a/tek/src/view_clock.rs b/tek/src/view_clock.rs index bbb311cd..8bdc28e6 100644 --- a/tek/src/view_clock.rs +++ b/tek/src/view_clock.rs @@ -46,6 +46,32 @@ impl Tek { ))) ) } + pub(crate) fn view_status (&self) -> impl Content + use<'_> { + self.update_clock(); + let theme = ItemPalette::G[96]; + let fmtd = self.fmtd.read().unwrap(); + Tui::bg(Black, row!(Bsp::a( + Fill::xy(Align::w(self.view_play_pause())), + Fill::xy(Align::e(row!( + FieldH(theme, "BPM", fmtd.bpm.view.clone()), + FieldH(theme, "Beat", fmtd.beat.view.clone()), + FieldH(theme, "Time", fmtd.time.view.clone()) + ))) + ))) + } + pub(crate) fn view_transport (&self) -> impl Content + use<'_> { + self.update_clock(); + let theme = ItemPalette::G[96]; + let fmtd = self.fmtd.read().unwrap(); + Tui::bg(Black, row!(Bsp::a( + Fill::xy(Align::w(FieldH(theme, "Sel", self.selected.describe(&self.tracks, &self.scenes)))), + Fill::xy(Align::e(row!( + FieldH(theme, "SR", fmtd.sr.view.clone()), + FieldH(theme, "Buf", fmtd.buf.view.clone()), + FieldH(theme, "Lat", fmtd.lat.view.clone()), + ))) + ))) + } fn view_play_pause (&self) -> impl Content + use<'_> { let playing = self.clock.is_rolling(); let compact = true;//self.is_editing(); diff --git a/tek/src/view_scene.rs b/tek/src/view_scene.rs index 964298d2..5fc6c159 100644 --- a/tek/src/view_scene.rs +++ b/tek/src/view_scene.rs @@ -1,15 +1,18 @@ use crate::*; impl Tek { + const H_SCENE: usize = 2; + const H_EDITOR: usize = 15; pub(crate) fn h_scenes (&self, editing: bool, height: usize, larger: usize) -> u16 { - self.scenes_sizes(editing, height, larger).last().map(|(_, _, _, y)|y as u16).unwrap_or(0)} + self.scenes_sizes(editing, height, larger).last().map(|(_, _, _, y)|y as u16).unwrap_or(0) + } pub(crate) fn scenes_sizes (&self, editing: bool, height: usize, larger: usize) -> impl ScenesSizes<'_> { - let mut y = 0; let (selected_track, selected_scene) = match self.selected() { Selection::Track(t) => (Some(*t), None), Selection::Scene(s) => (None, Some(*s)), Selection::Clip(t, s) => (Some(*t), Some(*s)), _ => (None, None) }; + let mut y = 0; self.scenes().iter().enumerate().map(move|(s, scene)|{ let active = editing && selected_track.is_some() && selected_scene == Some(s); let height = if active { larger } else { height }; @@ -19,22 +22,24 @@ impl Tek { pub fn view_scenes (&self) -> impl Content + use<'_> { let w_full = self.w(); let w = self.w_tracks_area(); - let h = self.h_tracks_area(); + let h_area = self.h_tracks_area(); let editing = self.is_editing(); let selected_track = self.selected().track(); let selected_scene = self.selected().scene(); + let h = self.h_scenes(editing, Self::H_SCENE, Self::H_EDITOR); let scene_names = Map::new( - move||self.scenes_with_colors(editing, h), + move||self.scenes_with_colors(editing, h_area), move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_name( w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)); let scene_clips = self.per_track(move|t, track|Map::new( - move||self.scenes_with_track_colors(editing, h, t), + move||self.scenes_with_track_colors(editing, h_area, t), move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_clip( w, (1 + y2 - y1) as u16, y1 as u16, scene, prev, s, t, editing, selected_track == Some(t), selected_scene))); - Tui::bg(Black, self.row_top(self.w_tracks_area(), h, scene_names, scene_clips, ())) } + Tui::bg(Reset, Fixed::y(self.h_tracks_area(), + self.row(self.w_tracks_area(), h, scene_names, scene_clips, ()))) } fn scenes_with_colors (&self, editing: bool, h: u16) -> impl ScenesColors<'_> { - self.scenes_sizes(editing, 2, 15).map_while( + self.scenes_sizes(editing, Self::H_SCENE, Self::H_EDITOR).map_while( move|(s, scene, y1, y2)|if y2 as u16 > h { None } else { Some((s, scene, y1, y2, if s == 0 { @@ -50,9 +55,9 @@ impl Tek { let fg = scene.color.lightest.rgb; let name = Some(scene.name.clone()); let cell = self.view_scene_cell(true, s, &bg, prev, name, " ⯈ ", fg); - map_south(offset, height, Fixed::y(height, cell)) } + Fill::x(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( + self.scenes_sizes(editing, Self::H_SCENE, Self::H_EDITOR).map_while( move|(s, scene, y1, y2)|if y2 as u16 > h { None } else { Some((s, scene, y1, y2, if s == 0 { @@ -92,8 +97,12 @@ impl Tek { let neighbor = same_track && scene > 0 && selected_scene == Some(scene - 1); 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, } } + Self::cell(icon, name, colors) } + fn cell <'a> ( + icon: &'a str, name: Option>, colors: [Color;4] + ) -> impl Content + use<'a> { + let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(icon, name)))); + Phat { width: 0, height: 0, content, colors, } } const TAB: &str = " Tab"; pub fn view_scene_add (&self) -> impl Content + use<'_> { let data = (self.selected().scene().unwrap_or(0), self.scenes().len()); diff --git a/tek/src/view_track.rs b/tek/src/view_track.rs index 0ce2009f..cfdb2a70 100644 --- a/tek/src/view_track.rs +++ b/tek/src/view_track.rs @@ -1,5 +1,15 @@ use crate::*; impl Tek { + pub(crate) fn w_tracks_area (&self) -> u16 { + self.w().saturating_sub(2 * self.w_sidebar()) + } + pub(crate) fn w_tracks (&self, editing: bool, bigger: usize) -> u16 { + self.tracks_sizes(editing, bigger).last().map(|(_, _, _, x)|x as u16).unwrap_or(0) + } + pub(crate) fn h_tracks_area (&self) -> u16 { + self.h().saturating_sub(self.h_inputs() + self.h_outputs() + 10) + } + pub fn view_tracks (&self) -> impl Content + use<'_> { let w = (self.size.w() as u16).saturating_sub(2 * self.w_sidebar()); let data = (self.selected.track().unwrap_or(0), self.tracks().len()); @@ -27,15 +37,12 @@ impl Tek { let width = self.w_tracks_area(); let filter = move|(t, track, x1, x2)|if x2 as u16 >= width {None} else {Some((t, track, x1, x2))}; let tracks = move||self.tracks_sizes(self.is_editing(), self.editor_w()).map_while(filter); - Align::x(Tui::bg(Green, Map::new(tracks, move|(index, track, x1, x2), _|{ + Align::x(Tui::bg(Reset, Map::new(tracks, move|(index, track, x1, x2), _|{ let width = (x2 - x1) as u16; map_east(x1 as u16, width, Fixed::x(width, Tui::fg_bg( track.color.lightest.rgb, track.color.base.rgb, f(index, track)))) }))) } - pub(crate) fn w_tracks (&self, editing: bool, bigger: usize) -> u16 { - self.tracks_sizes(editing, bigger).last().map(|(_, _, _, x)|x as u16).unwrap_or(0) - } fn tracks_sizes <'a> (&'a self, editing: bool, bigger: usize) -> impl TracksSizes<'a> { let mut x = 0; let active = match self.selected() { diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 177f9398..b87bd6d5 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -116,7 +116,6 @@ pub struct Phat { pub height: u16, pub content: T, pub colors: [Color;4], - pub selected: bool, } impl Phat { /// A phat line