From 2795c05275592475fee0373f786dbc7c541ab59c Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 12 Dec 2024 22:54:55 +0100 Subject: [PATCH] delegate more responsibilities to PhraseViewMode --- crates/tek/src/tui/app_arranger.rs | 2 +- crates/tek/src/tui/app_sequencer.rs | 10 +- crates/tek/src/tui/phrase_editor.rs | 190 ++++++++++------------------ 3 files changed, 71 insertions(+), 131 deletions(-) diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index f8813c96..778bd549 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -143,7 +143,7 @@ render!(|self: ArrangerTui|{ false, self.splits[1], PhraseListView::from(self), - PhraseView::from(self), + &self.editor, ) ]) ]) diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index b72fde0a..0a12eebd 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -22,14 +22,14 @@ impl TryFrom<&Arc>> for SequencerTui { phrases.phrase.store(1, Ordering::Relaxed); let mut player = PhrasePlayerModel::from(&clock); - player.play_phrase = Some((Moment::zero(&clock.timebase), Some(phrase))); + player.play_phrase = Some((Moment::zero(&clock.timebase), Some(phrase.clone()))); Ok(Self { + jack: jack.clone(), clock, phrases, player, editor: PhraseEditorModel::from(&phrase), - jack: jack.clone(), size: Measure::new(), cursor: (0, 0), entered: false, @@ -127,7 +127,7 @@ render!(|self: SequencerTui|{ false } ))), - PhraseView::from(self) + self.editor ]), ) )]); @@ -336,7 +336,9 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option if let Some((_, Some(playing_phrase))) = state.player.play_phrase() { - let editing_phrase = state.editor.phrase.read().unwrap().map(|p|p.read().unwrap().clone()); + let editing_phrase = state.editor.phrase() + .read().unwrap().as_ref() + .map(|p|p.read().unwrap().clone()); let selected_phrase = state.phrases.phrase().clone(); if Some(selected_phrase.read().unwrap().clone()) != editing_phrase { Editor(Show(Some(selected_phrase))) diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index f2546128..7c95c640 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -63,7 +63,8 @@ impl std::fmt::Debug for PhraseEditorModel { impl Default for PhraseEditorModel { fn default () -> Self { - let phrase = Arc::new(RwLock::new(None)); + let phrase = Arc::new(RwLock::new(None)); + let note_len = Arc::from(AtomicUsize::from(24)); Self { size: Measure::new(), phrase: phrase.clone(), @@ -72,24 +73,35 @@ impl Default for PhraseEditorModel { time_point: 0.into(), note_lo: 0.into(), note_point: 0.into(), - note_len: Arc::from(AtomicUsize::from(24)), + note_len: note_len.clone(), notes_in: RwLock::new([false;128]).into(), notes_out: RwLock::new([false;128]).into(), view_mode: Box::new(PianoHorizontal { - phrase, + phrase: Arc::new(RwLock::new(None)), buffer: Default::default(), time_zoom: 24, time_lock: true, - note_zoom: PhraseViewNoteZoom::N(1) + note_zoom: PhraseViewNoteZoom::N(1), + focused: true, + note_len }), } } } impl PhraseEditorModel { + /// Select which pattern to display. This pre-renders it to the buffer at full resolution. + pub fn show_phrase (&mut self, phrase: Option>>) { + *self.view_mode.phrase().write().unwrap() = if phrase.is_some() { + phrase.clone() + } else { + None + }; + self.view_mode.redraw(); + } /// Put note at current position - pub fn put_note (&mut self) { - if let Some(phrase) = *self.phrase.read().unwrap() { + pub fn put_note (&mut self, advance: bool) { + if let Some(phrase) = &*self.phrase.read().unwrap() { let note_len = self.note_len.load(Ordering::Relaxed); let time = self.time_point.load(Ordering::Relaxed); let note = self.note_point.load(Ordering::Relaxed); @@ -100,30 +112,20 @@ impl PhraseEditorModel { let end = (start + note_len) % phrase.length; phrase.notes[time].push(MidiMessage::NoteOn { key, vel }); phrase.notes[end].push(MidiMessage::NoteOff { key, vel }); - self.view_mode.show(Some(&phrase), note_len); - } - } - /// Move time cursor forward by current note length - pub fn time_cursor_advance (&self) { - let point = self.time_point.load(Ordering::Relaxed); - let length = self.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); - let forward = |time|(time + self.note_len) % length; - self.time_point.store(forward(point), Ordering::Relaxed); - } - /// Select which pattern to display. This pre-renders it to the buffer at full resolution. - pub fn show_phrase (&mut self, phrase: Option>>) { - if phrase.is_some() { - self.phrase = phrase; - let phrase = &*self.phrase.as_ref().unwrap().read().unwrap(); - self.view_mode.show(Some(&phrase), self.note_len); - } else { - self.view_mode.show(None, self.note_len); - self.phrase = None; + self.view_mode.redraw(); + if advance { + let point = self.time_point.load(Ordering::Relaxed); + let length = phrase.length; + let forward = |time|(time + note_len) % length; + self.time_point.store(forward(point), Ordering::Relaxed); + } } } } -pub trait PhraseViewMode: Debug + Send + Sync { +render!(|self: PhraseEditorModel|self.view_mode); + +pub trait PhraseViewMode: Render + Debug + Send + Sync { fn time_zoom (&self) -> usize; fn set_time_zoom (&mut self, time_zoom: usize); fn time_zoom_lock (&self) -> bool; @@ -133,6 +135,30 @@ pub trait PhraseViewMode: Debug + Send + Sync { fn phrase (&self) -> &Arc>>>>; } +impl PhraseViewMode for PhraseEditorModel { + fn time_zoom (&self) -> usize { + self.view_mode.time_zoom() + } + fn set_time_zoom (&mut self, time_zoom: usize) { + self.view_mode.set_time_zoom(time_zoom) + } + fn time_zoom_lock (&self) -> bool { + self.view_mode.time_zoom_lock() + } + fn set_time_zoom_lock (&mut self, time_lock: bool) { + self.view_mode.set_time_zoom_lock(time_lock); + } + fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) { + self.view_mode.buffer_size(phrase) + } + fn redraw (&mut self) { + self.view_mode.redraw() + } + fn phrase (&self) -> &Arc>>>> { + self.view_mode.phrase() + } +} + pub struct PhraseView<'a> { note_point: usize, note_range: (usize, usize), @@ -147,23 +173,6 @@ pub struct PhraseView<'a> { entered: bool, } -render!(|self: PhraseView<'a>|{ - let bg = if self.focused { TuiTheme::g(32) } else { Color::Reset }; - let fg = self.phrase.as_ref() - .map(|p|p.read().unwrap().color.clone()) - .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); - Tui::bg(bg, Tui::split_up(false, 2, - Tui::bg(fg.dark.rgb, col!([ - PhraseTimeline(&self, fg), - PhraseViewStats(&self, fg), - ])), - Split::right(false, 5, PhraseKeys(&self, fg), lay!([ - PhraseNotes(&self, fg), - PhraseCursor(&self), - ])), - )) -}); - impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> { fn from (state: &'a T) -> Self { let editor = state.editor(); @@ -192,62 +201,6 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> { } } -pub struct PhraseTimeline<'a>(&'a PhraseView<'a>, ItemPalette); -render!(|self: PhraseTimeline<'a>|Tui::fg(TuiTheme::g(224), Tui::push_x(5, format!("|000.00.00")))); - -pub struct PhraseViewStats<'a>(&'a PhraseView<'a>, ItemPalette); -render!(|self: PhraseViewStats<'a>|{ - let color = self.1.dark.rgb;//if self.0.focused{self.1.light.rgb}else{self.1.dark.rgb}; - row!([ - Tui::bg(color, Tui::fg(TuiTheme::g(224), format!( - " {} | Note: {} ({}) | {} ", - self.0.size.format(), - self.0.note_point, - to_note_name(self.0.note_point), - pulses_to_name(self.0.note_len), - ))), - { - let mut upper_right = format!("[{}]", if self.0.entered {"■"} else {" "}); - if let Some(phrase) = self.0.phrase { - upper_right = format!( - " Time: {}/{} {} {upper_right} ", - self.0.time_point, - phrase.read().unwrap().length, - pulses_to_name(self.0.view_mode.time_zoom().unwrap()), - ) - }; - Tui::bg(color, Tui::fg(TuiTheme::g(224), upper_right)) - } - ]) -}); - -struct PhraseKeys<'a>(&'a PhraseView<'a>, ItemPalette); -render!(|self: PhraseKeys<'a>|{ - let layout = |to:[u16;2]|Ok(Some(to.clip_w(5))); - Tui::fill_xy(Widget::new(layout, |to: &mut TuiOutput|Ok( - self.0.view_mode.render_keys(to, self.1.light.rgb, Some(self.0.note_point), self.0.note_range) - ))) -}); - -struct PhraseNotes<'a>(&'a PhraseView<'a>, ItemPalette); -render!(|self: PhraseNotes<'a>|Tui::fill_xy(render(|to: &mut TuiOutput|{ - self.0.size.set_wh(to.area.w(), to.area.h() as usize); - Ok(self.0.view_mode.render_notes(to, self.0.time_start, self.0.note_range.1)) -}))); - -struct PhraseCursor<'a>(&'a PhraseView<'a>); -render!(|self: PhraseCursor<'a>|Tui::fill_xy(render(|to: &mut TuiOutput|Ok( - self.0.view_mode.render_cursor( - to, - self.0.time_point, - self.0.time_start, - self.0.note_point, - self.0.note_len, - self.0.note_range.1, - self.0.note_range.0, - ) -)))); - #[derive(Clone, Debug)] pub enum PhraseCommand { // TODO: 1-9 seek markers that by default start every 8th of the phrase @@ -273,7 +226,8 @@ impl InputToCommand for PhraseCommand { let time_start = state.time_start.load(Ordering::Relaxed); let time_point = state.time_point.load(Ordering::Relaxed); let time_zoom = state.view_mode.time_zoom(); - let length = state.phrase.read().unwrap().map(|p|p.read().unwrap().length).unwrap_or(1); + let length = state.phrase().read().unwrap().as_ref() + .map(|p|p.read().unwrap().length).unwrap_or(1); let note_len = state.note_len.load(Ordering::Relaxed); Some(match from.event() { key!(Char('`')) => ToggleDirection, @@ -307,9 +261,6 @@ impl InputToCommand for PhraseCommand { key!(Ctrl-PageDown) => SetNoteScroll(note_point.saturating_sub(3)), key!(Ctrl-Left) => SetTimeScroll(time_start.saturating_sub(note_len)), key!(Ctrl-Right) => SetTimeScroll(time_start + note_len), - - - _ => return None }, }) @@ -320,29 +271,16 @@ impl Command for PhraseCommand { fn execute (self, state: &mut PhraseEditorModel) -> Perhaps { use PhraseCommand::*; match self { - Show(phrase) => { - state.show_phrase(phrase); - }, - PutNote => { - state.put_note(); - }, - AppendNote => { - state.put_note(); - state.time_cursor_advance(); - }, - SetTimeZoom(zoom) => { - state.view_mode.set_time_zoom(zoom); - state.show_phrase(state.phrase.clone()); - }, - SetTimeZoomLock(lock) => { - state.view_mode.set_zoom_lock(lock); - state.show_phrase(state.phrase.clone()); - }, - SetTimeScroll(time) => { state.time_start.store(time, Ordering::Relaxed); }, - SetTimeCursor(time) => { state.time_point.store(time, Ordering::Relaxed); }, - SetNoteLength(time) => { state.note_len.store(time, Ordering::Relaxed); }, - SetNoteScroll(note) => { state.note_lo.store(note, Ordering::Relaxed); }, - SetNoteCursor(note) => { + Show(phrase) => { state.show_phrase(phrase); }, + PutNote => { state.put_note(false); }, + AppendNote => { state.put_note(true); }, + SetTimeZoom(zoom) => { state.view_mode.set_time_zoom(zoom); }, + SetTimeZoomLock(lock) => { state.view_mode.set_time_zoom_lock(lock); }, + SetTimeScroll(time) => { state.time_start.store(time, Ordering::Relaxed); }, + SetTimeCursor(time) => { state.time_point.store(time, Ordering::Relaxed); }, + SetNoteLength(time) => { state.note_len.store(time, Ordering::Relaxed); }, + SetNoteScroll(note) => { state.note_lo.store(note, Ordering::Relaxed); }, + SetNoteCursor(note) => { let note = 127.min(note); let start = state.note_lo.load(Ordering::Relaxed); state.note_point.store(note, Ordering::Relaxed);