diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 9866c45a..3a09d730 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -97,7 +97,7 @@ render!(|self: ArrangerTui|{ let pool_size = if self.phrases.visible { self.splits[1] } else { 0 }; let with_pool = |x|Split::left(false, pool_size, PoolView(&self.phrases), x); let status = ArrangerStatus::from(self); - let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x); + let with_editbar = |x|Tui::split_n(false, 1, MidiEditStatus(&self.editor), x); let with_status = |x|Tui::split_n(false, 2, status, x); let with_size = |x|lay!([&self.size, x]); let arranger = ||lay!(|add|{ diff --git a/crates/tek/src/tui/app_sampler.rs b/crates/tek/src/tui/app_sampler.rs index ed10f73d..881ce6e7 100644 --- a/crates/tek/src/tui/app_sampler.rs +++ b/crates/tek/src/tui/app_sampler.rs @@ -24,27 +24,10 @@ pub struct SamplerTui { pub size: Measure, /// Lowest note displayed pub note_lo: AtomicUsize, - color: ItemColor + pub note_pt: AtomicUsize, + color: ItemPalette } -render!(|self: SamplerTui|{ - let keys_width = 5; - let keys = move||SamplerKeys(self); - let fg = TuiTheme::g(200); - let bg = TuiTheme::g(50); - let border = Fill::wh(Outer(Style::default().fg(fg).bg(bg))); - let with_border = |x|lay!([ - border, - Tui::inset_xy(1, 1, Fill::wh(&x)) - ]); - Tui::bg(bg, Fill::wh(with_border(Bsp::s( - "Sampler", - Bsp::e( - Fixed::w(keys_width, keys()), - Fill::wh(lay!([&self.size, Fill::wh("Sample")])), - ), - )))) -}); from_jack!(|jack|SamplerTui{ let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?; let audio_outs = vec![ @@ -57,7 +40,8 @@ from_jack!(|jack|SamplerTui{ mode: None, size: Measure::new(), note_lo: 36.into(), - color: ItemColor::default(), + note_pt: 36.into(), + color: ItemPalette::from(Color::Rgb(64, 128, 32)), state: Sampler { jack: jack.clone(), name: "Sampler".into(), @@ -72,8 +56,52 @@ from_jack!(|jack|SamplerTui{ } }); +render!(|self: SamplerTui|{ + let keys_width = 5; + let keys = move||SamplerKeys(self); + let fg = self.color.base.rgb; + let bg = self.color.darkest.rgb; + let border = Fill::wh(Outer(Style::default().fg(fg).bg(bg))); + let inset = 0; + let with_border = |x|lay!([border, Tui::inset_xy(inset, inset, Fill::wh(&x))]); + let with_size = |x|lay!([self.size, x]); + Tui::bg(bg, Fill::wh(with_border(Bsp::s( + Tui::push_x(1, Tui::fg(self.color.light.rgb, Tui::bold(true, "Sampler"))), + with_size(Tui::shrink_y(1, Bsp::e( + Fixed::w(keys_width, keys()), + Fill::wh(render(|to: &mut TuiOutput|Ok({ + let x = to.area.x() + 1; + let rows = self.size.h() as u16; + let bg_base = self.color.darkest.rgb; + let bg_selected = self.color.darker.rgb; + let style_empty = Style::default().fg(self.color.base.rgb); + let style_full = Style::default().fg(self.color.lighter.rgb); + let note_hi = self.note_hi(); + let note_pt = self.note_point(); + for y in 0..rows { + let note = note_hi - y as usize; + let bg = if note == note_pt { bg_selected } else { bg_base }; + let style = Some(style_empty.bg(bg)); + to.blit(&" (no sample) ", x, to.area.y() + y, style) + } + }))) + ))), + )))) +}); + +impl NoteRange for SamplerTui { + fn note_lo (&self) -> &AtomicUsize { &self.note_lo } + fn note_axis (&self) -> &AtomicUsize { &self.size.y } +} +impl NotePoint for SamplerTui { + fn note_len (&self) -> usize {0/*TODO*/} + fn set_note_len (&self, x: usize) {} + fn note_point (&self) -> usize { self.note_pt.load(Relaxed) } + fn set_note_point (&self, x: usize) { self.note_pt.store(x, Relaxed); } +} + struct SamplerKeys<'a>(&'a SamplerTui); -has_color!(|self: SamplerKeys<'a>|self.0.color); +has_color!(|self: SamplerKeys<'a>|self.0.color.base); render!(|self: SamplerKeys<'a>|render(|to|Ok(render_keys_v(to, self)))); impl<'a> NoteRange for SamplerKeys<'a> { fn note_lo (&self) -> &AtomicUsize { &self.0.note_lo } @@ -82,8 +110,8 @@ impl<'a> NoteRange for SamplerKeys<'a> { impl<'a> NotePoint for SamplerKeys<'a> { fn note_len (&self) -> usize {0/*TODO*/} fn set_note_len (&self, x: usize) {} - fn note_point (&self) -> usize {0/*TODO*/} - fn set_note_point (&self, x: usize) {} + fn note_point (&self) -> usize { self.0.note_point() } + fn set_note_point (&self, x: usize) { self.0.set_note_point(x); } } pub enum SamplerMode { @@ -101,15 +129,19 @@ pub enum SamplerCommand { NoteOn(u7, u7), NoteOff(u7) } -input_to_command!(SamplerCommand:|state:SamplerTui,input|match state.mode { +input_to_command!(SamplerCommand: |state: SamplerTui, input|match state.mode { Some(SamplerMode::Import(..)) => Self::Import( FileBrowserCommand::input_to_command(state, input)? ), _ => match input.event() { // load sample key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin), - key_pat!(KeyCode::Up) => { todo!() }, - key_pat!(KeyCode::Down) => { todo!() }, + key_pat!(KeyCode::Up) => { + Self::SelectNote(state.note_point().overflowing_add(1).0.min(127)) + }, + key_pat!(KeyCode::Down) => { + Self::SelectNote(state.note_point().overflowing_sub(1).0.min(127)) + }, _ => return None } //key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { @@ -135,12 +167,17 @@ input_to_command!(FileBrowserCommand:|state:SamplerTui,input|match input { _ => return None }); command!(|self:SamplerCommand,state:SamplerTui|match self { - SamplerCommand::Import(FileBrowserCommand::Begin) => { + Self::Import(FileBrowserCommand::Begin) => { let voices = &state.state.voices; let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?)); None }, + Self::SelectNote(index) => { + let old = state.note_point(); + state.set_note_point(index); + Some(Self::SelectNote(old)) + }, _ => todo!() }); command!(|self:FileBrowserCommand,state:SamplerTui|match self { diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 5bc6a43e..9b86fd2d 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -44,7 +44,7 @@ render!(|self: SequencerTui|{ let with_pool = move|x|Tui::split_w(false, pool_w, pool, x); let status = SequencerStatus::from(self); let with_status = |x|Tui::split_n(false, if self.status { 2 } else { 0 }, status, x); - let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x); + let with_editbar = |x|Tui::split_n(false, 1, MidiEditStatus(&self.editor), x); let with_size = |x|lay!([self.size, x]); let editor = with_editbar(with_pool(Fill::wh(&self.editor))); let color = self.player.play_phrase().as_ref().map(|(_,p)| diff --git a/crates/tek/src/tui/piano_h.rs b/crates/tek/src/tui/piano_h.rs index 3ed7840c..c7be69ac 100644 --- a/crates/tek/src/tui/piano_h.rs +++ b/crates/tek/src/tui/piano_h.rs @@ -52,20 +52,55 @@ impl PianoHorizontal { } render!(|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) + } else { + (ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false) + }; + + let field = move|x, y|row!([ + Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)), + Tui::fg_bg(color.light.rgb, color.darker.rgb, Tui::bold(true, "│")), + Tui::fg_bg(color.lighter.rgb, color.dark.rgb, &y), + ]); + let keys_width = 5; let keys = move||PianoHorizontalKeys(self); + let timeline = move||PianoHorizontalTimeline(self); + let notes = move||PianoHorizontalNotes(self); + let cursor = move||PianoHorizontalCursor(self); + let border = Fill::wh(Outer(Style::default().fg(self.color.dark.rgb).bg(self.color.darkest.rgb))); - let with_border = |x|lay!([border, Tui::inset_xy(1, 1, &x)]); - with_border(Fill::wh(Bsp::s( - Fixed::h(1, Bsp::e(Fixed::w(keys_width, ""), Fill::w(timeline()),)), - Bsp::e( - Fixed::w(keys_width, keys()), - Fill::wh(lay!([&self.size, Fill::wh(lay!([Fill::wh(notes()), Fill::wh(cursor()),]))])), - ), - ))) + let with_border = |x|lay!([border, Tui::inset_xy(0, 0, &x)]); + + with_border(lay!([ + Tui::push_x(0, row!(![ + " ", + field(" Edit", name.to_string()), + field(" Length", length.to_string()), + field(" Loop", looped.to_string()) + ])), + Tui::inset_xy(0, 1, Fill::wh(Bsp::s( + Fixed::h(1, Bsp::e( + Fixed::w(keys_width, ""), + Fill::w(timeline()), + )), + Bsp::e( + Fixed::w(keys_width, keys()), + Fill::wh(lay!([ + &self.size, + Fill::wh(lay!([ + Fill::wh(notes()), + Fill::wh(cursor()), + ])) + ])), + ), + ))) + ])) }); impl PianoHorizontal { diff --git a/crates/tek/src/tui/status/status_edit.rs b/crates/tek/src/tui/status/status_edit.rs index ebddb18d..2bbde7be 100644 --- a/crates/tek/src/tui/status/status_edit.rs +++ b/crates/tek/src/tui/status/status_edit.rs @@ -2,29 +2,30 @@ use crate::*; pub struct MidiEditStatus<'a>(pub &'a MidiEditorModel); render!(|self:MidiEditStatus<'a>|{ + let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) { (phrase.color, phrase.name.clone(), phrase.length, phrase.looped) } else { (ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false) }; - let bg = color.darkest.rgb; - let fg = color.lightest.rgb; + let field = move|x, y|row!([ Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)), Tui::fg_bg(color.light.rgb, color.darker.rgb, Tui::bold(true, "│")), - Fill::w(Tui::fg_bg(color.lightest.rgb, color.dark.rgb, &y)), + Tui::fg_bg(color.lightest.rgb, color.dark.rgb, &y), ]); - Fill::w(Tui::fg_bg(fg, bg, row!([ - Fixed::wh(26, 3, col!(![ - field(" Edit", name.to_string()), - field(" Length", length.to_string()), - field(" Loop", looped.to_string())])), - Fixed::wh(30, 3, col!(![ - field(" Time", format!("{}/{}-{} ({}*{}) {}", - self.0.time_point(), self.0.time_start().get(), self.0.time_end(), - self.0.time_axis().get(), self.0.time_zoom().get(), - if self.0.time_lock().get() { "[lock]" } else { " " })), - field(" Note", format!("{} ({}) {} | {}-{} ({})", - self.0.note_point(), to_note_name(self.0.note_point()), self.0.note_len(), - to_note_name(self.0.note_lo().get()), to_note_name(self.0.note_hi()), - self.0.note_axis().get()))]))])))}); + + let bg = color.darkest.rgb; + let fg = color.lightest.rgb; + Tui::bg(bg, Fill::w(Tui::fg(fg, row!([ + field(" Time", format!("{}/{}-{} ({}*{}) {}", + self.0.time_point(), self.0.time_start().get(), self.0.time_end(), + self.0.time_axis().get(), self.0.time_zoom().get(), + if self.0.time_lock().get() { "[lock]" } else { " " })), + " ", + field(" Note", format!("{} ({}) {} | {}-{} ({})", + self.0.note_point(), to_note_name(self.0.note_point()), self.0.note_len(), + to_note_name(self.0.note_lo().get()), to_note_name(self.0.note_hi()), + self.0.note_axis().get())) + ])))) +});